websocket协议规范

以下内容转载自 https://www.huaweicloud.com/articles/4157e9b5a58ef15e29d71f76b08e1b92.html

websocket协议规范

作者:二郎666 时间: 2019-11-02 08:06:49

标签:网络通信协议websocketwebsocket协议客户端vbwebsocket教程

《websocket协议详解》教程分三篇:

  1.  什么是websocket
  2. websocket协议规范
  3.  用vb编写websocket客户端示例(每秒百万弹幕吞吐量)

文章上方有详细的规范、源码链接。邮箱:952125505@qq.com ,QQ交流群:715895604


本文与websocket协议规范有冲突的地方,请以规范为准。(文章上方有websocket中文协议规范的链接)

握手

首先由客户端发起连接请求,服务端验证客户端握手数据并返回服务器握手数据,客户端验证服务端握手数据,双方完成握手。

客户端:请求协议升级

首先,客户端发起协议升级请求。(采用标准 HTTP 报文格式,且只支持GET方法。)

GET / HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: [LocalHost:80]
Origin: [LocalIP]
Pragma: no -cache
cache -Control: no -cache
Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==
Sec-WebSocket-Version: 13
Sec -WebSocket - Extensions: x -webkit - deflate - Frame;permessage-deflate; client_max_window_bits
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.2171.99 Safari/537.36

各参数含义:

  • Connection: Upgrade:表示要升级协议
  • Upgrade: websocket:表示要升级到 websocket 协议。
  • Sec-WebSocket-Version: 13:表示 websocket 的版本。
  • Sec-WebSocket-Key:阻止无效websocket协议连接。

需要指出Sec-WebSocket-Key的生成算法,伪代码:base64编码(16个随机字节)

其它部分没有什么好介绍的,跟http头信息一样,可以查看http相关资料。

服务端:响应协议升级

HTTP/1.1 101 Switching Protocols
Connection:Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=

返回状态码101表示协议升级成功(从http升级到websocket)。此后,所有的数据都按照websocket协议进行。如果返回其他状态码代表请求失败,必须要关闭连接(不要问为什么,规定)。具体状态码含义可以查看http相关资料。

Sec-WebSocket-Accept用于客户端验证服务器的合法性,如果验证数据不一致,客户端就要关闭连接。伪代码:base64编码(sha1加密( Sec-WebSocket-Key +"258EAFA5-E914-47DA-95CA-C5AB0DC85B11")  ),其中258EAFA5-E914-47DA-95CA-C5AB0DC85B11是固定的,代表websocket全局唯一标识符。 

至此,客户端与服务端已经完成协议升级。

数据帧

websocket中,数据是由一个个帧组成的,帧是组成一条数据的基本单位。

数据帧的大小是由数据帧类型决定的,而不是统一固定大小的数据单位。怎么理解,一个数据帧可以是一个bit大小,也可以N个bit大小。不同类型的帧大小可能是不一样的,但同一类型的帧大小是固定的。一条数据中包含多个帧,它们是由一个个不同类型的帧组成的,但不能含有两个或两个以上相同类型的帧。也就是说一个帧类型,在一条数据中只能出现一次。

每一种帧类型都代表一种特定含义,它们大小固定,顺序固定。

如下图,FIN就是一个数据帧类型,大小是1bit,位置是第1位。而opcode也是一种数据帧类型,它的大小却是4bit,位置是第5位。它们的大小和位置顺序是websocket定义好的,不能改变。

下面这幅图很好的说明了WebSocket 数据帧的格式,第一行代表数据大小,每一小格代表1bit单位。数据帧按照图中从左到右,从上到下的顺序排列。

websocket协议规范1

每一条websocket数据,都是按照上图所示的规则进行排列的。在本文中,我们把一条数据叫做消息。下面我们结合上图,详细的讲解各帧的含义。下文中未标注可以为空值的数据帧,都不能为空值。(如果讲解与websocket规范有冲突,请以规范为准)

FIN:1 bit

如果是0,表示这不是消息的最后一个分片。

如果是1,表示这是消息的最后一个分片。


