2. WebSocket协议基础:WebSocket握手过程、数据帧格式、与HTTP的对比

好,咱们进入第二讲。WebSocket,说白了就是让浏览器和服务器之间能「随时聊天」的协议。不像HTTP那样一问一答,WebSocket建立连接后,两边谁都可以主动发消息。我在做边缘设备项目时,最头疼的就是设备状态实时上报——用HTTP轮询?太浪费带宽了。后来换成WebSocket,整个世界清静了。

2.1 WebSocket握手过程

很多人以为WebSocket是全新的协议,其实它是在HTTP基础上「升级」过来的。握手阶段用的就是HTTP协议,只不过加了一些特殊的头信息。

我习惯把握手过程拆成三步:

  1. 客户端发起升级请求:发送一个HTTP GET请求,带上特殊的Header
  2. 服务器同意升级:返回101状态码,表示协议切换
  3. 连接建立成功:后续通信不再使用HTTP,直接走WebSocket协议

来看一个典型的握手请求长什么样:

GET /ws/edge-device HTTP/1.1
Host: 192.168.1.100:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: http://localhost:3000

这里有个关键字段——Sec-WebSocket-Key。它是一个随机生成的Base64编码字符串。服务器收到后,会拼接一个固定GUID(258EAFA5-E914-47DA-95CA-C5AB0DC85B11),然后做SHA-1哈希,再Base64编码,返回给客户端。

服务器响应长这样:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
我的小技巧: 调试握手问题时,我经常用Chrome开发者工具的Network面板,过滤「WS」标签,能清楚看到握手请求和响应。如果看到101状态码,说明握手成功了。

2.2 数据帧格式

握手完成后,数据传输就走WebSocket自己的帧格式了。嗯,这里要注意——WebSocket的数据是以「帧」为单位的,不是流式的。每一帧都有固定的结构。

我直接给你看最核心的帧格式:

  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表示这是最后一帧,0表示还有后续帧。边缘设备上报大文件时,可能会分片发送
  • opcode:表示帧类型。0x1是文本帧,0x2是二进制帧,0x8是关闭帧,0x9是Ping,0xA是Pong
  • MASK位:客户端发给服务器的数据必须掩码(MASK=1),服务器发给客户端的不需要
  • Payload长度:7位、16位或64位,取决于数据大小
避坑指南: 我曾经在边缘设备上踩过一个坑——设备端发送数据时忘了设置MASK位,结果服务器直接断开连接。后来查了RFC 6455才发现,客户端到服务器的数据必须掩码。这个细节很容易被忽略。

为什么客户端要掩码?说白了是为了防止缓存污染攻击。WebSocket设计者考虑得很周全,虽然增加了点计算开销,但安全性提升了。

2.3 与HTTP的对比

你想想看,HTTP和WebSocket到底有什么区别?我做个表格给你对比一下:

特性 HTTP WebSocket
通信模式 请求-响应,客户端主动 全双工,双方都可主动
连接开销 每次请求都要建立TCP连接 一次握手,长期保持
头部大小 几百字节到几KB 2-14字节(数据帧头部)
实时性 差,需要轮询 好,服务器可主动推送
适用场景 REST API、静态资源 实时数据、聊天、游戏
协议开销 高,每次请求都带完整Header 低,建立连接后几乎无额外开销

我个人习惯这样选型:如果边缘设备需要频繁上报数据(比如每秒一次),用WebSocket;如果只是偶尔查询设备状态,用HTTP就够了。没必要为了用新技术而用新技术。

举个例子,我在做一个温湿度传感器项目时,设备每5秒上报一次数据。如果用HTTP轮询,每次请求光Header就几百字节,一天下来流量惊人。换成WebSocket后,数据帧头部只有几个字节,流量直接降到原来的十分之一。

注意: WebSocket虽然好,但不是万能的。它需要服务器端保持大量长连接,对内存和并发处理能力有要求。边缘设备资源有限,建议控制同时连接数,或者使用连接池。

还有一个容易忽略的点——HTTP/2其实也支持服务器推送,但和WebSocket的推送机制完全不同。HTTP/2的推送是「服务器猜测客户端可能需要什么」,而WebSocket是「客户端明确订阅了什么」。在边缘计算场景下,我建议用WebSocket,因为控制逻辑更清晰。

好了,这一讲就到这里。下一讲我们会深入WebSocket在边缘设备上的具体实现,包括如何用Node.js和Python搭建WebSocket服务端,以及如何在资源受限的设备上优化连接。到时候我会分享一些实际项目中的踩坑经验。