Websocket协议
一、Websocket协议简介
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
1. Websocket和HTTP
- HTTP只能是浏览器向服务端发送请求,Websocket还可以是服务端主动向客户端推送数据。
- HTTP是无连接的,Websocket是长连接的。
- Websocket对协议的内容没有规定,只限制协议的格式,适合用于自定义通信协议。
- Websocket在TCP建立连接之后,还需要一次握手,采用HTTP协议格式完成Handshark。
- Websocket使用ws或wss的同一资源标志符,类似于HTTPS,其中wss表示建立在TLS之上的Websocket。
- 默认情况下,Websocket协议使用80端口;运行在TLS之上时,默认使用443端口。
2. Websocket的协议格式
对于协议格式的各个bit,在参考文档第28页中有详细介绍。
从第0个字节开始,第1个字节的高七位(Payload len(7))表示Payload Data的长度,只能表示0-125。
当Payload len(7)为126时,Payload Data的长度由Payload len(7)之后的两个字节表示,unsigned short。
当Payload len(7)为127时,Payload Data的长度由Payload len(7)之后的八个字节表示,且最高位必须为0,相当于64位机上的long long。
3. Websocket的一次握手
Websocket在TCP建立连接完成后,还需要进行一次握手,用于验证合法性。
Handshark的数据格式为HTTP,格式相对固定。
3.1 一个典型的Websocket握手请求如下:
客户端请求
GET / HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: example.com Origin: http://example.com Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
服务器回应
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Protocol: chat
- Connection 必须设置 Upgrade,表示客户端希望连接升级。
- Upgrade 字段必须设置 Websocket,表示希望升级到 Websocket 协议。
- Sec-WebSocket-Key 是客户端生成的随机的字符串,服务器端会根据这个字段返回Sec-WebSocket-Accept字段。
- Sec-WebSocket-Protocol是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议。
- Sec-WebSocket-Version 表示支持的 Websocket 版本。RFC6455 要求使用的版本是 13,之前草案的版本均应当弃用。
- Origin 字段是可选的,通常用来表示在浏览器中发起此 Websocket 连接所在的页面,类似于 Referer。但是,与 Referer 不同的是,Origin 只包含了协议和主机名称。
- 其他一些定义在 HTTP 协议中的字段,如 Cookie 等,也可以在 Websocket 中使用。
3.2 Sec-WebSocket-Accept的计算
Sec-WebSocket-Accept字段的值是服务器根据客户端发送的Sec-WebSocket-Key字段的值计算得到的。
RFC6455提供了一个固定的GUID("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")参与计算。
计算过程如下:
- sha_value = SHA1(Sec-WebSocket-Accept+GUID)
- Sec-WebSocket-Accept = base64_encode(sha_value )
4. Payload传输
Payload内容的传输可以是明文或密文,由MASK字段设定。该字段为1表示密文传输。
密钥为Masking-Key字段的32bits数据。
加解密算法为:(原始数据D,结果数据E,密钥为K[4]以字节为单位)
for(int i = 0; i < D.length; ++i)
E = D^K[i%4]
5. FIN标志位
指示这是消息中的最后一个片段。第一个片段也可能是最后的片段。
6. opcode标志位
定义“有效载荷数据”的解释。如果一个未知的opcode被接收时,接收端必须失败WebSocket Connection_。定义了以下值。
* %x0表示延续帧
* %x1表示文本框
* %x2表示二进制坐标系
* %x3-7被预留给其他非控制帧
* %x8表示连接关闭
* %x9表示一个ping
* %xA表示pong
* %xB-F预留给进一步的控制帧
----------------------------------------------------------------------------------------------------
参考文档:websocket_rfc6455
github:https://github.com/illusorycat/Websocket.git