WebSocket 深入介绍
本文从协议原理、握手、帧结构、连接生命周期、安全、工程实践到嵌入式落地,系统介绍 WebSocket(RFC 6455)。
目录
- 概述
- 历史与由来
- 与 HTTP / 轮询 / SSE / gRPC 的对比
- 协议栈与 URL
- 握手过程(Opening Handshake)
- 数据帧格式详解
- 掩码(Masking)机制
- 控制帧:Ping / Pong / Close
- 分片与消息边界
- 连接生命周期与状态机
- 扩展与子协议
- 客户端实现示例
- 服务端实现与常见框架
- 反向代理与负载均衡
- 安全实践
- 性能与工程实践
- 调试与抓包
- 嵌入式 / 物联网场景实践
- 常见问题 FAQ
- 参考资料
1. 概述
WebSocket 是一种在单个 TCP 连接上实现全双工(full-duplex)通信的应用层协议,由 HTML5 规范引入,后由 IETF 标准化为 RFC 6455(2011 年)。
核心特征:
- 全双工:连接建立后,客户端与服务器可随时、独立地互相发送数据。
- 长连接:一次握手后连接保持,不随每次消息重建。
- 低开销:帧头最小仅 2 字节,远小于 HTTP 每请求的完整头部。
- 基于 HTTP 握手:借助
Upgrade机制完成协议升级,天然穿透大多数防火墙与代理。 - 双向对等:客户端和服务器在数据层面地位对等,均可主动推送。
- 消息导向:协议本身保留消息边界(对比 TCP 是字节流),无需应用层处理"粘包/半包"。
URL 形式:
ws://host:port/path # 明文(默认端口 80)
wss://host:port/path # 基于 TLS(默认端口 443)
2. 历史与由来
HTTP 是"客户端发起、服务器响应"的单向模型,浏览器无法被服务器主动推送。在 WebSocket 出现之前,业界用各种方式模拟实时通信:
- 短轮询(Polling):客户端定时发请求,大量无效请求、延迟高。
- 长轮询(Comet / Long Polling):服务器 hold 住请求直到有数据,连接频繁重建、并发开销大。
- XHR Streaming / multipart streaming:利用不结束的 HTTP 响应流式下发,兼容性差、代理易截断。
- Flash Socket / Silverlight:依赖插件,已淘汰。
2008 年,Michael Carter 等人开始推动 TCP 级别的浏览器双向通信;2009 年协议草案提出;2011 年 RFC 6455 正式发布,浏览器统一支持 new WebSocket()。此后成为实时 Web 的事实标准,并被广泛用于 IM、推送、协同、游戏、IoT 等领域。
3. 与 HTTP / 轮询 / SSE / gRPC 的对比
| 方案 | 通信方向 | 连接 | 头部开销 | 延迟 | 复杂度 | 典型场景 |
|---|---|---|---|---|---|---|
| HTTP 短连接 | 请求—响应 | 短 | 高 | 高 | 低 | 普通资源请求 |
| HTTP Keep-Alive | 请求—响应 | 复用长连接 | 高 | 中 | 低 | REST API |
| 短轮询 | 客户端拉 | 短 | 高 | 高 | 低 | 简单状态刷新 |
| 长轮询 | 服务器推(伪) | 反复重建 | 中 | 中 | 中 | 兼容性要求高的推送 |
| SSE | 服务器→客户端单向 | 长 | 低 | 低 | 低 | 日志流、行情、通知 |
| WebSocket | 双向 | 长 | 极低 | 极低 | 中 | 实时双向交互 |
| gRPC (HTTP/2) | 双向(streaming) | 长 | 中(HPACK) | 低 | 高 | 微服务、强类型 RPC |
| MQTT | 双向(经 broker) | 长 | 极低 | 低 | 中 | IoT 海量设备 |
选型建议:
- 只需服务器→客户端单向推送:SSE 往往更简单。
- 需要真正的双向、低延迟、细粒度消息:WebSocket。
- 内部服务强类型 RPC、多路复用:gRPC。
- 海量设备、弱网、QoS 分级:MQTT。
4. 协议栈与 URL
┌─────────────────────────────┐
│ 应用数据 │
├─────────────────────────────┤
│ WebSocket(RFC 6455) │ ← 帧、握手、控制帧
├─────────────────────────────┤
│ TLS(可选,wss://) │
├─────────────────────────────┤
│ TCP │ ← 字节流、可靠、有序
├─────────────────────────────┤
│ IP │
└─────────────────────────────┘
- WebSocket 位于 TCP 之上,不依赖 HTTP 传输数据,仅借用 HTTP 完成握手。
- 握手成功后,连接是纯 TCP 字节流,双方按帧格式解析。
- HTTP/2 场景可通过 RFC 8441(Extended CONNECT)承载 WebSocket。
5. 握手过程(Opening Handshake)
WebSocket 复用 HTTP 协议完成"协议升级",因此能穿透大部分代理和防火墙。
5.1 客户端请求
GET /chat?room=1 HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Sec-WebSocket-Protocol: chat, mqtt
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Origin: https://example.com
Cookie: session=abc
关键字段:
| 头 | 含义 |
|---|---|
Upgrade: websocket | 请求升级为 WebSocket |
Connection: Upgrade | 通知中间件保持连接、勿缓存 |
Sec-WebSocket-Key | 客户端随机 16 字节的 Base64 编码 |
Sec-WebSocket-Version: 13 | 协议版本,当前唯一有效值 |
Sec-WebSocket-Protocol | 子协议列表(可选,应用层自定义) |
Sec-WebSocket-Extensions | 扩展协商,如压缩(可选) |
Origin | 来源,用于服务端 CSWSH 防护 |
Sec-WebSocket-Key 生成示例:
import os, base64, hashlib
key = base64.b64encode(os.urandom(16)).decode()
accept = base64.b64encode(hashlib.sha1(
(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").encode()
).digest()).decode()
5.2 服务器响应
成功升级:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Accept 计算:
Accept = Base64( SHA1( Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ) )
这个 GUID 是 RFC 6455 固定的"魔术字符串",用于防止普通的 HTTP 客户端被误升级。
失败时服务器返回普通 HTTP 错误,例如:
HTTP/1.1 401 Unauthorized
HTTP/1.1 403 Forbidden
HTTP/1.1 426 Upgrade Required
5.3 握手要点
- 握手是一次性的,之后不再有 HTTP 语义。
Sec-WebSocket-Protocol用于协商应用层子协议(如chat、mqtt),服务端从中选一个返回。- 无法在浏览器 WebSocket API 中自定义任意请求头(如
Authorization),常用替代:URL 查询参数、子协议、Cookie、或在连接后发送首帧鉴权。
6. 数据帧格式详解
帧是 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,末帧为 1 |
RSV1~3 | 3 | 保留位;未协商扩展时必须为 0 |
opcode | 4 | 帧类型 |
MASK | 1 | 负载是否加掩码;客户端→服务器必须为 1 |
Payload len | 7 | 负载长度;0~125 直接表示,126/127 表示后跟扩展长度 |
Extended payload length | 16 / 64 | 16 位最大 65535;64 位最高位必须为 0 |
Masking-key | 32 | 4 字节掩码,仅当 MASK=1 |
Payload Data | 变长 | 实际数据,若加掩码则已异或 |
opcode 取值:
| 值 | 类型 | 说明 |
|---|---|---|
0x0 | Continuation | 分片续帧 |
0x1 | Text | UTF-8 文本(必须是合法 UTF-8) |
0x2 | Binary | 二进制数据 |
0x3~0x7 | 保留(非控制) | 未定义 |
0x8 | Close | 关闭连接 |
0x9 | Ping | 心跳探测 |
0xA | Pong | 心跳应答 |
0xB~0xF | 保留(控制) | 未定义 |
长度编码规则:
0 ~ 125:负载长度直接写在 7 位字段。126:后跟 2 字节无符号整数(网络字节序)表示长度。127:后跟 8 字节无符号整数,最高位必须为 0。
控制帧(0x8~0xF)限制:
- 负载长度不得超过 125 字节。
- 不得分片(
FIN必须为 1)。
7. 掩码(Masking)机制
RFC 6455 规定:
- 客户端 → 服务器:所有帧必须加掩码(
MASK=1)。 - 服务器 → 客户端:必须不加掩码(
MASK=0)。 - 若违反,接收方必须关闭连接(
1002)。
掩码算法:对负载第 i 字节,与 masking-key[i % 4] 异或:
for (i = 0; i < payload_len; i++) {
payload[i] ^= masking_key[i % 4];
}
示例(4 字节负载 0x12 0x34 0x56 0x78,掩码 0xDE 0xAD 0xBE 0xEF):
0x12 ^ 0xDE = 0xCC
0x34 ^ 0xAD = 0x99
0x56 ^ 0xBE = 0xE8
0x78 ^ 0xEF = 0x97
→ 发送 0xCC 0x99 0xE8 0x97
掩码的目的不是加密,而是防止中间代理缓存污染攻击(早期代理可能把 WebSocket 帧误当 HTTP 缓存),同时让攻击者无法预测字节流以此构造可被缓存解析的内容。掩码是对称的,解掩码用同样操作。
8. 控制帧:Ping / Pong / Close
8.1 Ping / Pong
- 任意一方可发送
Ping,负载可选(≤125 字节)。 - 收到
Ping的一方必须尽快回一个Pong,负载与 Ping 相同。 - 用途:
- 保活:定时 Ping,检测对端存活。
- 探测半开连接:客户端断电、NAT 超时后,TCP 可能长时间不报错,Ping 可及时发现问题。
- 延迟测量:Ping 发出到 Pong 返回的时间约等于 RTT。
- 协议层 Ping/Pong 对应用层通常透明(库自动处理),但长连接仍建议应用层心跳以覆盖应用级假死。
8.2 Close
关闭是一次"双向握手":
- 主动方发送
Close帧(含可选关闭码 + UTF-8 原因)。 - 被动方收到后回一个
Close帧。 - 双方各自关闭底层 TCP。
常用关闭码:
| 码 | 含义 |
|---|---|
1000 | 正常关闭 |
1001 | 端点离开(如页面关闭/服务重启) |
1002 | 协议错误 |
1003 | 收到不可接受的数据类型 |
1007 | 文本非合法 UTF-8 |
1008 | 违反策略 |
1009 | 消息过大 |
1010 | 客户端期望扩展未协商 |
1011 | 服务器内部错误 |
3000~4999 | 库/应用自定义(如 4001 鉴权失败) |
状态码约定:
1000~2999:RFC 保留。3000~3999:IANA 注册,需公开。4000~4999:私有应用使用。
关闭细节:
- 已发送
Close后,不应再发送数据帧,但仍需处理接收到的帧直到对端关闭。 - 关闭时若仍有未发送完的数据,应先发完再
Close("优雅关闭")。
9. 分片与消息边界
一条"消息"可由一个或多个帧组成:
Text FIN=0 opcode=0x1 "Hel" ← 首帧
Cont FIN=0 opcode=0x0 "lo, " ← 续帧
Cont FIN=1 opcode=0x0 "world" ← 末帧
规则:
- 首帧携带真实
opcode(0x1/0x2),FIN=0。 - 后续帧
opcode=0x0(Continuation)。 - 最后一帧
FIN=1。 - 控制帧可以插在两个分片之间,接收方需妥善处理。
- 分片是可选优化:对于大消息可先发送前几字节再流式发送剩余(如流式 JSON、大文件)。
工程建议:
- 接收方必须支持重组分片;对超长消息设置上限(防 DoS)。
- 发送方除流式场景外通常不需要分片。
10. 连接生命周期与状态机
┌──────────┐ TCP 连接 + HTTP Upgrade ┌──────────┐
│ CONNECTING├──────────────────────────►│ OPEN │
└────┬─────┘ └────┬─────┘
│ 失败 │ 任一方发 Close / 出错 / 超时
▼ ▼
┌──────────┐ ┌──────────┐
│ CLOSED │◄───────────────────────────│ CLOSING │
└──────────┘ └──────────┘
浏览器 WebSocket.readyState 对应:
| 常量 | 值 | 状态 |
|---|---|---|
CONNECTING | 0 | 连接中(握手未完成) |
OPEN | 1 | 已连接,可收发 |
CLOSING | 2 | 关闭中(已发/收到 Close) |
CLOSED | 3 | 已关闭 |
连接建立后,需要关注:
- 保活:定时 Ping / 应用心跳,避免中间设备(NAT、LB)回收空闲连接。
- 超时:读写超时、握手超时(通常 5~10s)。
- 重连:断线后指数退避重连,并做会话恢复。
11. 扩展与子协议
11.1 子协议(Subprotocol)
应用层协议协商,服务于业务语义(如 chat、mqtt、graphql-ws)。
- 客户端在
Sec-WebSocket-Protocol列出支持项。 - 服务端从中选择一个放入响应;未选中则不带该头。
- 协商结果对双方可见,便于同一端点承载不同协议。
11.2 扩展(Extensions)
协议级能力协商,如:permessage-deflate、x-webkit-deflate-frame。
permessage-deflate(RFC 7692)对每条消息用 DEFLATE 压缩:
- 文本/JSON 压缩率通常 60%~90%,显著省带宽。
- 代价:CPU 占用、上下文内存、轻微延迟;小消息可能"越压越大"。
协商示例:
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits=15
服务端可接受、拒绝或调整参数。
12. 客户端实现示例
12.1 浏览器 / Node.js
// 二进制用 ArrayBuffer;文本为字符串
const ws = new WebSocket('wss://example.com/chat', ['chat']);
ws.binaryType = 'arraybuffer';
ws.onopen = () => {
console.log('connected');
ws.send(JSON.stringify({ type: 'hello', ts: Date.now() }));
ws.send(new Uint8Array([0x01, 0x02, 0x03])); // 二进制
};
ws.onmessage = (ev) => {
if (typeof ev.data === 'string') {
console.log('text:', ev.data);
} else {
console.log('binary:', new Uint8Array(ev.data));
}
};
ws.onerror = (e) => console.error('ws error', e);
ws.onclose = (e) => {
console.log(`closed code=${e.code} reason=${e.reason} clean=${e.wasClean}`);
// 触发重连(见第 16 节)
};
注意:浏览器 API 无法自定义握手请求头;需要 Authorization 时可用 Cookie、URL 参数或子协议携带。
12.2 Python(asyncio + websockets)
import asyncio
import websockets
async def main():
async with websockets.connect(
"wss://example.com/chat",
subprotocols=["chat"],
ping_interval=20, # 自动 Ping 保活
ping_timeout=20,
) as ws:
await ws.send("hello")
async for msg in ws:
print("recv:", msg)
asyncio.run(main())
12.3 C(简化的帧发送/接收骨架)
#include <stdint.h>
#include <string.h>
/* 发送一个未分片的 Binary/Text 帧;客户端必须加掩码 */
int ws_send_frame(int fd, uint8_t opcode, const uint8_t *data, size_t len)
{
uint8_t header[14];
size_t hlen = 0;
uint8_t mask[4] = { 0x11, 0x22, 0x33, 0x44 };
header[0] = 0x80 | (opcode & 0x0F); /* FIN=1 */
if (len <= 125) {
header[1] = 0x80 | (uint8_t)len;
hlen = 2;
} else if (len <= 65535) {
header[1] = 0x80 | 126;
header[2] = (len >> 8) & 0xFF;
header[3] = len & 0xFF;
hlen = 4;
} else {
header[1] = 0x80 | 127;
for (int i = 0; i < 8; i++) {
header[2 + i] = (len >> (56 - 8 * i)) & 0xFF;
}
hlen = 10;
}
memcpy(&header[hlen], mask, 4);
hlen += 4;
/* 发送 header,再逐字节异或后发送 payload */
/* ... write(fd, header, hlen); ... */
return 0;
}
真实项目中建议直接使用成熟库(libwebsockets、mongoose、WebSocket++、uWebSockets),除非有极致资源约束。
13. 服务端实现与常见框架
| 语言 | 框架/库 |
|---|---|
| Node.js | ws、socket.io(带降级与房间)、uWebSockets.js |
| Python | websockets、aiohttp、FastAPI(WebSocket)、Tornado |
| Go | gorilla/websocket、nhooyr.io/websocket |
| Java | Netty、Spring WebSocket、Jakarta WebSocket |
| C/C++ | libwebsockets、mongoose、WebSocket++、Boost.Beast |
| Rust | tokio-tungstenite、axum(ws) |
| Erlang/Elixir | Cowboy、Phoenix Channels |
服务端要点:
- 每个连接占一个 socket 与一定内存;高并发需事件驱动(epoll/kqueue/IOCP)。
- 维护连接表(用户→连接),支持广播、房间、定向推送。
- 心跳检测与僵尸连接清理。
- 优雅关闭:服务重启时先发
Close(1001)并等待。
14. 反向代理与负载均衡
WebSocket 需要中间件显式支持 Upgrade 透传。
Nginx
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl http2;
server_name example.com;
location /ws/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# 长连接超时需显著放大,否则空闲会被断开
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
}
其他
- HAProxy:
timeout tunnel 1h+option http-server-connection/mode http。 - Envoy / Traefik / API 网关:原生支持 WebSocket upgrade。
- HTTP/2 与 HTTP/3:WebSocket over HTTP/2(RFC 8441);HTTP/3 需 QUIC 扩展支持。
粘性会话(Sticky Session):WebSocket 是有状态长连接,如果服务端有多实例且维护内存态,需要四层/七层粘性,或把状态外置到 Redis 等。
15. 安全实践
- **始终使用
wss://**:ws://明文可被窃听、篡改、注入。 - 鉴权:
- 握手中用 Cookie / URL token / 子协议携带凭据;
- 或在连接建立后发送首帧鉴权,未通过则立即
Close(4001)。 - Token 应短期有效、可撤销。
- **校验
Origin**:防止跨站 WebSocket 劫持(CSWSH)。服务端应维护允许的来源白名单。 - 输入校验:所有消息都视为不可信,做长度、格式、类型校验。
- 限制资源:
- 单条消息大小上限、单连接发送速率;
- 单 IP 连接数上限;
- 空闲超时自动踢出。
- 防 SSRF:若服务端接受客户端提供的 URL 进行反向 WebSocket 连接,需严格校验。
- TLS 配置:使用现代密码套件、HSTS;避免旧协议。
- 日志与审计:记录连接建立/关闭、关闭码、异常,便于排查与风控。
- 不要信任
Sec-WebSocket-Key之外的内容:握手头可能被伪造。 - 子协议/扩展白名单:只接受已知的子协议和扩展。
16. 性能与工程实践
16.1 心跳与保活
let pingTimer = null;
function startHeartbeat(ws) {
pingTimer = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping', ts: Date.now() }));
}
}, 25000);
}
- 协议层 Ping/Pong 只能探测链路,应用层心跳还能探测"服务假死"。
- 服务端同样应检测长时间无数据/无 Pong 的连接并清理。
16.2 断线重连(指数退避 + 抖动)
function connect() {
const ws = new WebSocket(URL);
let attempt = 0;
const retry = () => {
const base = Math.min(30000, 500 * 2 ** attempt++);
const jitter = Math.random() * base * 0.3; // 防止惊群
setTimeout(connect, base + jitter);
};
ws.onopen = () => { attempt = 0; };
ws.onclose = retry;
ws.onerror = () => ws.close();
}
16.3 发送队列与背压
bufferedAmount(浏览器)反映未发送字节;过大时应暂停生产或丢弃。- 服务端同理:对慢消费者要有队列上限与丢弃策略,避免内存膨胀。
function safeSend(ws, data, limit = 1 << 20) {
if (ws.bufferedAmount > limit) {
console.warn('backpressure, drop message');
return false;
}
ws.send(data);
return true;
}
16.4 消息设计
- 优先使用二进制(MessagePack / Protobuf / 自定义 TLV)替代 JSON,省带宽与解析开销。
- 协议需带版本号、消息类型、请求 ID(便于请求-响应配对)。
- 大消息考虑分片或分块传输。
16.5 其他
- 多路复用:单连接承载多种业务(用消息类型区分),减少连接数。
- 连接数控制:移动端/嵌入式注意内存与 FD 上限。
- 优雅重启:滚动发布时先
Close(1001),客户端按重连策略切换。
17. 调试与抓包
- 浏览器 DevTools:Network → WS,可查看帧内容、方向、长度。
- 命令行:
npm i -g wscat wscat -c wss://example.com/chatwebsocat wss://example.com/chat - 抓包:Wireshark 支持 WebSocket dissector;明文
ws://可直接看帧,wss://需提供 TLS 密钥日志(SSLKEYLOGFILE)。 - 握手排查:
- 若返回
200而非101:代理未透传Upgrade。 - 若卡在
CONNECTING:防火墙/证书/跨域问题。 - 若频繁断开:检查空闲超时、
proxy_read_timeout、心跳配置。
- 若返回
18. 嵌入式 / 物联网场景实践
在资源受限设备(MCU + RTOS/Linux)上使用 WebSocket,需注意:
- 内存:TLS 握手与缓冲会占较多 RAM;考虑精简的 TLS 实现(如 mbedTLS 裁剪)与固定缓冲区。
- 断线重连:弱网下需健壮的指数退避与状态恢复;建议设备侧维护会话令牌。
- 心跳:设备常经 NAT/移动网络,必须定时心跳,否则连接被回收。
- 半双工约束:若设备音频/外设同一时间只能收或发,需在应用层协调收发时序。
- TLS 证书:内置根证书、SNI、时间同步(证书校验依赖正确时钟)。
- 可靠性:WebSocket 只保证传输层可靠,业务级"恰好一次/至少一次"需在应用层实现(ACK、序号、去重)。
- 二进制上行:音频/传感器数据用 Binary 帧,按固定帧长切片,配合服务端流式解析。
- 看门狗:网络栈阻塞要兜底,避免拖垮实时任务。
典型数据流:
设备传感器/麦克风 ──(Binary 帧, 固定帧长)──► 云端
云端结果/指令 ◄──(Text/Binary 帧)───── 设备
19. 常见问题 FAQ
Q1:WebSocket 和 Socket 是一回事吗?
不是。Socket 是操作系统提供的网络编程接口(TCP/UDP);WebSocket 是建立在 TCP 之上的应用层协议。日常说"用 Socket 长连接"通常指自研 TCP 协议,与 WebSocket 不同。
Q2:WebSocket 会自动重连吗?
不会。断线后 onclose 触发,重连需应用自己实现(含退避、抖动、会话恢复)。
Q3:为什么客户端必须加掩码?
防止中间代理缓存污染,并让攻击者无法预测字节流。服务器帧不得加掩码。
Q4:101 之外还能返回什么?
普通 HTTP 错误码。401/403 鉴权失败,426 Upgrade Required 提示需要升级,404 路径不存在。
Q5:WebSocket 能传二进制吗?
能,用 Binary 帧(opcode=0x2),适合音频、图片、Protobuf 等。
Q6:为什么连接一段时间后自己断了?
多因 NAT/负载均衡/代理的空闲超时。解决:协议层 Ping + 应用层心跳,且调大 proxy_read_timeout。
Q7:WebSocket 和 SSE 怎么选?
只需服务器推送 → SSE 更简单;需要双向、低延迟 → WebSocket。
Q8:WebSocket 消息会"粘包/半包"吗?
不会。帧头含长度,协议保留消息边界;但若用 TCP 自研协议才需要处理粘包。
Q9:如何做请求-响应式调用?
在应用协议里加 requestId,服务端回带同一 requestId,客户端用 map 配对 Promise。
Q10:permessage-deflate 该开吗?
文本/JSON 多、带宽敏感时建议开;小消息多、CPU 紧张时可关。
20. 参考资料
- RFC 6455 — The WebSocket Protocol:https://datatracker.ietf.org/doc/html/rfc6455
- RFC 7692 — Compression Extensions for WebSocket:https://datatracker.ietf.org/doc/html/rfc7692
- RFC 8441 — Bootstrapping WebSockets with HTTP/2:https://datatracker.ietf.org/doc/html/rfc8441
- MDN — WebSocket API:https://developer.mozilla.org/docs/Web/API/WebSocket
- IANA WebSocket Close Code Registry:https://www.iana.org/assignments/websocket/websocket.xhtml
- RFC 8307 / RFC 8323 — CoAP over TCP/TLS/WebSockets(IoT 相关)
更多推荐
所有评论(0)