esp32: websocket
介绍
WebSocket 是什么?
WebSocket是一种网络传输协议,可在单个TCP连接上进行全双工通信,位于OSI模型的应用层。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以建立持久性的连接,并进行双向数据传输。
WebSocket是一种与HTTP不同的协议,WebSocket提供全双工通信。两者都位于OSI模型的应用层,并且都依赖于传输层的TCP协议。 虽然它们不同,但是RFC 6455中规定:it is designed to work over HTTP ports 80 and 443 as well as to support HTTP proxies and intermediaries
(WebSocket通过HTTP端口80和443进行工作,并支持HTTP代理和中介),从而使其与HTTP协议兼容。 为了实现兼容性,WebSocket握手使用HTTP Upgrade头[1]从HTTP协议更改为WebSocket协议。
WebSocket协议支持Web浏览器(或其他客户端应用程序)与Web服务器之间的交互,具有较低的开销,便于实现客户端与服务器的实时数据传输。大多数浏览器都支持该协议,包括Google Chrome、Firefox、Safari、Microsoft Edge、Internet Explorer和Opera。
WebSocket协议规范将ws(WebSocket)和wss(WebSocket Secure)定义为两个新的统一资源标识符(URI)方案[4],分别对应明文和加密连接。
WebSocket数据帧
WebSocket是通过发送HTTP 请求,然后升级到WebSocket。然后就可以根据WebSocket定义的数据帧进行数据传输。
请求帧
通过HTTP请求升级为WebSocket的数据格式:客户端请求和服务器响应
客户端请求:
1 | GET /chat HTTP/1.1 |
服务器响应:
1 | HTTP/1.1 101 Switching Protocols |
数据帧
1 | 0 1 2 3 |
字段解释:
名词 | 说明 | 大小 |
---|---|---|
FIN | 如果是 1,表示这是消息(message)的最后一个分片(fragment);如果是 0,表示不是是消息(message)的最后一个分片(fragment) | 1 个比特 |
RSV1, RSV2, RSV3 | 一般情况下全为 0。当客户端、服务端协商采用 WebSocket 扩展时,这三个标志位可以非 0,且值的含义由扩展进行定义。如果出现非零的值,且并没有采用 WebSocket 扩展,连接出错 | 各占 1 个比特 |
opcode | 操作代码,Opcode 的值决定了应该如何解析后续的数据载荷(data payload)。如果操作代码是不认识的,那么接收端应该断开连接(fail the connection) | 4 个比特 |
mask | 表示是否要对数据载荷进行掩码操作。从客户端向服务端发送数据时,需要对数据进行掩码操作;从服务端向客户端发送数据时,不需要对数据进行掩码操作。 如果服务端接收到的数据没有进行过掩码操作,服务端需要断开连接。 如果 Mask 是 1,那么在 Masking-key 中会定义一个掩码键(masking key),并用这个掩码键来对数据载荷进行反掩码。所有客户端发送到服务端的数据帧,Mask 都是 1。 | 1 个比特 |
Payload length | 数据载荷的长度,单位是字节。假设数 Payload length === x,如果: x 为 0~126:数据的长度为 x 字节。 x 为 126:后续 2 个字节代表一个 16 位的无符号整数,该无符号整数的值为数据的长度。 x 为 127:后续 8 个字节代表一个 64 位的无符号整数(最高位为 0),该无符号整数的值为数据的长度。 此外,如果 payload length 占用了多个字节的话,payload length 的二进制表达采用网络序(big endian,重要的位在前)。 | 为 7 位,或 7+16 位,或 1+64 位。 |
Masking-key | 所有从客户端传送到服务端的数据帧,数据载荷都进行了掩码操作,Mask 为 1,且携带了 4 字节 的 Masking-key。如果 Mask 为 0,则没有 Masking-key。 备注:载荷数据的长度,不包括 mask key 的长度。 | 0 或 4 字节(32 位 |
Payload data | 载荷数据:包括了扩展数据、应用数据。其中,扩展数据 x 字节,应用数据 y 字节。The “Payload data” is defined as “Extension data” concatenated with “Application data”. 扩展数据:如果没有协商使用扩展的话,扩展数据数据为 0 字节。所有的扩展都必须声明扩展数据的长度,或者可以如何计算出扩展数据的长度。此外,扩展如何使用必须在握手阶段就协商好。如果扩展数据存在,那么载荷数据长度必须将扩展数据的长度包含在内。 应用数据:任意的应用数据,在扩展数据之后(如果存在扩展数据),占据了数据帧剩余的位置。载荷数据长度 减去 扩展数据长度,就得到应用数据的长度。 | (x+y) 字节 |
opcode 操作码定义:
- 0x0:表示一个延续帧(continuation frame)。当 Opcode 为 0 时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片。
- 0x1:表示这是一个文本帧(frame),text frame
- 0x2:表示这是一个二进制帧(frame),binary frame
- 0x3~0x7:保留的操作代码,用于后续定义的非控制帧。
- 0x8:表示连接断开。connection close
- 0x9:表示这是一个 ping 操作。a ping
- 0xA:表示这是一个 pong 操作。a pong
- 0xB~0xF:保留的操作代码,用于后续定义的控制帧。
抓包分析
使用 Python 编译一个 WebSocket Client工具,用于连接 WebSocket Server。
websocket连接
捕获 WebSocket 连接数据包:
HTTP websocket连接请求数据:
HTTP websocket连接响应数据:
websocket数据传输
捕获 WebSocket 客户端发送数据给服务器数据包:
客户端发送数据到服务器:
服务器发送数据到客户端:
websocket 断开
websocket客户端断开与服务器的连接:
首先,看到 WebSocket 断开数据包,然后接着就是TCP四次挥手断开TCP连接。
客户端断开请求:
服务器断开应答: