背景
websocket是html5提出的新的协议规范,填补了无法长连接的空缺。在没有websocket之前,由于http是短连接,只能由客户端请求服务器,一些功能(即时通讯/处理耗时的请求等)只能通过轮询来做,不断的轮询服务器是否有新的消息。
websocket正是为了解决这个问题而生,websocket是一个长连接/全双工的协议。
优点
不用频繁送HTTP请求,只需要发送一个HTTP请求进行websocket握手,接下来则可以利用该TCP连接通过websocket协议通讯,避免了传输多个HTTP Header的浪费。
全双工通道,突破了HTTP请求时只能client请求server的限制,server可以直接push消息给client端。
可以边生成数据边传递,不需要等数据生成完毕,提高了效率
websocket数据传输是基于数据帧的,可以分片传输,不需要怕数据太大包容纳不下。
过程
websocket协议包含三个过程。
1. 握手打开连接(使用HTTP协议,通过HTTP协议建立连接,以免服务器不支持websocket直接不予回应)
2. 数据传输
3. 握手结束连接
注意:以下大部分例子使用官网提供的demo进行测试,使用wireshark进行抓包
1. Opening handshake
Opening handshake使用HTTP协议,通过HTTP协议建立连接,以免服务器不支持websocket直接不予回应。
从最开始到现在一共有三种握手方法:
(1)基于flash的handshake,一般用于IE或者旧版chrome,没有加密
(2)基于md5,较早的握手方法,有两个key,使用md5加密
(3)基于sha1,协议版本需要是13或以上,现在主要是使用这个,所以这里只讨论这个。
当客户端想要和服务器建立websocket连接,客户端需要发送一个HTTP请求,服务器也会响应这个请求。
请求:
响应:
- 请求中的这两个字段代表着客户端想将HTTP连接升级为websocket连接
Connection: Upgrade
Upgrade: websocket
- 客户端会经过校验才确定和服务器创建websocket连接,这里需要关注请求中的Sec-WebSocket-Key和响应中的Sec-WebSocket-Accept
Sec-WebSocket-Key: MK5n+7rxCGBj608e1/VJQw==
Sec-WebSocket-Accept: ofJ3UpTzhgemcUGIgfEw7NmPbqw=
这里key和accept的关系是:
(1)key与一段特殊字符串拼接(”258EAFA5-E914-47DA-95CA-C5AB0DC85B11”)
MK5n+7rxCGBj6O8e1/VJQw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
(2)再经过sha1运行得出160位的字符串
a1f2775294f38607a671418881f130ecd98f6eac
(3)将上面的hash值经过base64加密
注意此处,hash值是16进制模式,直接转换成二进制加密即可
101000011111001001110111010100101001 ...
↓
101000 011111 001001 110111 010100 101001 ...
↓
ofJ3UpTzhgemcUGIgfEw7NmPbqw=
最后得出来的值必须与accept的值一致,否则校验不通过。
2. 数据传输
当客户端校验成功,就会与服务端建立起websocket连接,可以开始传输数据。
使用客户端向服务端发送文本消息,可见websocket的数据包较小,这里来逐个分析数据包格式。
(1)前面四个bit为标识位。
第一位为Fin,标识着此消息是否为最后一个数据包。如果数据太大被切分成多个包,则前面若干个包Fin为0,最后一个为1 。此处包比较小,可以容纳下数据,为1
后面3个bit暂时保留。
(2)4-7bit为Opcode,操作码
0x0:标识一个中间数据包
0x1:标识一个text类型数据包
0x2:标识一个binary类型数据包
0x3-7:保留
0x8:标识一个断开连接类型数据包
0x9:标识一个ping类型数据包
0xA:标识一个pong类型数据包
0xB-F:保留
(3)第8bit为Mask
标识着是否使用掩码,协议要求客户端发送数据必须使用掩码,Mask为1,服务器发送数据不使用掩码,Mask为0
(4)9-15bit为payload的长度
这7个bit固定用于显示paylaod长度,如果7个bit不够用,可以扩展最多至后面8个字节
- 如果其值在0-125,则该值是payload的真实长度。
- 如果值是126,则payload的真实长度为后面16位
- 如果值是127,则payload的真实长度为后面64位
(5)payload Length后面是Mask-Key,占8位。
假如Mask为1,则payload会与mask-key做掩码运算(每8位做一次XOR运算)得到Masked-Payload用作实际传输。
总结一下,websocket数据包格式如下:
3. closing handshake
当某一端想要关闭连接时,发送close包,另一端回复一个同样的包即可。close包的格式与上面的数据传输的格式一致,Opcode为0x8。
当某一端发生异常,或者是Mask码不对(客户端发的不为1,或者服务端发的不为0),将会关闭连接,这时close包中会包含错误状态码,1002
更多的websocket状态码:https://segmentfault.com/a/1190000007587248