WebSocket
WebSocket的帧格式
WebSocket 使用了自定义的二进制分帧格式,将每个应用消息切分成一个或多个帧,对端等到接收到完整的消息后再进行组装与处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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。占1bit。表示后续是否还有帧。一个消息可能拆分成多个帧,接收方判断为最后一帧后将前面的帧拼接组成消息。TCP没有粘包,粘包是不合理的应用层协议设计导致的问题。
- opcode。占4bit。
- 8表示close(关闭连接)帧,主动关闭连接时需要发送这个控制指令。否则websocket会报1006错误,这个错误码可以用于区分连接是正常关闭的,还是其他异常情况。
- 9表示ping帧,10表示pong帧。ping/pong机制是为了在长时间无消息通信时,检测连接是否断开。目前只能由服务器发ping给浏览器,浏览器返回pong消息。浏览器目前没有开放发送控制指令的接口。
FIN
:1 bit 表示消息结束标志位。0表示还有后续帧, 1表示最后一帧。一个消息可能拆分成多个帧,接收方判断为最后一帧后将前面的帧拼接组成消息。RSV1
、RSV2
、RSV3
:1 bit 保留字段,除非一个扩展经过协商赋予了非零值的某种含义,否则必须为0。opcode
:4 bit 解释 payload data 的类型。如果收到识别不了的opcode,会直接断开。0表示连续的帧; 1表示text(纯文本)帧; 2表示binary(二进制)帧 ; 8表示close(关闭连接)帧; 9表示ping帧 ;10表示pong帧;其余为非控制帧而预留。ping/pong类型帧是为了在长时间无消息通信时,检测连接是否断开,目前只能由服务器发ping给浏览器,浏览器返回pong消息。1 2 3 4 5 6 7 8
%x0:表示一个延续帧。当 Opcode 为 0 时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片; %x1:表示这是一个文本帧(frame); %x2:表示这是一个二进制帧(frame); %x3-7:保留的操作代码,用于后续定义的非控制帧; %x8:表示连接断开; %x9:表示这是一个 ping 操作; %xA:表示这是一个 pong 操作; %xB-F:保留的操作代码,用于后续定义的控制帧。
MASK
:1 bit 标识 Payload data 是否经过掩码处理,如果是 1,Masking-key域的数据即为掩码密钥,用于解码Payload data。在标准规定,客户端发送数据必须使用掩码,而服务器发送则一定不使用掩码。所有客户端发送到服务端的数据帧,Mask都是1。Payload len
:7 bit | 7+16 bit | 7+64 bit 表示了 “有效负荷数据 Payload data”的长度:(1)如果是 0~125,那么就直接表示了 payload 长度 (2) 如果是 126,那么接下来的两个字节表示的 16位无符号整型数的值就是 payload 长度 (3)如果是 127,那么接下来的八个字节表示的 64位无符号整型数的值就是 payload 长度1 2 3 4
载荷数据的长度 x 为 0~126:数据的长度为 x 字节; x 为 126:后续 2 个字节代表一个 16 位的无符号整数,该无符号整数的值为数据的长度; x 为 127:后续 8 个字节代表一个 64 位的无符号整数(最高位为 0),该无符号整数的值为数据的长度
Masking-key
:0 | 4 bytes 掩码密钥。所有从客户端发送到服务端的帧都包含一个 32bits 的掩码(如果mask被设置成1),否则为0。一旦掩码被设置,所有接收到的 payload data 都必须与该值以一种算法做异或运算来获取真实值。client选取的32为随机数
掩码计算方法如下
1 2 3 4 5
original-octet-i:为原始数据的第i字节。 transformed-octet-i:为转换后的数据的第i字节。 j = i MOD 4 transformed-octet-i = original-octet-i XOR masking-key-octet-j
Payload data
:(x+y) bytes (载荷数据 = 扩展数据 + 应用数据) 它是 Extension data 和 Application data 数据的总和,但是一般扩展数据为空。Extension data
:x bytes 除非扩展被定义,否则就是0Application data
:y bytes 占据 Extension data 后面的所有空间
连接关闭
- 关闭帧包含一个关闭的状态码和指定的原因; 如果关闭帧中没有状态码,状态码默认为1005,如果websocket已经关闭,并且终端没有收到任何关闭帧,那么websocket的关闭状态码为1006
- 应用在发送了关闭帧之后,不能发送任何的数据帧
- 如果一端收到了关闭帧,并且之前没有发送关闭帧,这一端必须回复一个关闭帧(尽快回包,也可能延迟发送回包,直到当前message发送完成)
- 当一端发送和接收到了关闭报文,该端可以考虑关闭websocket连接,并且必须关闭底层的TCP连接; 如果是sever端必须立刻关闭TCP连接,client端等待server端关闭链接,但是也有可能关闭连接在收到关闭报文之后的任意时间(例如. 没有收到关闭的报文)
- 两端同时发送关闭报文,两端都会发送和接收到close报文,应该考虑关闭websocket连接和底层的tcp连接
规范化的关闭代码(标准)
- 1000:正常关闭(Normal Closure)
- 表示连接成功完成了预期的功能。
- 1001:终端离开(Going Away)
- 表示服务器或客户端因为离开或关闭而中断连接。
- 1002:协议错误(Protocol Error)
- 表示由于协议错误而中断连接。
- 1003:不可接受的数据(Unsupported Data)
- 表示接收到不能处理的数据类型(例如,服务器只能处理文本数据却收到了二进制数据)。
- 1005:没有状态码(No Status Received)
- 表示连接关闭时没有收到任何关闭状态码。这不是一个实际传输的状态码,只是一个占位符。
- 1006:连接异常关闭(Abnormal Closure)
- 表示连接非正常关闭,没有发送关闭帧。这不是一个实际传输的状态码,只是一个占位符。
- 1007:无效数据(Invalid frame payload data)
- 表示接收到的数据格式不正确(例如,非UTF-8数据)。
- 1008:违反策略(Policy Violation)
- 表示由于收到的数据违反了服务器的策略(例如,消息过大)。
- 1009:消息过大(Message Too Big)
- 表示收到的消息太大,无法处理。
- 1010:客户端要求扩展(Mandatory Extension)
- 表示客户端希望服务器支持某些扩展,而服务器没有满足此要求。
- 1011:服务器遇到意外情况(Internal Server Error)
- 表示服务器由于一些意外情况关闭了连接(例如,处理请求时出现了异常)。
- 1012:服务重启(Service Restart)
- 表示服务器由于重启而关闭连接。
- 1013:试试别的服务器(Try Again Later)
- 表示服务器由于过载或维护,暂时无法处理请求。
- 1014:坏网关(Bad Gateway)
- 表示服务器作为网关或代理时,收到上游服务器的无效响应。
自定义关闭代码(非标准)
应用程序或框架可能会定义自己的非标准关闭代码,这些代码通常在4000至4999的范围内。
使用这些关闭代码
关闭代码通常与一个关闭理由(reason)一起发送,可以帮助对端理解关闭的具体原因。关闭代码在WebSocket协议中通过关闭帧(close frame)发送。
iOS websocket 抓包工具
- Charles 、 Wireshark 、mitmproxy 、Socks5 代理(付费)