分片我们可以理解为一条消息的一个部分,只要你愿意,可以随意将一条消息分为N个分片。比如“这是一条消息”是一条消息,则我们可以随意把它划分为 “这是”、“一”、“条”、“消息”,共4个分片。但是注意,websocket协议中没有分片顺序标识符,为了保证接收端能接收到正确的消息,分片要按严格按照前后顺序发送。

只有一个分片的消息不叫分片发送,很好理解,因为此时分片就是消息本身,一次即可发送完毕。只有包含两个或两个分片以上的消息才叫分片发送。

FIN用来判断消息是否接收完,如果是1,表示是一条消息的接收完毕。

RSV1, RSV2, RSV3:每个 1 bit

必须是0,除非一个扩展协商为非0值定义含义。如果收到一个非0值且没有协商的扩展定义这个非0值的含义,接收端就必须断开连接。

一般情况下,这三个帧全部定义为0就完事了。因为非0的情况下,对方会告诉你这些自定义值的含义,比如RSV1=1,代表对面很蛋疼的含义。当然如果你也要蛋疼自定义一些非0值,当我啥也没说。

Opcode: 4 bits

定义了“负载数据”的解释。如果收到一个未知的操作码,接收端点必须断开连接。

Opcode值含义(16进制)
含义
0继续帧,表示消息分片模式
1文本帧,表示文本格式传输
2二进制帧,表示二进制格式传输
3-7保留(目前还未定义的意思),用于定义未来的非控制帧
8关闭帧,表示关闭连接
9Ping帧,一般主动发送ping给对方,确认对方状态
APong帧,一般发送了ping给对方,对方就回复pong
B-F保留,用于定义未来的控制帧


负载数据,可以理解为我们要传送的文字、视频、图片等数据。比如上面说的“这是一条消息”就是负载数据,对于分片发送模式,负载数据就是当前的分片,比如“这”。

关闭帧:只要收到关闭帧,必须立即关闭连接。发送关闭帧时,可以在扩展数据中附带状态码和应用数据中添加自定义数据。状态码含义见文章最后列表。这里你只需记住收到关闭帧就必须断开连接,其他的概念不理解也不要紧,下面我们有详细介绍。

客户端和服务端建立连接之后,可能遇到,双方长时间没有数据往来,被消息中间件断开连接的情况,或一方发生其它意外连接关闭而没通知对方的情况。为保证通讯的正常进行,就需要不定时的确认对方状态。websocket为我们提供了ping pong 心跳机制来避免这种意外的发生。

  • 发送方 ->接收方:ping
  • 接收方 ->发送方:pong

着重说一下Opcode=0,继续帧,表示当前分片消息不是消息的第一个分片,是一个继续分片。也就是说一条消息有两个或两个以上分片,才能出现Opcode=0的情况。结合FIN帧,我们就推导出当前消息是否是是分片发送,第一个分片,中间分片,最后一个分片。公式如下:

一条消息发送:

FIN=1且Opcode<>0

分片消息发送:

  1. 第一个分片:      FIN=0      Opcode=1 或 Opcode=2
  2. 中间的分片:      FIN=0      Opcode=0
  3. 最后一个分片:   FIN=1      Opcode=0

Mask: 1 bit

如果0,不需掩码处理。(不掩码处理也就无需掩码键,所以消息中masking-key为空值,也就是不包含masking-key帧)

如果1,需要掩码处理。(掩码处理必须要有掩码键,所以masking-key帧不能为空值)

根据websocket定义:

客户端发送数据需要进行掩码处理,接收数据无需反掩码操作

服务端发送数据无需进行掩码处理,接收数据需要反掩码操作

掩码与反掩码采用一样的算法,伪代码为:

j =i mod 4  

返回数据(i)=原始数据 (i) xor masking-key(j)

i分别代表返回数据和原始数据的第i个字节(byte),j代表masking-key(掩码键)的第j个字节。

掩码键生成算法见下面Masking-key介绍

Payload length: 7 bits, 7 + 16 bits 或者 7 + 64 bits

“负载数据”的长度,单位是字节(byte).

如果 0-125,这是负载长度。

如果 126,之后的2个字节(16 位)表示负载数据长度。

如果 127,之后的8个字节(64位)表示负载数据长度。而且最高有效位必须是0。

