既生 HTTP 何生 WebSocket

/ python / 没有评论 / 2046浏览

既生 HTTP 何生 WebSocket

最近一两年一直在使用 Python,使用体验从最开始的惊喜有趣,到后面的简简单单,一路走来收获颇多。现如今仍旧保持好奇心,遇到自己觉得新鲜的就去思考它是如何实现的,这种好奇心驱使着我不断求知,嗯,程序员的生活就是这么朴实无华,平淡无奇。

我一直好奇于 Jupyter Notebook 是怎么实现一个远程终端的。

使用 Django、Django rest framework 的感受是:HTTP 协议真的流弊,基本解决了我们遇到的客户端服务器的通信问题,直到使用了 Jupyter NoteBook 的 terminal 功能之后,我开始反思,为什么这个 Jupyter 可以让 linux 的本地 terminal 运行在浏览器上?如果要服务器源源不断的向客户端返回数据,使用 HTTP 实现的话,除了处理不停的刷新请求,应该没有别的好办法。

好奇的我打开了谷歌浏览器的开发者工具,发现 terminal 的 js 源码中有这一段:

......
 function make_terminal(element, ws_url) {
        var ws = new WebSocket(ws_url);
        Terminal.applyAddon(fit);
        var term = new Terminal();
        ws.onopen = function(event) {
            term.on('data', function(data) {
                ws.send(JSON.stringify(['stdin', data]));
            });

            term.on('title', function(title) {
                document.title = title;
            });

            term.open(element);
            term.fit();
            // send the terminal size to the server.
            ws.send(JSON.stringify(["set_size", term.rows, term.cols,
                                        window.innerHeight, window.innerWidth]));

            ws.onmessage = function(event) {
                var json_msg = JSON.parse(event.data);
                switch(json_msg[0]) {
                    case "stdout":
                        term.write(json_msg[1]);
                        break;
                    case "disconnect":
                        term.write("\r\n\r\n[CLOSED]\r\n");
                        break;
                }
            };
        };
        return {socket: ws, term: term};
    }
......

这便是 WebSocket 客户端应用的典型代码。也许你也想知道,既然已经有了 HTTP 协议,为什么还需要 WebSocket?它能带来什么好处?

WebSocket Vs HTTP

我查了下资料,答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起,当客户端与服务器需要频繁通信时,HTTP 协议非常低效,为什么低效? 因为 HTTP 接口之下是 TCP/TSL 套接字(Socket)连接。每一次请求,通常都会重新建立一次 TCP/TSL 握手;在请求结束之后,断开这个链接,也许你听说过面试常问的“TCP 协议的三次握手和四次分手”,这个过程,比我们想象的要慢很多。

而 WebSocket 是一种在单个 TCP/TSL 连接上,进行全双工、双向通信的协议。WebSocket 可以让客户端与服务器之间的数据交换变得更加简单高效,服务端也可以主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就可以直接创建持久性的连接,并进行双向数据传输。对于 web terminal 这种应用,WebSocket 就非常高效。

说到这里,也许你了解到 WebSocket 可以全双工通信,但还不知道要不要用 WebSocket,那么我先问你两个问题:

如果你的答案是肯定的,那么请学习 WebSocket 并尽情的使用吧,如果是否定的,那么可以看下典型的 7 个使用场景,在大脑里留个印象。

  1. 社交订阅,多人聊天
  2. 多玩家游戏
  3. 协同编辑,在线文档
  4. 流式数据,股票 K 线
  5. 在线教育
  6. 基于位置的应用
  7. 实时监控

下面是 WebSocket 的一些工具介绍

1、WebSocket 服务器:Websocketd。

一个命令行的 WebSocket 服务器,它的最大特点,就是后台脚本不限语言,标准输入(stdin)就是 WebSocket 的输入,标准输出(stdout)就是 WebSocket 的输出,因此,只要你可以写程序从标准输入读取数据,并写入标准输出,你就将你的程序作为 WebSocket 服务器,因此,你可以使用任何编程语言,Python, Ruby, Perl, Bash, .NET, C, Go, PHP, Java, Clojure, Scala, Groovy, Expect, Awk, VBScript, Haskell, Lua, R 都可以。举个例子:

你的 shell 程序:

#!/bin/bash
for ((COUNT = 1; COUNT <= 10; COUNT++)); do
  echo $COUNT
  sleep 1
done

执行该程序:

$ chmod +x count.sh
$ ./count.sh
1
2
3
4
5
6
7
8
9
10

然后将 count.sh 作为服务器的处理逻辑。

$ websocketd --port=8080 ./count.sh

编写前端页面 test.html:

<!DOCTYPE html>
<pre id="log"></pre>
<script>
  // helper function: log message to screen
  function log(msg) {
    document.getElementById('log').textContent += msg + '\n';
  }

  // setup websocket with callbacks
  var ws = new WebSocket('ws://localhost:8080/');
  ws.onopen = function() {
    log('CONNECT');
  };
  ws.onclose = function() {
    log('DISCONNECT');
  };
  ws.onmessage = function(event) {
    log('MESSAGE: ' + event.data);
  };
</script>

浏览器打开 test.html 你就会发现程序的标准输出打印在浏览器了。

github 链接:https://github.com/joewalnes/websocketd 14.6K Star。

2、把你的 terminal 搬到浏览器上:gotty

gotty 可以实现 Jupyter Notebook terminal 一样的功能,而且可以自由定制。

用法

Usage: gotty [options] <command> [<arguments...>]

默认情况下,GoTTY 不允许客户端发送任何按键或命令(终端窗口大小调整除外)。如果要允许客户将输入写入TTY,请添加该 -w 选项。

但是,对于大多数命令来说,接受来自远程客户端的输入是危险的。当出于某些原因需要与 TTY 进行交互时,请考虑使用 tmux 或 GNU Screen 启动 GoTTY并在其上运行命令(有关详细信息,请参见“与多个客户端共享”部分)。

要限制客户端访问,可以使用该 -c 选项启用基本身份验证。使用此选项,客户端需要输入指定的用户名和密码才能连接到 GoTTY 服务器。请注意,凭据将以纯文本格式在服务器和客户端之间传输。要进行更严格的身份验证,请考虑以下所述的SSL / TLS客户端证书身份验证。

-r 选项是一种比较随意的方式来限制访问。使用此选项,GoTTY 会生成一个随机 URL,以便只有知道该 URL 的人才能访问服务器。

github 链接: https://github.com/yudai/gotty 13.9k Star

3、WebSocket 教程

易学易用的 WebSocket 教程,前端使用 html5, 后端使用 Python 的 pywebsocket: https://www.runoob.com/html/html5-websocket.html

如果您对文章感兴趣,请微信搜索「Python七号」并关注,第一时间获取一手原创干货。