websocket协议报文头格式:
分片的好处之一:是多用于多路复用,一个逻辑通道上的一个大消息独占输出通道是不可取的,因此多路复用需要可以分割消息为更小的的分段(分片)来更好的共享输出通道。
FIN字段(1bit):指示这个websocket消息的最后片段(设置为1时),是第一个片段(没有分片时第一个片段也是最后一个片段)也可能是最后一个片段(分片时的最后一个片段)。
RSV1,RSV2,RSV3(每一位一bit):
必须是0,除非一个扩展协商为非零值定义含义(也就是两端协商好的一个值)。如果一端收到一个非零值且没有协商的扩展定义这个非零值的含义,接收端必须以报文处理失败告终,并发送close报文给对端,断开websocket连接。
opcode(4bit):此字段标识着这个websocket报文的报文数据类型,占4bit,常用数据含义如下:
0x0:代表一个继续帧
0x1:文本数据帧
0x2:二进制数据帧
0x3-0x7:保留用于未来的非控制帧
0x8:close帧代表链接关闭
0x9:代表ping报文
0xA:代表pong报文
0xB-F:保留用于未来的控制帧
控制帧类型说明:
ping/pang报文: 一个端点在收到对端发过来的ping报文时,必须在响应中发送一个ping帧,除非他早已接收到一个close帧。它应尽可能快的以pong帧作为响应。这也就是控制帧要求时效性比较高的体现。
一个端点可以在链接建立之后和链接关闭之前的任何时刻发送一个ping帧。
注:一个ping帧既可以充当一个keepalive,也可以作为验证远程端点仍可响应的手段。实际应用的时候,一般ping/pong报文用于刷中间链路net表项,防止net表项老化的一个手段。一般发送方发送ping报文之后不关心是否收到pong报文响应。只是定时发送而已。
close帧:
close报文用于关闭连接,可以包含内容体的应用数据部分,用于指示一个关闭的原因;如果有内容体,内容体的头两个字节必须是2个字节的无符号整数(网络序),代表断开链接的状态码,具体的状态码含义可以参照协议的讲解。内容体(playload数据部分)紧跟着2个字节状态码后面的数据如果有的话,包含的是UTF-8编码的reason值,---》字符串。reason消息,
也可以不包含内容体,即payload的长度为0,有种情况是:某一端点在关闭连接时发送给对端的close报文不带报文体内容。只有websocket头。
如果一个端点在接收到一个关闭帧且先前没有发送一个关闭帧,端点必须在响应中发送一个关闭帧。(当在响应中发送关闭帧时,端点通常回送它接收到的状态码填充到响应的close帧当中)。它应该根据实际情况尽快这样做。
在应用发送关闭帧之后,必须不发送任何更多的数据帧。多线程操作时,可能保证不了这一点,因为在关闭连接的的同时,其他线程可能继续发送数据,因为是并行的。端点可以延迟发送关闭帧,直到它当前消息发送完成。例如如果一个分片消息的大多数已经发送了,端点可以发送剩余的片段在发送一个关闭帧之前。但是,不保证一个已经发送关闭帧的端点将继续处理数据。
分片情况说明
websocket 报文没有分片时:
FIN:1 opcode:非0(真实数据的opcode)
非分片报文,FIN为1即是第一片也是最后一片。
注:控制帧可以被注入到一个分片消息的中间,控制帧本身不能被分片。所以一般只有非控制帧进行分片操作(0x1、0x2)
websocket 报文分片时:
1:首片:
FIN:0 opcode:非0(真实数据的opcode)
2:中间片:
FIN:0 opcode:0
2:中间片:
FIN:1 opcode:0
分片报文,FIN为1即分片的最后一片。
也就是说:分片报文的只有首片才会填写opcode,FIn字段为1标识着分片报文的最后一片,中间片FIn字段和opcode都为0.
几条原则:
1:控制帧可以被注入到一个分片消息的中间发送出去,发送给对端,控制帧本身必须不能被分割(即控制帧本身不能被分片)。
2:任意一个端点(链接的两端: 客户端和服务器端)必须能处理一个分片消息中间的控制帧。
如果控制帧不能被插入一组分片报文中间发送出去,或者任意的一个端点,不能处理分片消息中间的控制帧,那么控制帧只能按照顺序发送出去,不能插入分片消息中间,待发送的控制帧就必须等待前面带发送 的分片消息发送完成,才可以发送。此时如果遇到一组特别大的分片消息发送,那么后续的控制帧发送就会造成延时问题产生。控制帧的时效性比较高,比如close报文,要求立刻断开链接,但是消息延时,造成控制帧延时,就不合理。所以有以上两点需要注意一下。
3:消息分片要按发送者发送的顺序交付给收件人(不能乱序,要保序)
4:分片的一个消息必须不能与片段中的另一个消息交替,除非已经协商好了一个解释交替的扩展。
5:一个发送者可以为非控制消息创建任何大小的片段(发送方主动分片并自定义分片大小)
6:客户端和服务器必须支持结束分片消息和非分片消息。
websocket协议头中的Payload Len(7位)字段和Extended payload length(16/64 位):
Payload Len的值用于计算此次从对端接收的websocket报文的“负载数据”的长度,以字节为单位。
如果Payload Len字段的值解析出来为0-125,这就是真正的负载数据长度,就代表 后续的负载数据长度的值为0-125,并且表示此websocket协议报文头没有后续扩展的负载长度字段(Extended payload length)。
Extended payload length:当Payload Len的值为126或127时,代表此websocket协议报文头里面有Extended payload length,且后续真正的负载数据长度通过此扩展字段进行解析得出。
如果Payload Len字段的值解析出来为126, 那么代表存在的Extended payload length的字段占16位(2个字节--65535最大长度),并且通过解析这16位字段的值得出后续的负载数据长度,长度单位是字节。
如果Payload Len字段的值解析出来为127, 那么代表存在的Extended payload length的字段占64位(8个字节(64位),最高为必须是0即63位全1 得出最大长度的值为9223372036854775807),并且通过解析这64位字段的值得出后续的负载数据长度,长度单位是字节。
一般使用16位的扩展字段(2字节)进行代表负载数据的长度,也就是报文负载的最大长度为65535.
负载长度不包括掩码的长度。他是“负载数据”的长度,即跟在掩码键后边的字节数,掩码键下面进行叙述。
所以7位Payload Len二进制的最大值十进制表示:127(1111111)也就,使用完毕:0-125 、126 、127的各个含义介绍完毕。
Masking-key(if MASK set to 1, 如果websocket协议头的第八位MASK设置为1,才存在掩码数据位,否则不存在):如果掩码数据位存在,占且只能占4个字节,即0 or 4 字节:
它用于掩码后续的负载数据,掩码键是由客户端随机选择的32的值(websocket协议规范掩码键占用四个字节)。
掩码的作用: 一般是一串二进制代码对目标字段进行位与运算,屏蔽当前的输入位。具体的操作是:将源码与掩码经过按位运算或逻辑运算得出新的操作数,屏蔽原始的操作数,提供一种报文安全的传输机制。
当准备一个掩码帧时,客户端必须从允许的32值的集合当中选择一个新的掩码键。掩码键需要时不可预测的;因此掩码键必须来自一个强大的熵源。
掩码不影响“”负载数据“”的长度。变换掩码数据到解掩码数据,或反之亦然。相同的算法应用,不管转化的方向,例如相同的步骤即应用到掩码数据也应用到解掩码数据 。所以通信的一端发送的报文如果使用掩码键进行掩码处理,此时就必须将用于报文掩码的掩码键封装到报文之中带到对端,报文达到对端之后,需要使用最初的掩码键进行接掩码操作,这样才能得到原始报文数据。
只转码payload 数据,不转码websocket头数据。
以下算法被应用(websocket协议报文一般的掩码和解掩码操作算法),协议的规范,协议的一致性就成为了标准,这样双发就都可以按照协议的标准进行数据的自由转化。
1:将第i个字节的原始数据按字节转换为八位二进制数据(1个字节八位)和第(i和4取余)个掩码键的键值的八位二进制数据进行亦或运算,生成最好的掩码数据。
2:对端在解析掩码数据的时候顺序相反,拿着收到的第i个字节掩码数据八位二进制数据(1个字节八位)和第(i和4取余)个掩码键的键值的八位二进制数据进行亦或运算,倒推出原始的数据报文内容。
亦或的实质:
c = a ^ b;
c ^ b == a;
c ^ a == b;