要确定负载数据长度,首先先判断第一个字节的值,如果>0且<125,那么这个值就是负载数据的长度,为0时候,就代表负载数据长度为0,也就是不包含负载数据。如果=126,那么就取这个字节后面的两个字节作为负载数据的长度。如果=127,就取后面8个字节表示数据的长度。

需要注意多字节长度数量以网络字节顺序来表示,也就是大端模式(按字节顺序的从左向右读取数据)。以第一个字节等于126为例,根据上面介绍,我们知道要取后面两个字节作为负载数据长度,假设两个字节的值分别为A0,01(16进制)。那么负载数据长度就是A001

Masking-key: 0 or 32 bits

指掩码键,可以为空值。客户端发送到服务器的所有帧必须通过一个包含在帧中的 32 位值来掩码。

如果 mask 位设置为 1,则该字段存在。

如果 mask 位设置为 0,则该字段缺失。

详细信息见上面介绍的Mask帧介绍

Masking-key的生成算法也很简单,就是由4个随机字节组成。

Payload data: (x+y)  bytes

指负载数据,可以为空值

“负载数据”=“扩展数据”+“应用数据”。

以上是负载数据的权威解释,前面说负载数据可以理解为文字、图片、视频的解释不确切,这样解释只是为了让读者更好的进入状态,通过这里我们知道,它只是代表无扩展数据时候的负载数据。

Extension data: x bytes

指“扩展数据”,排在负载数据最前面。可以为空值

通常是0字节,除非已经协商了一个扩展。任何扩展必须指定“扩展数据” 的长度,或长度是如何计算的,以及扩展如何使用,必须在打开阶段握手期间协商。

如果存在,“扩展数据”包含在总负载数据长度中。

其实就是自定义一个协议,如果有扩展数据,扩展数据就加在应用数据前面,并且要协商好扩展数据长度如何计算。

Application data: y bytes

指“应用数据”,“扩展数据”之后帧的剩余部分,可以为空值。

“应用数据”  = “负载数据”  -  “扩展数据” 

状态码

当收到一个关闭帧的时候,可能附带关闭的状态码,含义如下表

状态码名称描述
0–999 保留段, 未使用.
1000CLOSE_NORMAL正常关闭; 无论为何目的而创建, 该链接都已成功完成任务.
1001CLOSE_GOING_AWAY终端离开, 可能因为服务端错误, 也可能因为浏览器正从打开连接的页面跳转离开.
1002CLOSE_PROTOCOL_ERROR由于协议错误而中断连接.
1003CLOSE_UNSUPPORTED由于接收到不允许的数据类型而断开连接 (如仅接收文本数据的终端接收到了二进制数据).
1004 保留. 其意义可能会在未来定义.
1005CLOSE_NO_STATUS保留.  表示没有收到预期的状态码.
1006CLOSE_ABNORMAL保留. 用于期望收到状态码时连接非正常关闭 (也就是说, 没有发送关闭帧).
1007Unsupported Data由于收到了格式不符的数据而断开连接 (如文本消息中包含了非 UTF-8 数据).
1008Policy Violation由于收到不符合约定的数据而断开连接. 这是一个通用状态码, 用于不适合使用 1003 和 1009 状态码的场景.
1009CLOSE_TOO_LARGE由于收到过大的数据帧而断开连接.
1010Missing Extension客户端期望服务器商定一个或多个拓展, 但服务器没有处理, 因此客户端断开连接.
1011Internal Error客户端由于遇到没有预料的情况阻止其完成请求, 因此服务端断开连接.
1012Service Restart服务器由于重启而断开连接.
1013Try Again Later服务器由于临时原因断开连接, 如服务器过载因此断开一部分客户端连接.
1014 由 WebSocket 标准保留以便未来使用.
1015TLS Handshake保留. 表示连接由于无法完成 TLS 握手而关闭 (例如无法验证服务器证书).
1016–1999 由 WebSocket 标准保留以便未来使用.
2000–2999 由 WebSocket 拓展保留使用.
3000–3999 可以由库或框架使用.不应由应用使用. 可以在 IANA 注册, 先到先得.
4000–4999 可以由应用使用.

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值