WebSocket 协议

解决的问题

基于浏览器的机制,实现客户端与服务端的双向通信.

协议概述

  1. 来自客户端握手

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

      2.来自服务端的握手

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
// 可选的头,表示允许的通过的客户端
Sec-WebSocket-Protocol: chat

以上,头顺序无所谓.

一旦客户端和服务器都发送了握手信号,如果握手成功,数据传输部分启动。这是双方沟通的渠道,独立于另一方,可随意发送数据。

服务器的响应,不是随意的,需要遵循一定的规则 请参考RFC 文档 第 6/7页:

  1. 获取客户端请求的 Sec-Weboscket-Key 字段值,去除收尾空白字符
  2. 与全球唯一标识符拼接 258EAFA5-E914-47DA-95CA-C5AB0DC85B11
  3. sha1 加密(短格式)
  4. base64 加密

上述结果得出的值即是服务端返回给客户端握手的 Sec-Websocket-Accept 头字段值. 

关闭链接

接收到一个 0x8 (opencode字段)控制帧后,链接也许立即断开,也许在接收完剩下的数据后断开。

  • 可以有消息体,指明消息原因,可作为日志进行记录。
  • 应用发送关闭帧后必须不在发送更多数据帧。
  • 如果一个端点接受到一个关闭帧且先前没有发送关闭帧,则必须发送一个关闭帧。
  • 端点在接受到关闭帧后,可以延迟响应关闭帧,继续发送或接受数据帧,但不保证一个已经发送关闭帧的端点继续处理数据。
  • 发送并接收了关闭帧的端点,被认为是关闭了 websocket 连接,其必须关闭底层的 TCP 连接。

设计理念

基于框架而不是基于流/文本或二进制帧.

链接要求

针对客户端要求

  • 握手必须是一个有效的 HTTP 请求
  • 请求的方法必须为 GET,且 HTTP 版本必须是 1.1
  • 请求的 REQUEST-URI 必须符合文档规定的要求(详情查看 Page 13)
  • 请求必须包含 Host 头
  • 请求必须包含 Upgrade: websocket 头,值必须为 websocket
  • 请求必须包含 Connection: Upgrade 头,值必须为 Upgrade
  • 请求必须包含 Sec-WebSocket-Key 头
  • 请求必须包含 Sec-WebSocket-Version: 13 头,值必须为 13
  • 请求必须包含 Origin 头
  • 请求可能包含 Sec-WebSocket-Protocol 头,规定子协议
  • 请求可能包含 Sec-WebSocket-Extensions ,规定协议扩展
  • 请求可能包含其他字段,如 cookie 等

不符合上述要求的服务器响应,客户端都会断开链接.

  • 如果响应不包含 Sec-WebSocket-Protocol 中指定的子协议,客户端断开
  • 如果响应 HTTP/1.1 101 Switching Protocols 状态码不是 101,客户端断开

针对服务端要求

  • 如果请求是 HTTP/1.1 或更高的 GET 请求,包含 REQUEST-URI 则应正确地按照文档要求进行解析.
  • 必须验证 Host 字段
  • Upgrade 头字段值必须是大小写不敏感的 websocket
  • Sec-WebSocket-keyd 解码时长度为 16Byte
  • Sec-WebSocket-Version 值必须是 13
  • Host 如果没有被包含,则链接不应该被解释为浏览器发起的行为
  • Sec-WebSocket-Protocol 中列出的客户端请求的子协议,服务端应按照优先顺序排列,响应
  • 任选的其他字段

响应要求:

  • 验证 Origin 字段,如果不符合要求的请求则返回适当的错误代码(例如:403)
  • Sec-WebSocket-Key 值是一个 base64 加密后的值,服务端不需要对其进行解码,而仅是用来创建服务器的握手.
  • 验证 Sec-WebSocket-Version 值,如果不是 13,则返回一个适当的错误代码(例如:HTTP/1.1 426 Upgrade Required)
  • 资源名验证
  • 子协议验证
  • extensions 验证

如果通过了上述验证,则服务器表示接受该链接.那么起响应必须符合以下要求详情查看 Page 23:

  1. 必须,状态行 HTTP/1.1 101 Switching Protocols
  2. 必须,协议升级头 Upgrade: websocket
  3. 必须,表示连接升级的头字段 Connection: Upgrade
  4. 必须,Sec-WebSocket-Accept 头字段,详情请查阅 协议概述 部分
  5. 可选:Sec-WebSocket-Protocols 头部

