系列"/>
Websocket系列
系列文章目录
Websocket系列 --前世今生
文章目录
- 系列文章目录
- @[TOC](文章目录)
- 前言
- 连接握手
- 请求
- 应答
- 数据传输
- 关闭请求
- 总结
- 参考链接
- @[TOC](文章目录)
- 请求
- 应答
前言
Websocket通信分为三个步骤:
- 连接握手
- 数据传输(双工)
- 关闭握手
连接握手
连接握手分为两个步骤:请求和应答。WebSocket利用了HTTP协议来建立连接,使用的是HTTP的协议升级机制。
请求
一个标准的HTTP请求,格式如下:
请求头的具体格式定义参见Request-Line格式
请求header中的字段解析
-
协议升级机制
-
Origin
所有浏览器将会发送一个 Origin请求头。 你可以将这个请求头用于安全方面(检查是否是同一个域,白名单/ 黑名单等),如果你不喜欢这个请求发起源,你可以发送一个403 Forbidden。需要注意的是非浏览器只能发送一个模拟的 Origin。大多数应用会拒绝不含这个请求头的请求。 -
Sec-WebSocket-Key
由客户端随机生成的,提供基本的防护,防止恶意或者无意的连接。
应答
返回字段解析
-
Connection
可以参见 rfc7230 6.1 和 rfc7230 6.7 -
Sec-WebSocket-Accept
它通过客户端发送的Sec-WebSocket-Key 计算出来。计算方法:-
将 Sec-WebSocket-Key 跟 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接;
-
通过 SHA1 计算出摘要,并转成 base64 字符串。
Sec-WebSocket-Key / Sec-WebSocket-Accept 的主要作用还是为了避免一些网络通信过程中,一些非期待的数据包,”乱入“进来,导致一些错误的响应,并不能用于实现登录认证和数据安全,这些功能还需要应用层自己实现。
-
数据传输
此段落包含的内容,来自于此链接
WebSocket 以 frame 为单位传输数据, frame 是客户端和服务端数据传输的最小单元, 当一条消息过长时, 通信方可以将该消息拆分成多个 frame 发送, 接收方收到以后重新拼接、解码从而还原出完整的消息, 在 WebSocket 中, frame 有多种类型, frame 的类型由 frame 头部的 Opcode 字段指示, WebSocket frame 的结构如下所示:
该结构的字段语义如下:
-
FIN, 长度为 1 比特, 该标志位用于指示当前的 frame 是消息的最后一个分段, 因为 WebSocket 支持将长消息切分为若干个 frame 发送, 切分以后, 除了最后一个 frame, 前面的 frame 的 FIN 字段都为 0, 最后一个 frame 的 FIN 字段为 1, 当然, 若消息没有分段, 那么一个 frame 便包含了完成的消息, 此时其 FIN 字段值为 1。
-
RSV 1 ~ 3, 这三个字段为保留字段, 只有在 WebSocket 扩展时用, 若不启用扩展, 则该三个字段应置为 1, 若接收方收到 RSV 1 ~ 3 不全为 0 的 frame, 并且双方没有协商使用 WebSocket 协议扩展, 则接收方应立即终止 WebSocket 连接。
-
Opcode, 长度为 4 比特, 该字段将指示 frame 的类型, RFC 6455 定义的 Opcode 共有如下几种:
- 0x0, 代表当前是一个 continuation frame,既被切分的长消息的每个分片frame
- 0x1, 代表当前是一个 text frame
- 0x2, 代表当前是一个 binary frame
- 0x3 ~ 7, 目前保留, 以后将用作更多的非控制类 frame
- 0x8, 代表当前是一个 connection close, 用于关闭 WebSocket 连接
- 0x9, 代表当前是一个 ping frame
- 0xA, 代表当前是一个 pong frame
- 0xB ~ F, 目前保留, 以后将用作更多的控制类 frame
-
Mask, 长度为 1 比特, 该字段是一个标志位, 用于指示 frame 的数据 (Payload) 是否使用掩码掩盖, RFC 6455 规定当且仅当由客户端向服务端发送的 frame, 需要使用掩码覆盖, 掩码覆盖主要为了解决代理缓存污染攻击 (更多细节见 RFC 6455 Section 10.3)。
-
Payload Len, 以字节为单位指示 frame Payload 的长度, 该字段的长度可变, 可能为 7 比特, 也可能为 7 + 16 比特, 也可能为 7 + 64 比特. 具体来说, 当 Payload 的实际长度在 [0, 125] 时, 则 Payload Len 字段的长度为 7 比特, 它的值直接代表了 Payload 的实际长度; 当 Payload 的实际长度为 126 时, 则 Payload Len 后跟随的 16 位将被解释为 16-bit 的无符号整数, 该整数的值指示 Payload 的实际长度; 当 Payload 的实际长度为 127 时, 其后的 64 比特将被解释为 64-bit 的无符号整数, 该整数的值指示 Payload 的实际长度。
-
Masking-key, 该字段为可选字段, 当 Mask 标志位为 1 时, 代表这是一个掩码覆盖的 frame, 此时 Masking-key 字段存在, 其长度为 32 位, RFC 6455 规定所有由客户端发往服务端的 frame 都必须使用掩码覆盖, 即对于所有由客户端发往服务端的 frame, 该字段都必须存在, 该字段的值是由客户端使用熵值足够大的随机数发生器生成, 关于掩码覆盖, 将下面讨论, 若 Mask 标识位 0, 则 frame 中将设置该字段 (注意是不设置该字段, 而不仅仅是不给该字段赋值)。
-
Payload, 该字段的长度是任意的, 该字段即为 frame 的数据部分, 若通信双方协商使用了 WebSocket 扩展, 则该扩展数据 (Extension data) 也将存放在此处, 扩展数据 + 应用数据, 它们的长度和便为 Payload Len 字段指示的值。
以下是一个客户端和服务端相互传递文本消息的示例(内容来此于此链接)
其中模拟了长消息被切分为多个帧(continuation frame)的例子。
关闭请求
关闭相对简单,由客户端或服务端发送关闭帧,即可完成关闭。
总结
后续会讲一些市面上常用的一些实现WebSocket封装的类库。
参考链接
更多推荐
Websocket系列
发布评论