1. 基础
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议,是应用层协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就形成了一条快速通道,创建了持久性的连接,并可进行双向数据传输。这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。
现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
Websocket 使用 ws 或 wss 的统一资源标志符,类似于 HTTPS,其中 wss 表示在 TLS 之上的 Websocket。如:
ws://example.com/wsapi
wss://secure.example.com/
Websocket 使用和 HTTP 相同的 TCP 端口,可以绕过大多数防火墙的限制。默认情况下,Websocket 协议使用 80 端口;运行在 TLS 之上时,默认使用 443 端口。
2. 协议
WebSocket并不是全新的协议,而是利用了HTTP协议来建立连接。我们来看看WebSocket连接是如何创建的。
一个典型的Websocket握手请求如下:
客户端请求
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13
服务器回应
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/
- Connection 必须设置 Upgrade,表示客户端希望连接升级。
- Upgrade 字段必须设置 Websocket,表示希望升级到 Websocket 协议。
- Sec-WebSocket-Key 是随机的字符串,服务器端会用这些数据来构造出一个 SHA-1 的信息摘要。把 “Sec-WebSocket-Key” 加上一个特殊字符串 “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后计算 SHA-1 摘要,之后进行 BASE-64 编码,将结果做为 “Sec-WebSocket-Accept” 头的值,返回给客户端。如此操作,可以尽量避免普通 HTTP 请求被误认为 Websocket 协议。
- Sec-WebSocket-Version 表示支持的 Websocket 版本。RFC6455 要求使用的版本是 13,之前草案的版本均应当弃用。
- Origin 字段是可选的,通常用来表示在浏览器中发起此 Websocket 连接所在的页面,类似于 Referer。但是,与 Referer 不同的是,Origin 只包含了协议和主机名称。
- 其他一些定义在 HTTP 协议中的字段,如 Cookie 等,也可以在 Websocket 中使用。
返回码101
表示本次连接的HTTP协议即将被更改,更改后的协议就是Upgrade: websocket
指定的WebSocket协议。
如此,一个WebSocket连接就建立成功,浏览器和服务器就可以随时主动发送消息给对方。消息有两种,一种是文本,一种是二进制数据。通常,我们可以发送JSON格式的文本,这样,在浏览器处理起来就十分容易。
为什么WebSocket连接可以实现全双工通信而HTTP连接不行呢?实际上HTTP协议是建立在TCP协议之上的,TCP协议本身就实现了全双工通信,但是HTTP协议的请求-应答机制限制了全双工通信。WebSocket连接建立以后,其实只是简单规定了一下:接下来,咱们通信就不使用HTTP协议了,直接互相发数据吧。
3. 应用
在服务器方面,网上都有不同对websocket支持的服务器:
php - http://code.google.com/p/phpwebsocket/
jetty - http://jetty.codehaus.org/jetty/(版本7开始支持websocket)
netty - http://www.jboss.org/netty
ruby - http://github.com/gimite/web-socket-ruby
Kaazing - https://web.archive.org/web/20100923224709/http://www.kaazing.org/confluence/display/KAAZING/Home
Tomcat - http://tomcat.apache.org/(7.0.27支持websocket,建议用tomcat8,7.0.27中的接口已经过时)
node.js - https://github.com/Worlize/WebSocket-Node
node.js - http://socket.io
nginx - http://nginx.com/
mojolicious - http://mojolicio.us/
python - https://github.com/abourget/gevent-socketio
Django - https://github.com/stephenmcd/django-socketio
erlang - https://github.com/ninenines/cowboy.git
客户端API简单示例:
var ws = new WebSocket("wss://echo.websocket.org"); ws.onopen = function(evt) { console.log("Connection open ..."); ws.send("Hello WebSockets!"); }; ws.onmessage = function(evt) { console.log( "Received Message: " + evt.data); ws.close(); }; ws.onclose = function(evt) { console.log("Connection closed."); };
可利用echo.websocket.org测试TLSv1.2或wss,协议报文为密文。
4. 报文
在本地nodejs运行websocket服务器,浏览器console运行client,基于HTTP交互的websocket报文是明文。
服务器
node_server.js
'use strict'; const WebSocket = require('ws'); const WebSocketServer = WebSocket.Server; const wss = new WebSocketServer({ port : 3000 }); wss.on('connection', function(ws){ console.log(`[SERVER] connection()`); ws.on('message', function(message){ console.log(`[SERVER] Received: ${message}`); ws.send(`ECHO : ${message}`, (err) => { if(err){ console.log(`[SERVER] error: ${err}`); } }); }); });
# npm install ws # node node_server.js
客户端
// 打开一个WebSocket: var ws = new WebSocket('ws://localhost:3000/test'); // 响应onmessage事件: ws.onmessage = function(msg) { console.log(msg); }; // 给服务器发送一个字符串: ws.send('Hello!');
浏览器console下输入即可。
报文
wireshark直接监听lo即可监测websocket整个交互过程。TCP三次握手 -- HTTP升级协议websocket -- websocket明文发送接收 -- TCP keep-alive -- websocket/TCP断开连接。
一次完整的交互报文如下:
HTTP协议升级websocket:
HTTP协议升级websocket确认:
websocket发送Hello world:
websocket接收:
参考:
1. websocket是什么及原理 知乎 ovear 或转载 看完让你彻底搞懂Websocket原理
3. websocket教程 阮一峰 http://jsbin.com/muqamiqimu/edit?js,console js在线运行
4. HTML5 websocket runoob