【通信协议】WebSocket协议
🏫【通信协议】WebSocket协议
2022-3-23
| 2023-10-28
0  |  0 分钟
type
status
date
slug
summary
tags
category
icon
password
Sub-item
Last edited time
Oct 28, 2023 10:39 AM
Parent item
领域
WebSocket协议是一个独立的基于TCP的协议。它与HTTP唯一的关系是它的握手是由HTTP服务器解释为一个Upgrade请求。

为什么需要websocket

初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。HTTP 协议做不到服务器主动向客户端推送信息。
这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。
轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。

websocket协议的通信

websocket协议的通信分为3个部分:
  • 打开通道阶段握手
  • 数据传输
  • 关闭通道阶段握手
notion image

打开通道阶段握手

WebSocket协议首先借助于HTTP协议建立通道。然后在此基础上用真正的WebSocket协议进行通信。
notion image
具体请求数据的实例:
notion image
其中重要的几个字段如下:
  1. Origin 表明请求来自于哪个站点;
  1. Host 表明目标主机;
  1. Sec-WebSocket-Version 表明协议版本,要求双端版本一致。当前 WebSocket 协议版本默认为 13
  1. Connection和Upgrade字段告诉服务器,客户端发起的是WebSocket协议请求
  1. Sec-WebSocket-Extensions表示客户端想要表达的协议级的扩展。
  1. Sec-WebSocket-Key是一个Base64编码值,由浏览器随机生成,用于防止攻击者恶意欺骗服务端。
  1. Sec-WebSocket-Accept 则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key
  1. Sec-WebSocket-Version表明客户端所使用的协议版本。
  1. Sec-WebSocket-Accept 表示服务器接受了客户端的请求
http请求完成后响应的状态码为101,表示切换了协议,说明WebSocket协议通过http协议来建立运输层的TCP连接,之后便与http协议无关了

发送和接收数据

WebSocket 双端传输的是一个个数据帧。格式如下:
notion image
  • FIN。
    • 占 1 bit,值对应的含义如下:
    • 0:不是消息的最后一个分片;
    • 1:是消息的最后一个分片;
  • RSV1 RSV2 RSV3
    • 均占 1 bit,一般情况下值为 0。当客户端、服务端协商采用 WebSocket 扩展时,这三个标志位可以非 0,且值的含义由扩展进行定义。如果出现非零的值,但并没有采用 WebSocket 扩展,则连接出错。
  • Opcode
    • 占 4 bit,其值可以是 %x0%x1%x2%x3~7%x8%x9%xA%xB~F 中的任何一个。值对应的含义如下:
    • %x0:表示一个延续帧。当 Opcode 为 0 时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片;
    • %x1:表示这是一个文本帧(text frame);
    • %x2:表示这是一个二进制帧(binary frame);
    • %x3-7:保留的操作代码,用于后续定义的非控制帧;
    • %x8:表示连接断开,是一个控制帧(close);
    • %x9:表示这是一个心跳请求(ping);
    • %xA:表示这是一个心跳响应(pong);
    • %xB-F:保留的操作代码,用于后续定义的控制帧;
  • Mask
    • 占 1 bit,其值为 01。值 0 表示要对数据进行掩码异或操作,反之亦然。
  • Payload length
    • 占 7 bit 或 7+16 bit 或 7+64 bit,表示数据的长度,其值可以是0~127 中的任何一个数。值对应的含义如下:
    • 0~125:数据的长度等于该值;
    • 126:后续 2 个字节代表一个 16 位的无符号整数,该无符号整数的值为数据的长度;
    • 127:后续 8 个字节代表一个 64 位的无符号整数(最高位为 0),该无符号整数的值为数据的长度。
  • 掩码
    • 掩码的作用并不是为了防止数据泄密,而是为了防止早期版本的协议中存在的代理缓存污染攻击(proxy cache poisoning attacks)问题。这里要注意的是从客户端向服务端发送数据时,需要对数据进行掩码操作;从服务端向客户端发送数据时,不需要对数据进行掩码操作。
      所有客户端发送到服务端的数据帧,Mask都是1。如果服务端接收到的数据没有进行过掩码操作,服务端需要断开连接。
      如果Mask是1,那么在Masking-key中会定义一个掩码键(masking key),并用这个掩码键来对数据载荷进行反掩码。
      掩码算法:按位做循环异或运算,先对该位的索引取模来获得 Masking-key 中对应的值 x,然后对该位与 x 做异或,从而得到真实的 byte 数据。
      掩码键是由客户端随机选择的32位值。
  • Masking-key
    • 0 or 4 bytes。客户端发送到服务器的所有帧通过一个包含在帧中的32位值来掩码。如果mask位设置为1,则该字段存在,如果mask位设置为0,则该字段缺失。
  • Payload Data
    • 双端接收到数据帧之后,可以根据上述几个数据帧组件的值对 Payload Data 进行处理或直接提取数据。
在双端建立 WebSocket 连接后,任何一端都可以给另一端发送消息,这里的消息指的就是数据帧。但平时我们输入或输出的信息都是“明文”,所以在消息发送前需要将“明文”通过一定的方法转换成数据帧。而在接收端,拿到数据帧后需要按照一定的规则将数据帧转换为”明文“。下图描述了双端收发 Hello, world 的主要流程:
notion image

关闭通道阶段握手

服务端会定期给所有的客户端发送一个 opcode 为 %x9 的数据帧,这个数据帧被称为 Ping 帧。客户端在收到 Ping 帧时,必须回复一个 opcode 为 %xA 的数据帧(又称为 Pong 帧),否则服务端就可以主动断开连接。反之,如果服务端在发送 Ping 帧后能够得到客户端 Pong 帧的回应,就代表这个客户端是活跃的,不要断开连接。
如果需要关闭连接,那么一端向另一端发送 opcode 为 %x8 的数据帧即可,这个数据帧被称为关闭帧。在收到这样一个帧时,另一个节点在响应中发送一个Close帧。
发送一个控制帧之后,表示连接将被关闭,一个节点不会发送任何更多的数据;
接收一个控制帧之后,表示连接将被关闭,一个节点会丢弃任何收到的数据;

总结

WebSocket 有几个关键点:握手、数据与数据帧的转换、保持连接的 Ping 帧和 Pong 帧、主动关闭连接的关闭帧。
 
websocket协议规范的中文翻译如下:
websocket-protocol
shiqinfeng1Updated Aug 12, 2019
计算机基础
  • Web服务
  • 【通信协议】浏览器跨域访问机制【通信协议】ProtoBuf
    目录