websocket 建立过程
客户端请求
- Connection: Upgrade 表示要升级协议
- Upgrade: websocket 告诉服务器要升级为 websocket 协议
- Sec-WebSocket-Version: 13 表示 websocket 的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader,里面包含服务端支持的版本号。
- Sec-WebSocket-Key:客户端随机生成的一个base64编码,与后面服务端响应头配套,提供基本的防护,防止无意的连接。
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
服务器响应
- 服务器返回 101 状态码,并切换为 WebSocket 协议建立全双工连接
- Sec-WebSocket-Accept:客户端请求头的 Sec-Websocket-Key 与 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接,通过 SHA1 算摘要,最后转为 base64 字符串返回。
所谓防止无意的连接是指,http 客户端不小心请求了 websocket 服务。
HTTP/1.1 101
Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
数据帧分析
图来自掘金-黄Java-【译】WebSocket协议第五章——数据帧(Data Framing)
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
FIN: 1 bit。表示这是消息的最后一个片段。第一个片段也有可能是最后一个片段。
RSV1,RSV2,RSV3: 每个1 bit。必须设置为0,除非扩展了非0值含义的扩展。如果收到了一个非0值但是没有扩展任何非0值的含义,接收终端必须断开WebSocket连接。
Opcode: 4 bit。操作码,如果收到一个未知的操作码,接收终端必须断开WebSocket连接。
- %x0 表示一个持续帧
- %x1 表示一个文本帧
- %x2 表示一个二进制帧
- %x3-7 预留给以后的非控制帧
- %x8 表示一个连接关闭包
- %x9 表示一个ping包
- %xA 表示一个pong包
- %xB-F 预留给以后的控制帧
Mask: 1 bit,mask标志位,定义“有效负载数据”是否添加掩码。如果设置为1,那么掩码的键值存在于Masking-Key中。Masking-Key中一般用于解码“有效负载数据”。
Payload length: 7 bits, 7+16 bits, or 7+64 bits,以字节为单位的“有效负载数据”长度。如果值为0-125,那么就表示负载数据的长度。如果是126,那么接下来的2个bytes解释为16bit的无符号整形作为负载数据的长度。如果是127,那么接下来的8个bytes解释为一个64bit的无符号整形(最高位的bit必须为0)作为负载数据的长度。多字节长度量以网络字节顺序表示(译注:应该是指大端序和小端序)。在所有的示例中,长度值必须使用最小字节数来进行编码,例如:长度为124字节的字符串不可用使用序列126,0,124进行编码。有效负载长度是指“扩展数据”+“应用数据”的长度。“扩展数据”的长度可能为0,那么有效负载长度就是“应用数据”的长度。
Masking-Key: 0 or 4 bytes,所有从客户端发往服务端的数据帧都已经与一个包含在这一帧中的32 bit的掩码进行过了运算。为什么需要掩码?为了安全,但并不是为了防止数据泄密,而是为了防止早期版本的协议中存在的代理缓存污染攻击(proxy cache poisoning attacks)等问题。
Payload data:有效负载数据,是指“扩展数据”和“应用数据”。
Extension data: x bytes:除非协商过扩展,否则“扩展数据”长度为0 bytes。
Application data: y bytes。任意的“应用数据”,占用“扩展数据”后面的剩余所有字段。“应用数据”的长度等于有效负载长度减去“扩展应用”长度。