2. WebSocket协议基础:WebSocket握手过程、数据帧格式、与HTTP的对比
好,咱们进入第二讲。WebSocket,说白了就是让浏览器和服务器之间能「随时聊天」的协议。不像HTTP那样一问一答,WebSocket建立连接后,两边谁都可以主动发消息。我在做边缘设备项目时,最头疼的就是设备状态实时上报——用HTTP轮询?太浪费带宽了。后来换成WebSocket,整个世界清静了。
2.1 WebSocket握手过程
很多人以为WebSocket是全新的协议,其实它是在HTTP基础上「升级」过来的。握手阶段用的就是HTTP协议,只不过加了一些特殊的头信息。
我习惯把握手过程拆成三步:
- 客户端发起升级请求:发送一个HTTP GET请求,带上特殊的Header
- 服务器同意升级:返回101状态码,表示协议切换
- 连接建立成功:后续通信不再使用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=
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位,取决于数据大小
为什么客户端要掩码?说白了是为了防止缓存污染攻击。WebSocket设计者考虑得很周全,虽然增加了点计算开销,但安全性提升了。
2.3 与HTTP的对比
你想想看,HTTP和WebSocket到底有什么区别?我做个表格给你对比一下:
| 特性 | HTTP | WebSocket |
|---|---|---|
| 通信模式 | 请求-响应,客户端主动 | 全双工,双方都可主动 |
| 连接开销 | 每次请求都要建立TCP连接 | 一次握手,长期保持 |
| 头部大小 | 几百字节到几KB | 2-14字节(数据帧头部) |
| 实时性 | 差,需要轮询 | 好,服务器可主动推送 |
| 适用场景 | REST API、静态资源 | 实时数据、聊天、游戏 |
| 协议开销 | 高,每次请求都带完整Header | 低,建立连接后几乎无额外开销 |
我个人习惯这样选型:如果边缘设备需要频繁上报数据(比如每秒一次),用WebSocket;如果只是偶尔查询设备状态,用HTTP就够了。没必要为了用新技术而用新技术。
举个例子,我在做一个温湿度传感器项目时,设备每5秒上报一次数据。如果用HTTP轮询,每次请求光Header就几百字节,一天下来流量惊人。换成WebSocket后,数据帧头部只有几个字节,流量直接降到原来的十分之一。
还有一个容易忽略的点——HTTP/2其实也支持服务器推送,但和WebSocket的推送机制完全不同。HTTP/2的推送是「服务器猜测客户端可能需要什么」,而WebSocket是「客户端明确订阅了什么」。在边缘计算场景下,我建议用WebSocket,因为控制逻辑更清晰。
好了,这一讲就到这里。下一讲我们会深入WebSocket在边缘设备上的具体实现,包括如何用Node.js和Python搭建WebSocket服务端,以及如何在资源受限的设备上优化连接。到时候我会分享一些实际项目中的踩坑经验。