杂谈
在一两年前就已经使用过websocket协议,但是当时也仅仅是停留在使用上,对于很多原理以及协议的实现没有过多的理解。
在很长一段时间里,我对websocket都还只是停留在“会用”的阶段。
最近总算有时间来捣鼓捣鼓一些底层原理性的东西,算是查缺补漏吧。
让我们进入我们的主题——websocket。
WebSocket 初探
首先,WebSocket是一个持久连接协议,WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)
WebSocket和HTTP之前会有一些小小的共同点,比如:
1、他们都是基于TCP。因为WebSocket是基于TCP的,所以连接可靠性得到了保障。
2、他们都有“Header”,像 Cookie=xxxx;Content-Type=xxxx;
这样的键值对
他们之间的关系就像是这样的:
WebSocket 之深入
握手阶段
一起来看看WebSocket建立连接的过程。
这里是浏览器发起的请求:
可以看到,比起传统的HTTP协议,多了几个头:Upgrade
Sec-WebSocket-Key
Sec-WebSocket-Protocol
Sec-WebSocket-Version
这些头部是用于给WebSocket握手建立连接用的,指定的是websocket协议Sec-WebSocket-Key
是浏览器随机生成的base64 encode的值,用来询问服务器是否是支持WebSocket。
再看看服务端返回的Response:
Sec-WebSocket-Accept
是将请求包Sec-WebSocket-Key
的值,与”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″这个字符串进行拼接,然后对拼接后的字符串进行sha-1运算,再进行base64编码得到的。用来说明自己是WebSocket助理服务器。
Data Frame(数据帧)
WebSocket数据帧结构如下图所示:
这是官方文档上给出的示例。
FIN:
这里第一位FIN表示这是消息的最后一帧(结束帧),一个消息由一个或多个数据帧构成。若消息由一帧构成,起始帧即结束帧。FIN位占1bit。
RSV1,RSV2,RSV3:各1位
MUST be 0 unless an extension is negotiated that defines meanings for non-zero values. If a nonzero value is received and none of the negotiated extensions defines the meaning of such a nonzero value, the receiving endpoint MUST Fail the WebSocket Connection.
大致意思是如果未定义扩展,各位是0;如果定义了扩展,即为非0值。如果接收的帧此处非0,扩展中却没有该值的定义,那么关闭连接。
OPCODE:4位
解释PayloadData,如果接收到未知的opcode,接收端必须关闭连接。
0x0表示附加数据帧
0x1表示文本数据帧
0x2表示二进制数据帧
0x3-7暂时无定义,为以后的非控制帧保留
0x8表示连接关闭
0x9表示ping
0xA表示pong
0xB-F暂时无定义,为以后的控制帧保留
MASK:1位
用于标识PayloadData是否经过掩码处理。如果是1,Masking-key域的数据即是掩码密钥,用于解码PayloadData。客户端发出的数据帧需要进行掩码处理,所以此位是1。
Payload length:7位,7+16位,7+64位
PayloadData的长度(以字节为单位)。
如果其值在0-125,则是payload的真实长度。
如果值是126,则后面2个字节形成的16位无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。
如果值是127,则后面8个字节形成的64位无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。
长度表示遵循一个原则,用最少的字节表示长度(我理解是尽量减少不必要的传输)。举例说,payload真实长度是124,在0-125之间,必须用前7位表示;不允许长度1是126或127,然后长度2是124,这样违反原则。
Payload长度是ExtensionData长度与ApplicationData长度之和。ExtensionData长度可能是0,这种情况下,Payload长度即是ApplicationData长度。
WebSocket协议规定数据通过帧序列传输。
客户端必须对其发送到服务器的所有帧进行掩码处理。
服务器一旦收到无掩码帧,将关闭连接。服务器可能发送一个状态码是1002(表示协议错误)的Close帧。
而服务器发送客户端的数据帧不做掩码处理,一旦客户端发现经过掩码处理的帧,将关闭连接。客户端可能使用状态码1002。
WebSocket 心跳
心跳主要是用于检测对端是否在线,以减小资源压力。
WebSocket的心跳主要是通过 Ping
、Pong
控制帧进行控制。Ping的协议头是0x9,Pong的协议头是0xA
在一端发出 Ping
帧后,若对端收到该帧,则必须回应 Pong
帧以表示在线
参考
https://datatracker.ietf.org/doc/rfc6455/
https://www.zhihu.com/question/20215561/answer/40316953