完整的响应代码如下(严格按照如下格式响应!!头部顺序无所谓!关键是后面的换行符注意了!严格控制数量!):

HTTP/1.1 101 Switching Protocols\r\n
Connection: Upgrade\r\n
Upgrade: websocket\r\n
Sec-WebSocket-Accept: 3nlEzv+LqVBYnTHclAqtk62uOTQ=\r\n
// 下面这个头字段为可选字段
Sec-WebSocket-Protocols: chat\r\n\r\n

基本框架协议

数据传输部分对  进行了分组!!由于是在bit层面上进行的数据封装,所以如果直接取出的话,获取到的将是处理后的数据,需要解密。下图是传输数据格式: 

  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+
 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 | |1|2|3|       |K|             |                               |
 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 |     Extended payload length continued, if payload len == 127  |
 + - - - - - - - - - - - - - - - +-------------------------------+
 |                               |Masking-key, if MASK set to 1  |
 +-------------------------------+-------------------------------+
 | Masking-key (continued)       |          Payload Data         |
 +-------------------------------- - - - - - - - - - - - - - - - +
 :                     Payload Data continued ...                :
 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 |                     Payload Data continued ...                |
 +---------------------------------------------------------------+

1. 特殊名词含义介绍

  1. 1bit,FIN
  2. 每个 1bit, RSV1、RSV2、RSV3
  3. 4bit,opcode(以下定义在ABNF中)

    • %x0 连续帧
    • %x1 文本帧
    • %x2 二进制帧
    • %x3 - %x7 保留帧
    • %x8 链接关闭
    • %x9 ping
    • %xA pong
    • %xB-F 保留的控制帧
    • 以上表示的都是 16 进制数值
  4. 1bit, mask

    • 客户端发送给服务端的数据都需要设置为 1
    • 也就是说数据都是经过掩码处理过的
  5. 7bit、7 + 16bit、7 + 64bit,Payload length 具体范围请参阅 RFC 文档(Page 31)

    payloadData的长度:7位,7+16位,7+64位,(超过16位和64位包长度,需要考虑网络字节序和本地字节序转换)
    如果其值在0-125,则是payload的真实长度。
    如果值是126,则后面2个字节形成的16位无符号整型数的值是payload的真实长度。
    如果值是127,则后面8个字节形成的64位无符号整型数的值是payload的真实长度
  6. 0/4 byte, masking-key

    • 客户端发送给服务端的数据都是经过掩码处理的,长度为 32bit
    • 服务端发送给客户端的数据都是未经过掩码处理的,长度为 0bit
  7. x + y Byte, Payload Data

    • 有效载荷数据
  8. x Byte, Extension Data

    • 扩展数据
  9. y Byte, Application Data

    • 应用数据

消息分片

分片目的

消息分片的主要目的是允许消息开始但不必缓冲整个消息时,发送一个未知大小的消息;未分片的消息需要缓冲整个消息,以便获取消息大小;

分片要求:

  • 首个分片 Fin = 0,opcode != 0x0,其后跟随多个 Fin = 0,opcode = 0x0的分片,终止于 Fin = 1,opcode = 0x0的片段
  • 扩展数据可能发生在分片中的任意一个分片中
  • 控制帧可能被注入到分片消息的中间,控制帧本身必须不被分割
  • 消息分片必须按照发送者发送顺序交付给收件人
  • 片段中的一个消息必须不能与片段中的另一个消息交替,除非已协商了一个能解释交替的扩展。
  • websocket服务器应能够处理分片消息中间的控制帧
  • 一个发送者可以为非控制消息(非控制帧)创建任何大小的片段
  • 不能处理控制帧
  • 如果使用了任何保留的位值且这些值的意思对中间件是未知的,一个中间件必须不改变一个消息的分片。
  • 在一个连接上下文中,已经协商了扩展且中间件不知道协商的扩展的语义,一个中间件必须不改变任何消息的分片。同样,没有看见WebSocket握手(且没被通知有关它的内容)、导致一个WebSocket连接的一个中间件,必须不改变这个链接的任何消息的分片。
  • 由于这些规则,一个消息的所有分片是相同类型,以第一个片段的操作码设置。因为控制帧不能被分片,用于一个消息中的所有分片的类型必须或者是文本、或者二进制、或者一个保留的操作码。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值