简介
什么是 WebSocket
WebSocket 协议是 HTML5 标准中定义的一种新协议(RFC 6455),它实现了浏览器与服务器之间的全双工通信(full-duplex)。
为什么需要 WebSocket
传统的 HTTP 方案只适用于客户端主动发起请求的场景,无法满足服务器主动推送消息的需求。基于 HTTP 的 Ajax 和长轮询(Long Poll)等技术通过轮询方式工作,消耗大量网络带宽和计算资源。
WebSocket 相对于普通的 TCP Socket 通信,在应用层定义了基本的交互流程,使得 Tornado 等服务器框架和 JavaScript 客户端可以构建标准的 WebSocket 模块。
WebSocket 的特点
WebSocket 的最大特点是服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,实现真正的双向平等对话。
其他特点包括:
- 建立在 TCP 协议之上,服务器端实现较为容易。
- 与 HTTP 协议兼容良好,默认端口为 80 和 443,握手阶段使用 HTTP 协议,因此不易被屏蔽,能通过各种 HTTP 代理服务器。
- 数据格式轻量,性能开销小,通信高效。
- 可以发送文本或二进制数据。
- 无同源限制,客户端可与任意服务器通信。
- 协议标识符为
ws
(加密时为wss
),服务器网址即为 URL。
WebSocket 握手原理
WebSocket 的通信原理是在客户端与服务器之间建立 TCP 持久连接,从而实现服务器即时推送消息。
虽然 WebSocket 不是 HTTP 协议,但在握手阶段仍使用 HTTP 进行传输。
客户端握手请求
客户端通过发送包含特殊字段的 HTTP 请求告知服务器建立 WebSocket 连接,特殊字段如下:
GET ws://echo.websocket.org/?encoding=text HTTP/1.1
Origin: http://websocket.org
Connection: Upgrade
Sec-WebSocket-Key: uRovscZjNol/umbTt5uKmw==
Upgrade: websocket
Sec-WebSocket-Version: 13
含义如下:
- 客户端希望建立 WebSocket 连接,
ws
对应http
,wss
对应https
。 - 客户端使用的 WebSocket 版本为 13,密钥为
uRovscZjNol/umbTt5uKmw==
(用于标识连接,不用于加密)。 Origin
由浏览器添加,WebSocket 协议本身不强制同源策略,但服务器可根据Origin
拒绝请求。
可能还存在 Sec-WebSocket-Protocol: chat
字段,由用户定义,用于区分同 URL 下不同服务的协议。
服务端握手响应
服务器若同意建立 WebSocket 连接,则返回类似响应,特殊字段如下:
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: WebSocket
Access-Control-Allow-Origin: http://websocket.org
Access-Control-Allow-Credentials: true
Sec-WebSocket-Accept: rLHCkw/SKsO9GAH/ZSFhBATDKrU=
Access-Control-Allow-Headers: content-type
- 服务器返回 HTTP 101,表示已将连接转换为 WebSocket。
Sec-WebSocket-Accept
是服务器确认并处理后的Sec-WebSocket-Key
,用于客户端验证服务器身份。- 同源策略取决于服务器实现。
至此,客户端与服务器间建立 TCP 持久连接,双方可随时发送消息。
HTML5 客户端实现
客户端围绕 WebSocket
对象展开。
在 JavaScript 中,通过如下代码初始化 WebSocket
对象,构造函数需传入服务器的 ws
或 wss
开头的 URL:
var Socket = new WebSocket(url);
回调函数属性
可为该对象的以下事件指定处理函数:
WebSocket.onopen
:连接建立时触发。WebSocket.onmessage
:收到服务器消息时触发。WebSocket.onerror
:通信出错时触发。WebSocket.onclose
:连接关闭时触发。
普通属性
WebSocket.readyState
:返回当前状态,共四种:WebSocket.CONNECTING
(0):正在连接。WebSocket.OPEN
(1):连接成功,可通信。WebSocket.CLOSING
(2):正在关闭。WebSocket.CLOSED
(3):已关闭或连接失败。
WebSocket.bufferedAmount
:表示未发送的二进制数据字节数,可用于判断发送是否完成。
主动操作方法
此外,可通过以下方法主动操作:
WebSocket.send(data)
:向服务器发送消息。WebSocket.close()
:主动关闭连接。
Tornado 服务端实现
Tornado 定义了 tornado.websocket.WebSocketHandler
类用于处理 WebSocket 请求。
消息处理方法
open()
:新连接建立时调用,可在此获取参数或操作 Cookie。on_message(message)
:收到客户端消息时调用。on_close()
:连接关闭时调用。
主动操作方法
write_message(message, binary=False)
:发送消息。close(code=None, reason=None)
:主动关闭连接。
其他方法
check_origin(origin)
:自定义同源检查策略。origin
参数来自请求头,若无Origin
则不调用。返回True
接受,False
拒绝。Tornado 4.0+ 默认仅允许Origin
与Host
域名一致的请求。
Nginx 配置
主要涉及 Upgrade
和 Connection
头的设置。
location ~ /api/kernels/ {
proxy_pass http://localhost:8888;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade "websocket";
proxy_set_header Connection "Upgrade";
proxy_read_timeout 86400;
}
保持连接
可通过以下方式保持连接:
- 修改 Nginx 的
proxy_read_timeout
。 - 客户端定时发送心跳包。
- 客户端异常断开时自动重连。