websocket协议解析[RFC6455]
阅读数:117 评论数:0
跳转到新版页面分类
网络/通信
正文
连接时握手
websocket的握手实际上就是给服务器发送一个GET请求,里面带上指定的header即可。
1、request例子:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
(1)upgrade和Connection是请求握手时固定要填写的两个键值对。
(2)Sec-WebSocket-Key是一个16位的随机值,经过base64编码后生成。
(3)Sec-WebSocket-Version是使用的版本号。
(4)Sec-WebSocket-Protocol是选用的子协议,为可选字段,子协议是由websocket承载的协议。
2、response例子:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
(1)状态码为101的响应
(2)Sec-WebSocket-Accept是服务端利用Key和UUID拼接后再进行base64编码产生的一个值,客户端可进行验证。
这样,连接时握手就完成了。
数据帧
1、基础帧协议
因为一些安全的原因,从客户端发送到服务端的帧全部要与掩码进行异或运算过才有效,而服务端发送到客户端的帧不需要进行异或运算。
名称 | 长度 | 注释 |
FIN | 1bit | 标明这一帧是否是整个消息体的最后一帧 |
RSV1 RSV2 RSV3 | 1bit | 保留位,必须为0,如果不为0,则标记为连接失败 |
opcode | 4bit | 定义这一帧的类型 |
MASK | 1bit | 标明承载的内容是否需要用掩码进行异或 |
Masking-key | 0或4bytes | 掩码异或运算用的key |
Payload length | 7bit or 7+16bit or 7+64bit | 承载体的长度 |
2、opcode
数据包大体可分为两种,一种是字符数据包(string), 一种是字节数据包(byte)。
值 | 定义 |
%x0 | continuation frame,表明这个数据包是上一个数据包的延续。 |
%x1 | text frame,数据包是一个字符帧 |
%x2 | binary frame,数据包是一个字节帧 |
%x3-7 | 预留,非控制帧使用 |
%x8 | 关闭连接 |
%x9 | ping,心跳请求 |
%xA | pong,心跳响应 |
%B-F | 预留,供制帧使用 |
3、MASK
如果是客户端发送到服务端的数据包,我们需要使用掩码对payload的每一个字节进行异或运算,生成masked payload才能被服务器读取。
具体的运算其实很简单。
假设payload长度为pLen,mask-key长度为mLen,i作为payload的游标,j作为mask-key的游标,伪代码如下:
for (i = 0; i < pLen; i++){
int j = i % mLen;
maskedPayload[i] = payload[i] ^ maskKey[j];
}
4、payload长度
读取负载数据,需要知道读到哪里为止,这个过程稍微有点复杂:
(1)读取9-15位(包括9和15位本身),并转换为无符号整数,如果值小于或等于125,这个值就是长度;如果是126,请转到步骤2。如果127,请转到步骤3。
(2)读取接下来的16位并转换为无符号整数,并作为长度。
(3)读取接下来的64位并转换为无符号整数,并作为长度。
关闭连接时的握手
关闭连接的时候,只用发送一个opcode为0x08的帧,payload中前2个字节写入定义code,后续写入关闭连接的reason。