前言:
WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。在这之前都是客户端主动请求服务端,一请求一应答,很多时候实现消息更新都是采用ajax轮询,有延迟,有了WebSocket双方都可以主动发给对端,实现真正的推送。
1、HTTP和webSocket
(1)HTTP协议有两种方式
短轮询:ajax请求定时轮询,浏览器请求,服务端响应长轮询:浏览器请求到服务器,服务器一直打开,直到数据可发送。发送完数据后,浏览器关闭连接。缺点:实时性低,服务端到浏览器反馈效率低。
(2)webSocket协议,一次HTTP握手后,脱离HTTP协议使用webSocket通信
(3)HTTP向webSocket如何改造?
2、webSocket分为两个部分(基于协议帧):
(1)握手 (handshake)
握手基于HTTP,版本至少:1.1,方法GET
client(客户端发起连接请求):
Websocket客户端首先发起一个连接请求
Sec-Websocket-Key后面的那一串长度为24的字符串是客户端随机生成16位字符再通过base64编码生成的,我们暂时叫它cli_key。服务器必须用它经过一定的运算规则生成服务器端的key,暂时叫做ser_key,然后把ser_key发回给客户端,客户端验证正确后,握手成功。
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket // 指明使用websocket协议 Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 // 使用协议版本
生成服务端的密钥
然后把这一长串经过SHA-1算法加密,得到长度为20字节的二进制数据,
再将这些数据经过Base64编码,最终得到服务端的密钥,也就是ser_key。
server(服务端返回密钥):
服务端需要把密钥返回给客户端,完成握手,发送的数据格式如下:
至此,算是握手成功了!
HTTP/1.1 101 Switching Protocols Upgrade: websocket // 指明使用websocket协议 Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: chat
(2)数据传输(data transfer)
服务器端采用Tcp Socket方式接收数据,进行解析,协议格式如下: 第一个字节:FIN:1位,用于描述消息是否结束,如果为1则该消息为消息尾部,如果为零则还有后续数据包;
RSV1,RSV2,RSV3:各1位,用于扩展定义的,如果没有扩展约定的情况则必须为0
OPCODE:4位,用于表示消息接收类型,如果接收到未知的opcode,接收端必须关闭连接。
Webdocket数据帧中OPCODE定义:
0x0表示附加数据帧
0x1表示文本数据帧
0x2表示二进制数据帧
0x3-7暂时无定义,为以后的非控制帧保留
0x8表示连接关闭
0x9表示ping
0xA表示pong
0xB-F暂时无定义,为以后的控制帧保留
第二个字节:MASK:1位,用于标识PayloadData是否经过掩码处理,客户端发出的数据帧需要进行掩码处理,所以此位是1。数据需要解码。
PayloadData的长度:7位,7+16位,7+64位
如果其值在0-125,则是payload的真实长度。
如果值是126,则后面2个字节形成的16位无符号整型数的值是payload的真实长度。
如果值是127,则后面8个字节形成的64位无符号整型数的值是payload的真实长度。
上图是客户端发送给服务端的数据包,其中PayloadData的长度为二进制:01111110——>十进制:126;如果值是126,则后面2个字节形成的16位无符号整型数的值是payload的真实长度。也就是圈红的十六进制:00C1——>十进制:193 byte。所以PayloadData的真实数据长度是193 bytes;
3、webSocket实现接口:
interface WebSocket : EventTarget { readonly attribute DOMString url; // ready state const unsigned short CONNECTING = 0; const unsigned short OPEN = 1; const unsigned short CLOSING = 2; const unsigned short CLOSED = 3; readonly attribute unsigned short readyState; readonly attribute unsigned long bufferedAmount; // networking attribute EventHandler onopen; attribute EventHandler onerror; attribute EventHandler onclose; readonly attribute DOMString extensions; readonly attribute DOMString protocol; void close([Clamp] optional unsigned short code, optional DOMString reason); // messaging attribute EventHandler onmessage; attribute DOMString binaryType; void send(DOMString data); void send(Blob data); void send(ArrayBuffer data); void send(ArrayBufferView data); };4、客户端和服务端实现流程:
5、demo演示:
git地址:https://github.com/yanghao001/springboot-service/tree/master/springboot-websocket