握手
1 客户端发起握手请求:客户端向服务器发送一个特殊的HTTP请求,其中包含一个Upgrade
字段,表明客户端希望将该连接从HTTP协议升级为WebSocket协议。请求的关键部分包括:
GET请求:客户端使用GET
方法请求与WebSocket连接
- Connection字段:值为
Upgrade
,指示连接要从HTTP升级。 - Upgrade字段:值为
websocket
,表示升级的目标协议。 - Sec-WebSocket-Key:随机生成的Base64编码密钥,用于握手的安全性验证。
- Sec-WebSocket-Version:协议版本号,通常为13。
2 服务器响应握手请求:服务器接收到握手请求后,确认请求是否有效,并返回一个包含101 Switching Protocols
状态码的响应,表示协议升级成功。服务器的响应包括:
- HTTP 101状态码:101 状态码表示服务器已经理解了客户端的请求,并将通过
Upgrade
消息头通知客户端采用不同的协议来完成这个请求。 - Upgrade字段:与客户端相同,表明升级为WebSocket协议。
- Sec-WebSocket-Accept:根据客户端的
Sec-WebSocket-Key
计算出的验证密钥,用于完成握手的安全性校验。这个密钥是通过将Sec-WebSocket-Key
与一个常量字符串进行拼接(258EAFA5-E914-47DA-95CA-C5AB0DC85B11),然后通过SHA-1哈希生成,再进行Base64编码得到的。
3 连接升级完成:握手完成后,HTTP连接升级为WebSocket连接,客户端和服务器可以通过这个持久的连接开始双向通信。
帧
1、FIN: 占 1 个 bit
0:不是消息的最后一个分片 1:是消息的最后一个分片
2、RSV1, RSV2, RSV3:各占 1 个 bit一般情况下全为 0。当客户端、服务端协商采用 WebSocket 扩展时,这三个标志位可以非 0,且值的含义由扩展进行定义。如果出现非零的值,且并没有采用 WebSocket 扩展,连接出错。
3、Opcode: 4 个 bit
%x0:表示一个延续帧。当 Opcode 为 0 时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片;
%x1:表示这是一个文本帧(frame);
%x2:表示这是一个二进制帧(frame);
%x3-7:保留的操作代码,用于后续定义的非控制帧;
%x8:表示连接断开;
%x9:表示这是一个 ping 操作;
%xA:表示这是一个 pong 操作;
%xB-F:保留的操作代码,用于后续定义的控制帧。
4、Mask: 1 个 bit 表示是否要对数据载荷进行掩码异或操作。
0:否 1:是
5、Payload length: 7bit or 7 + 16bit or 7 + 64bit
表示数据载荷的长度
x
为 0~126:数据的长度为x
字节;
x
为 126:后续 2 个字节代表一个 16 位的无符号整数,该无符号整数的值为数据的长度;
x
为 127:后续 8 个字节代表一个 64 位的无符号整数(最高位为 0),该无符号整数的值为数据的长度。6、Masking-key: 0 or 4bytes
当 Mask 为 1,则携带了 4 字节的 Masking-key;
当 Mask 为 0,则没有 Masking-key。
7、Payload Data: 载荷数据
j = i % 4
maskkey[4] // j用来形容maskkey 0 1 2 3
payload[N] // i是给payload做索引用的payload[i] ^= maskkey[j];
每个WebSocket数据帧包含两个主要部分:帧头(Header)和有效载荷(Payload),帧头部分包含了控制信息,而有效载荷部分则是实际传输的数据。
- FIN:表示该数据帧是否是消息的最后一个帧,值为1表示结束,0表示后续还有帧。
- Opcode:标识该数据帧的类型,如文本帧、二进制帧等。常见的值为
0x1
:文本帧(UTF-8编码)0x2
:二进制帧0x8
:关闭帧0x9
:Ping帧0xA
:Pong帧
- Mask:标识数据帧的有效载荷是否进行了掩码操作。客户端发送的帧必须带有掩码,而服务器发送的帧可以不带掩码。
- Payload Length:有效载荷的长度,值为7位或更多,具体取决于载荷的大小。
- Masking Key:如果Mask位为1,则这个字段存在,用于解码有效载荷数据。
- Masking Key:如果Mask位为1,则这个字段存在,用于解码有效载荷数据。
客户端发送一帧数据
服务端回复同样的数据
部分测试代码
服务端
使用acl做Websocket服务端 , 只需要在HttpServlet类里面重载doWebSocket方法就可以实现websocket协议的交互
如果要加上ssl验证 , 在创建HttpServlet之前调用conn->setup_hook即可实现wss加密,否则普通的websocket连接将失败
客户端
使用libhv做websocket客户端 , examples里面的例子相当的简单,如果需要支持wss, 则需要在连接之前调用withTLS把初始化的hssl_ctx_opt_t设置进去即可。
wss 报文
server
client
使用了ssl加密之后抓包发现多了很多TLS的报文,且看不到自己发送的明文了