阅读本文大概需要 3.6 分钟。
最近一两年一直在使用 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>
<preid="log"></pre>
<script>
// helper function: log message to screen
function log(msg){
document.getElementById('log').textContent += msg +'\n';
}
// setup websocket with callbacks
var ws =newWebSocket('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