5.3 客户端到服务端掩码一个被掩码的帧需要将掩码标识位(第 5.2 节定义)设置为 1。
掩码钥匙 masking key 整个都在帧中,就像第 5.2 节定义的。它用于对 “有效负荷数据” 进行掩码操作,包括 “扩展数据” 和 “应用数据”。
掩码钥匙由客户端随机选取一个 32 位的值。在每次准备对帧进行掩码操作时,客户端必须选择在可选的 32 位数值集合中选取一个新的掩码钥匙。掩码钥匙的值需要是不可被预测的;因此,掩码钥匙必须来源于一个具有很强保密性质的生成器,并且 服务器/代理 不能够轻易的预测到一连串的帧中使用的掩码钥匙。不可预测的掩码钥匙可以防止恶意程序在帧的传输过程中探测到掩码钥匙的内容。 具体讨论了为什么对于一个安全性比较敏感的应用程序需要使用一个很强保密性质的生成器。
掩码不会影响 “有效负载数据” 的长度。为了将掩码后的数据进行反掩码,或者倒过来,可以使用下面的算法。同样的算法适用于不同方向发来的帧,比如,对于掩码和反掩码使用相同的步骤。
传输数据中的每 8 个数位的字节 i (transformed-octet-i),生成方式是通过原数据中的每 8 个数位的字节 i (original-octet-i)与以 i 与 4 取模后的数位为索引的掩码钥匙中的 8 为字节 j(masking-key-octet-j) 进行异或(XOR)操作:
j = i MOD 4transformed-octet-i = original-octet-i XOR masking-key-octet-j负载的长度不包括掩码钥匙的长度,它是 “有效负载数据 Payload data” 的长度,比如,位于掩码钥匙后的字节的长度。
5.4 消息碎片化消息碎片化的目的就是允许发送那些在发送时不知道其缓冲的长度的消息。如果消息不能被碎片化,那么一端就必须将消息整个地载入内存缓冲,这样在发送消息前才可以计算出消息的字节长度。有了碎片化的机制,服务端或者中间件就可以选取其适用的内存缓冲长度,然后当缓冲满了之后就发送一个消息碎片。
碎片机制带来的另一个好处就是可以方便实现多路复用。没有多路复用的话,就需要将一整个大的消息放在一个逻辑通道中发送,这样会占用整个输出通道。多路复用需要可以将消息分割成小的碎片,使这些小的碎片可以共享输出通道。(注意多路复用的扩展在这片文档中并没有进行描述)
除非运用了特定的扩展,否则帧是没有特定的语义的。在客户端和服务端协商了某个扩展,或者客户端和服务端没有协商扩展的情况下,中间件都有可能将帧进行 合并/分隔。也就是说,在客户端和服务端没有协商某个扩展时,双方都不应该猜测帧与帧之间的边界。注:这里的某个扩展的意思就是赋予了帧特定的语义的扩展,比如多路复用扩展。
下面的规则解释了如何进行碎片化:
- 一个没有被碎片化的消息只包含一个帧,并且帧的 FIN 数位被设置为 1,且操作码 opcode 不为 0。
- 一个碎片化的消息包含了一个 FIN 未被置为 0 的帧,且这个帧的 opcode 不为 0,在这个帧之后,将有 0 个或者多个 FIN 为 0 且 opcode 为 0 的帧,最后以一个 FIN 为 1 和 opcode 为 0 的帧结束。对于一个碎片化后的消息,它的有效负荷就等于将碎片化后的帧的有效负荷按顺序连接起来;不过当存在扩展时,这一点就不一定正确了,因为扩展可能会设置帧的 “扩展数据”。在没有 “扩展数据” 的情况下,下面的例子演示了碎片化是如何工作的。
例子:对于一个以三个帧发送的文本消息,其第一个帧的 opcode 是 0x1 并且 FIN 位是 0,第二个帧的 opcode 是 0x0 且 FIN 位是 0,第三个帧的 opcode 是 0x0 且 FIN 位是 1。
- 控制帧(见第 5.5 节),可能会夹杂在消息帧之间。控制帧是不能被碎片化的。
- 消息帧必须以其被发送时的顺序传递到接收端。
- 不同消息的消息帧之间不可以相互夹杂,除非协商了一个定义了如何解释这种夹杂行为的扩展。
- 发送端可以创建任意大小的非控制帧。
- 客户端和服务端必须支持发送和接受碎片化或者非碎片化的消息。
- 一个控制帧是不可以被碎片化的,中间件必须不可以试图将控制帧进行碎片化。
- 如果帧中使用了 RSV 数位,但是中间件不理解其中的任意的 RSV 数位 的值时,它必须不可以改变消息的原有的碎片化帧。
- 在中间件不能确定客户端和服务端进行了哪些扩展协商的情况下,中间件必须不可以修改原有的碎片化帧。
- 最后,组成消息的所有帧都是相同的数据类型,在第一个帧中的 opcode 中指明。因为控制帧不能被碎片化,组成消息的碎片类型必须是文本、二进制、或者其他的保留类型。
注意:如果控制帧不能夹杂在消息帧的话,那么将导致 ping 的结果产生延迟,比如在处理了一个非常长的消息后才响应 ping 控制帧时。因此,要求在处理消息帧的期间可以响应控制帧。
重点注意:在没有扩展的情况下,接收端为了处理消息不是非得缓冲所有的帧。比如如果使用了 流API (streaming API),数据帧可以直接传递给应用层。不过这样假设并不一定在所有的扩展中都适用。 |