最新内容请跟踪我的Github:https://github.com/zhangkaitao/websocket-protocol
5.1概述
在WebSocket协议中,数据使用帧序列来传输。为避免混淆网络中间件(例如拦截代理)和出于安全原因,第10.3节进一步讨论,客户端必须掩码(mask)它发送到服务器的所有帧(更多详细信息请参见5.3节)。(注意不管WebSocket协议是否运行在TLS至上,掩码都要做。) 当收到一个没有掩码的帧时,服务器必须关闭连接。在这种情况下,服务器可能发送一个定义在7.4.1节的状态码1002(协议错误)的Close帧。服务器必须不掩码发送到客户端的所有帧。如果客户端检测到掩码的帧,它必须关闭连接。在这种情况下,它可能使用定义在7.4.1节的状态码1002(协议错误)。(这些规则可能在未来规范中放宽。)
基本帧协议定义了带有操作码(opcode)的帧类型、负载长度、和用于“扩展数据”与“应用数据”及它们一起定义的“负载数据”的指定位置。某些字节和操作吗保留用于未来协议的扩展。
一个数据帧可以被客户端或者服务器在打开阶段握手完成之后和端点发送Close帧之前的任何时候传输(5.5.1节)。
5.2基本帧协议
用于数据传输部分的报文格式是通过本节中详细描述的ABNF来描述。(注意,不像本文档的其他章节,本节中的ABNF是在位(bit)组上操作。每一个位组的长度在注释中指出。在编码报文时,最重要的位是在ABNF的最左边。)下图给出了帧的高层次概述。在下图和在本节后边指定的ABNF之间冲突的,这个图表是权威的。
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 ... |
+---------------------------------------------------------------+
FIN:1 bit
指示这个是消息的最后片段。第一个片段可能也是最后的片段。
RSV1, RSV2, RSV3: 每个1 bit
必须是0,除非一个扩展协商为非零值定义含义。如果收到一个非零值且没有协商的扩展定义这个非零值的含义,接收端点必须_失败WebSokcket连接_。
Opcode: 4 bits
定义了“负载数据”的解释。如果收到一个未知的操作码,接收端点必须_失败WebSocket连接_。定义了以下值。
- %x0 代表一个继续帧
- %x1 代表一个文本帧
- %x2 代表一个二进制帧
- %x3-7 保留用于未来的非控制帧
- %x8 代表连接关闭
- %x9 代表ping
- %xA 代表pong
- %xB-F 保留用于未来的控制帧
Mask: 1 bit
定义是否“负载数据”是掩码的。如果设置为1,一个掩码键出现在masking-key,且这个是用于根据5.3节解掩码(unmask)“负载数据”。从客户端发送到服务器的所有帧有这个位设置为1。
Payload length: 7 bits, 7+16 bits, 或者 7+64 bits
“负载数据”的长度,以字节为单位:如果0-125,这是负载长度。如果126,之后的两字节解释为一个16位的无符号整数是负载长度。如果127,之后的8字节解释为一个64位的无符号整数(最高有效位必须是0)是负载长度。多字节长度数量以网络字节顺序来表示。注意,在所有情况下,最小数量的字节必须用于编码长度,例如,一个124字节长的字符串的长度不能被编码为序列126,0,124。负载长度是“扩展数据”长度+“应用数据”长度。“扩展数据”长度可能是零,在这种情况下,负载长度是“应用数据”长度。
Masking-key: 0 or 4 bytes
客户端发送到服务器的所有帧通过一个包含在帧中的32位值来掩码。如果mask位设置为1,则该字段存在,如果mask位设置为0,则该字段缺失。详细信息请参见5.3节 客户端到服务器掩码。
Payload data: (x+y) bytes
“负载数据”定义为“扩展数据”连接“应用数据”。
Extension data: x bytes
“扩展数据”是0字节除非已经协商了一个扩展。任何扩展必须指定“扩展数据”的长度,或长度是如何计算的,以及扩展如何使用必须在打开阶段握手期间协商。 如果存在,“扩展数据”包含在总负载长度中。
Application data: y bytes
任意的“应用数据”,占用“扩展数据”之后帧的剩余部分。“应用数据”的长度等于负载长度减去“扩展数据”长度。
基本帧协议是由以下ABNF[RFC5234]正式定义。重要的是要注意这个数据是二进制表示的,而不是ASCII字符。因此,一个1位长度的字段取值为%x0 / %x1 是表示为单个位,其值为0或1,不是以ASCII编码代表字符“0”或“1”的完整的字节(8位位组)。4位长度的字段值介于%0-F之间,是通过4位表示的,不是通过ASCII字符或这些值的完整字节(8位位组)。[RFC5234]没有指定字符编码:“规则解析为最终值的字符串,有时候被称为字符。在ABNF中,一个字符仅仅是一个非负整数。在某些上下文中,一个值到一个字符集的特定映射(编码)将被指定。” 在这里,指定的编码是二进制编码,每一个最终值是编码到指定数量的比特中,每个字段是不同的。
ws-frame = frame-fin ; 1位长度
frame-rsv1 ; 1位长度
frame-rsv2 ; 1位长度
frame-rsv3 ; 1位长度
frame-opcode ; 4位长度
frame-masked ; 1位长度
frame-payload-length ; 或者 7、 7+16、
; 或者7+64 位长度
[ frame-masking-key ] ; 32位长度
frame-payload-data ; n*8位长度; n>=0
frame-fin = %x0 ; 这条消息后续还有更多的帧
/ %x1 ; 这条消息的最终帧
; 1位长度
frame-rsv1 = %x0 / %x1
; 1位长度,必须是0,除非协商其他
frame-rsv2 = %x0 / %x1
; 1位长度,必须是0,除非协商其他
frame-rsv3 = %x0 / %x1
; 1位长度,必须是0,除非协商其他
frame-opcode = frame-opcode-non-control /
frame-opcode-control /
frame-opcode-cont
frame-opcode-cont = %x0 ; 帧继续
frame-opcode-non-control= %x1 ; 文本帧
/ %x2 ; 二进制帧
/ %x3-7
; 4位长度,保留用于未来的非控制帧
frame-opcode-control = %x8 ; 连接关闭
/ %x9 ; ping
/ %xA ; pong
/ %xB-F ; 保留用于未来的控制帧
; 4位长度
frame-masked = %x0
; 帧没有掩码,没有frame-masking-key
/ %x1
; 帧被掩码,存在frame-masking-key
; 1位长度
frame-payload-length = ( %x00-7D )
/ ( %x7E frame-payload-length-16 )
/ ( %x7F frame-payload-length-63 )
; 分别7, 7+16, or 7+64位长度
frame-payload-length-16 = %x0000-FFFF ; 16位长度
frame-payload-length-63 = %x0000000000000000-7FFFFFFFFFFFFFFF
; 64位长度
frame-masking-key = 4( %x00-FF )
; 仅当frame-masked 是 1时存在
; 32位长度
frame-payload-data = (frame-masked-extension-data
frame-masked-application-data)
; 当frame-masked是1
/ (frame-unmasked-extension-data
frame-unmasked-application-data)
; 当frame-masked是0
frame-masked-extension-data = *( %x00-FF )
; 保留用于未来扩展
; n*8 位长度,n >= 0
frame-masked-application-data = *( %x00-FF )
; n*8 位长度,n >= 0
frame-unmasked-extension-data = *( %x00-FF )
; 保留用于未来扩展
; n*8 位长度,n >= 0
frame-unmasked-application-data = *( %x00-FF )
; n*8 位长度,n >= 0
5.3.客户端到服务器掩码
一个掩码的帧必须有5.2节定义的字段frame-masked设置为1。 掩码键完全包含在帧中,5.2节定义的frame-masking-key。它用于掩码定义在相同章节的frame-payload-data 中的“负载数据”,其包含“扩展数据”和“应用数据”。
掩码键是由客户端随机选择的32位值。当准备一个掩码的帧时,客户端必须从允许的32位值集合中选择一个新的掩码键。掩码键需要是不可预测的;因此,掩码键必须来自一个强大的熵源,且用于给定帧的掩码键必须不容易被服务器/代理预测用于后续帧的掩码键。掩码键的不可预测性对防止恶意应用的作者选择出现在报文上的字节是必要的。RFC 4086 [RFC4086]讨论了什么需要一个用于安全敏感应用的合适的熵源。
掩码不影响“负载数据”的长度。变换掩码数据到解掩码数据,或反之亦然,以下算法被应用。相同的算法应用,不管转化的方向,例如,相同的步骤即应用到掩码数据也应用到解掩码数据。
变换数据的八位位组i ("transformed-octet-i")是原始数据的八位位组i("original-octet-i")异或(XOR)i取模4位置的掩码键的八位位组("masking-key-octet-j"):
j = i MOD 4
transformed-octet-i = original-octet-i XOR masking-key-octet-j
负载长度,在帧中以frame-payload-length表示,不包括掩码键的长度。它是“负载数据”的长度,例如,跟在掩码键后边的字节数。
5.4.分片(Fragmentation)
分片的主要目的是允许当消息开始但不必缓冲该消息时发送一个未知大小的消息。如果消息不能被分片,那么端点将不得不缓冲整个消息以便在首字节发生之前统计出它的长度。对于分片,服务器或中间件可以选择一个合适大小的缓冲,当缓冲满时,写一个片段到网络。 第二个分片的用例是用于多路复用,一个逻辑通道上的一个大消息独占输出通道是不可取的,因此多路复用需要可以分割消息为更小的分段来更好的共享输出通道。(注意,多路复用扩展在本文档中没有描述)
除非另有扩展指定,帧没有语义含义。一个中间件可能合并且/或分割帧,如果客户端和服务器没有协商扩展;或如果已协商了一些扩展,但中间件理解所有协商的扩展且知道如何去合并且/或分割在这些扩展中存在的帧。这方面的一个含义是,在没有扩展情况下,发送者和接收者必须不依赖于特定帧边界的存在。
以下规则应用到分片:
- 一个没有分片的消息由单个带有FIN位设置(5.2节)和一个非0操作码的帧组成。
-
一个分片的消息由单个带有FIN位清零(5.2节)和一个非0操作码的帧组成,跟随零个或多个带有FIN位清零和操作码设置为0的帧,且终止于一个带有FIN位设置且0操作码的帧。一个分片的消息概念上是等价于单个大的消息,其负载是等价于按顺序串联片段的负载;然而,在存在扩展的情况下,这个可能不适用扩展定义的“扩展数据”存在的解释。例如,“扩展数据”可能仅在首个片段开始处存在且应用到随后的片段,或 “扩展数据”可以存在于仅用于到特定片段的每个片段。在没有“扩展数据”的情况下,以下例子展示了分片如何工作。 例子:对于一个作为三个片段发送的文本消息,第一个片段将有一个0x1操作码和一个FIN位清零,第二个片段将有一个0x0操作码和一个FIN位清零,且第三个片段将有0x0操作码和一个FIN位设置。
-
控制帧(参见5.5节)可能被注入到一个分片消息的中间。控制帧本身必须不被分割。
-
消息分片必须按发送者发送顺序交付给收件人。
-
片段中的一个消息必须不能与片段中的另一个消息交替,除非已协商了一个能解释交替的扩展。
-
一个端点必须能处理一个分片消息中间的控制帧。
-
一个发送者可以位非控制消息创建任何大小的片段。
-
客户端和服务器必须支持接收分片和非分片的消息。
-
由于控制帧不能被分片,一个中间件必须不尝试改变控制帧的分片。
-
如果使用了任何保留的位值且这些值的意思对中间件是未知的,一个中间件必须不改变一个消息的分片。
-
在一个连接上下文中,已经协商了扩展且中间件不知道协商的扩展的语义,一个中间件必须不改变任何消息的分片。同样,没有看见WebSocket握手(且没被通知有关它的内容)、导致一个WebSocket连接的一个中间件,必须不改变这个链接的任何消息的分片。
-
由于这些规则,一个消息的所有分片是相同类型,以第一个片段的操作码设置。因为控制帧不能被分片,用于一个消息中的所有分片的类型必须或者是文本、或者二进制、或者一个保留的操作码。
注意:如果控制帧不能被插入,一个ping延迟,例如,如果跟着一个大消息将是非常长的。因此,要求在分片消息的中间处理控制帧。
实现注意:在没有任何扩展时,一个接收者不必按顺序缓冲整个帧来处理它。例如,如果使用了一个流式API,一个帧的一部分能被交付到应用。但是,请注意这个假设可能不适用所有未来的WebSocket扩展。
5.5.控制帧
控制帧由操作码确定,其中操作码最重要的位是1。当前定义的用于控制帧的操作码包括0x8 (Close)、0x9(Ping)、和0xA(Pong)。 操作码0xB-0xF保留用于未来尚未定义的控制帧。
控制帧用于传达有关WebSocket的状态。控制帧可以插入到分片消息的中间。
所有控制帧必须有一个125字节的负载长度或更少, 必须不被分段。
5.5.1.Close
关闭(Close)帧包含0x8操作码。
关闭帧可以包含内容体(“帧的“应用数据”部分)指示一个关闭的原因,例如端点关闭了、端点收到的帧太大、或端点收到的帧不符合端点期望的格式。如果有内容体,内容体的头两个字节必须是2字节的无符号整数(按网络字节顺序)代表一个在7.4节的/code/值定义的状态码。跟着2字节的整数,内容体可以包含UTF-8编码的/reason/值,本规范没有定义它的解释。数据不必是人类可读的但可能对调试或传递打开连接的脚本相关的信息是有用的。由于数据不保证人类可读,客户端必须不把它显示给最终用户。
客户端发送到服务器的关闭帧必须根据5.3节被掩码。
在应用发送关闭帧之后,必须不发送任何更多的数据帧。
如果一个端点接收到一个关闭帧且先前没有发送一个关闭帧,端点必须在响应中发送一个关闭帧。(当在响应中发生关闭帧时,端点通常回送它接收到的状态码) 它应该根据实际情况尽快这样做。端点可以延迟发送关闭帧知道它当前消息发送了(例如,如果一个分片消息的大多数已经发送了,端点可以发送剩余的片段在发送一个关闭帧之前)。但是,不保证一个已经发送关闭帧的端点将继续处理数据。 发送并接收一个关闭消息后,一个端点认为WebSocket连接关闭了且必须关闭底层的TCP连接。服务器必须立即关闭底层TCP连接,客户端应该等待服务器关闭连接但可能在发送和接收一个关闭消息之后的任何时候关闭连接,例如,如果它没有在一个合理的时间周期内接收到服务器的TCP关闭。
如果客户端和服务器同时都发送了一个关闭消息,两个端点都将发送和接收一个关闭消息且应该认为WebSocket连接关闭了并关闭底层TCP连接。
5.5.2. Ping
Ping帧包含0x9操作码。
Ping帧可以包含“应用数据”。
当收到一个Ping帧时,一个端点必须在响应中发送一个Pong帧,除非它早已接收到一个关闭帧。它应该尽可能快地以Pong帧响应。Pong帧在5.5.3节讨论。
一个端点可以在连接建立之后并在连接关闭之前的任何时候发送一个Ping帧。 注意:一个Ping即可以充当一个keepalive,也可以作为验证远程端点仍可响应的手段。
5.5.3. Pong
Pong帧包含一个0xA操作码。
5.5.2节详细说明了应用Ping和Pong帧的要求。
一个Pong帧在响应中发送到一个Ping帧必须有在将回复的Ping帧的消息内容体中发现的相同的“应用数据”。
如果端点接收到一个Ping帧且尚未在响应中发送Pong帧到之前的Ping帧,端点可以选择仅为最近处理的Ping帧发送一个Pong帧。
一个Pong帧可以未经请求的发送。这个充当单向的心跳(heartbeat)。到未经请求的Pong帧的一个响应是不期望的。
5.6.数据帧
数据帧(例如,非控制帧)由操作码最高位是0的操作码标识。当前为数据帧定义的操作码包括0x1(文本)、0x2(二进制)。操作码0x3-0x7保留用于未来尚未定义的非控制帧。
数据帧携带应用层和/或扩展层数据。操作码决定了数据的解释:
Text
“负载数据”是编码为UTF-8的文本数据。注意,一个特定的文本帧可能包括部分UTF-8序列;不管怎么样,整个消息必须包含有效的UTF-8。重新组装的消息中的无效的UTF-8的处理描述在8.1节。
Binary
“负载数据”是随意的二进制数据,其解释仅仅是在应用层。
5.7.示例
-
未掩码文件消息的单个帧
0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f (包含 "Hello")
-
掩码的文本消息的单个帧
0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58 (包含 "Hello")
-
一个分片的未掩码的文本消息
0x01 0x03 0x48 0x65 0x6c (包含 "Hel")
0x80 0x02 0x6c 0x6f (包含 "lo")
-
未掩码的Ping请求和掩码的Ping响应
0x89 0x05 0x48 0x65 0x6c 0x6c 0x6f
(包含内容体"Hello"、但内容体的内容是随意的)
0x8a 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58
(包含内容体"Hello"、匹配ping的内容体)
-
单个未掩码帧中的256字节的二进制消息
0x82 0x7E 0x0100 [256字节的二进制数据]
-
单个未掩码帧中的64KB的二进制消息
0x82 0x7F 0x0000000000010000 [65536字节的二进制数据]
5.8.可扩展性
协议被设计为允许扩展,这将增加功能到基础协议。端点的一个连接必须在打开阶段握手期间协商使用的任何扩展。本规范提供了用于扩展的操作码0x3到0x7和0xB到0xF、“扩展数据”字段、和帧-rsv1、帧rsv2、和帧rsv3帧头位。9.1节进一步讨论了扩展协商。以下是一些预期使用的扩展。这个列表是不完整的也不规范的。
- “扩展数据”可以放置在“负载数据”中的“应用数据”之前。
- 保留的位可以分配给需要的每个帧。
- 保留的操作码值能被定义。
- 如果需要更多的操作码值,保留的位可以分配给操作码字段。
- 一个保留的位或一个“扩展”操作码可以定义以从“负载数据”中分配额外的位来定义更大的操作码或更多的每帧位。
相关推荐
考虑到WebSocket协议允许两种帧类型——文本帧和二进制帧,处理函数需要能够正确区分并处理这两种帧。 对于错误处理和异常安全,需要确保在出现错误时,例如网络中断或无效数据,能够适当地关闭连接并清理资源。...
Tomcat 7是第一个正式支持WebSocket的版本,因此,这两个jar包——catalina.jar和tomcat-coyote.jar,很可能是Tomcat服务器的核心组件,它们负责处理HTTP和HTTPS请求,以及WebSocket协议的解析和管理。 catalina....
在《WebSocket协议》章节中,深入探讨了WebSocket协议的工作原理,它是一个基于TCP的协议,使用了HTTP升级机制,使得客户端和服务器之间可以在同一连接上同时进行双向数据交换。了解协议细节对于开发高效和安全的...
1. **WebSocket协议**:了解WebSocket协议的基本原理,如握手过程、数据帧结构、关闭连接等。 2. **WebSocket API**:在Netty中,可以通过WebSocketServerProtocolHandler和WebSocketFrameDecoder/Encoder等组件来...
由于提供的文件名称列表只有一个——WebSocket_DIPSwitches-master,我们可以推测这可能是一个GitHub仓库的克隆或下载,其中包含了项目的主目录。通常,这样的目录会包含源代码、文档、测试文件等。WebSocket相关的...
首先,我们要了解WebSocket协议。WebSocket是一种在客户端和服务器之间建立长连接的协议,允许双向通信,从而实现真正的实时性。与传统的HTTP协议不同,WebSocket在建立连接后,双方可以随时发送数据,无需等待请求-...
RFC6455是互联网工程任务组(IETF)发布的WebSocket协议规范,定义了WebSocket握手过程、数据帧格式以及错误处理等核心机制。这个Go库实现了这一标准,使得开发者可以轻松地在Go应用程序中集成WebSocket功能,而无需...
3. **WebSocket协议客户端**:WebSocket是一种在单个TCP连接上进行全双工通信的协议,适用于实时性要求高的应用。Qt5.4及以上版本引入了QWebSocket类,允许开发者创建WebSocket客户端。通过建立连接,发送和接收二...
该压缩包文件“Android源码——PC机摄像头摄像数据在Android手机上同步显示的源.zip”包含了一个Android项目,该项目的目的是实现一个功能,即通过网络将PC机的摄像头摄像数据实时传输并显示在Android手机上。...
在实现WebSocket聊天室时,开发者需要理解WebSockets的协议规范,包括握手过程、帧结构以及错误处理。此外,还需要熟悉SpringBoot的WebSocket API,例如`@ServerEndpoint`注解用于标记WebSocket端点,以及如何使用`...
3. WebSocket协议:学习WebSocket的基本概念、握手过程和数据帧格式,以便正确地实现客户端与服务器的通信。 4. 数据库操作:Moleserver可能需要与数据库交互来存储和检索用户信息,了解SQL和NoSQL数据库的基本操作...
对于远程监控,可能需要实时传输视频流或图像,这可能涉及TCP/IP套接字编程或WebSocket协议,以便建立持久连接。 2. **摄像头访问**:Android系统的Camera API允许开发者访问和控制手机的摄像头。从API Level 21...
在这个压缩包中,我们包含了JMeter的安装包以及一个专门用于WebSocket协议测试的插件——JMeterWebSocketSampler-1.0.2-SNAPSHOT。 首先,让我们详细了解JMeter的基本知识。JMeter是Apache软件基金会开发的一款纯...
4. 解码与渲染:使用JavaScript实现的解码器对FLV数据进行解码,然后将解码后的视频帧和音频帧传递给HTML5的video元素进行渲染。 5. 实时播放:通过不断接收服务器推送的数据,实时解码并播放,同时处理播放控制、...
WebSocket协议则解决了传统HTTP协议的不足,提供了一种全双工的通信方式,允许客户端和服务器之间进行持久连接,实时交换数据。在坦克大战中,WebSocket被用来实现实时的游戏交互,玩家的移动、射击等操作可以即时...
在本压缩包“安卓Android源码——Android游戏源码——忍者快跑.zip”中,包含的是一个基于Android平台的游戏应用源代码,名为“忍者快跑”。这个游戏源码是学习和研究Android游戏开发的理想资源,它能帮助开发者深入...
这可能涉及到HTTP请求、WebSocket或其他网络协议。 9. **游戏性能优化**:优化内存使用、减少CPU占用和提高帧率是游戏开发的重要环节。源码分析可以揭示如何实现高效的内存管理,避免内存泄漏,以及如何优化渲染...
虽然WebSocket协议本身提供了基本的安全特性,但在实际部署中,还需要考虑防火墙配置、安全策略和服务器的负载均衡。gwsocket可能需要配合其他安全工具和实践来确保服务器的稳定性和安全性。 ### 10. 总结 ...
远程视频监控需要通过网络传输实时的音视频数据,可能采用TCP/IP协议进行稳定的数据传输,或者使用UDP协议以追求更高的实时性。此外,可能还会使用HTTP、RTSP(Real Time Streaming Protocol)或WebRTC(Web Real-...
在深入探讨CookIM之前,我们先来了解一下关键的两个概念——Akka和WebSocket。 Akka是一个用Scala语言编写的,基于actor模型的并发框架,它为Java和Scala应用提供了高度容错和高性能的平台。Akka的actor系统使得...