RFC 6455 - WebSocket 协议
- 状态: Proposed Standard
- 发布日期: 2011 年 12 月
- 文档流: IETF
- 勘误: 无勘误
摘要
WebSocket 协议允许客户端和服务器在单个 TCP 连接上进行全双工通信. WebSocket 协议设计为可在 Web 浏览器和 Web 服务器中实现, 但也可由任何客户端或服务器应用使用.
WebSocket 协议是一个独立的, 基于 TCP 的协议. 它与 HTTP 的唯一关系是其握手会被 HTTP 服务器解释为 Upgrade 请求.
目录
- 1. 引言
- 1.1 背景
- 1.2 协议概览
- 1.3 开放握手
- 1.4 关闭握手
- 1.5 设计理念
- 1.6 安全模型
- 1.7 与 TCP 和 HTTP 的关系
- 1.8 建立连接
- 1.9 子协议和扩展
- 2. 一致性要求
- 2.1 术语和其他约定
- 3. WebSocket URI
- 4. 开放握手
- 4.1 客户端要求
- 4.2 服务器端要求
- 4.3 收集扩展和子协议
- 4.4 支持多个版本
- 5. 数据成帧
- 5.1 概览
- 5.2 基础成帧协议
- 5.3 客户端到服务器的掩码
- 5.4 分片
- 5.5 控制帧
- 5.6 数据帧
- 5.7 示例
- 5.8 可扩展性
- 6. 发送和接收数据
- 6.1 发送数据
- 6.2 接收数据
- 7. 关闭连接
- 7.1 定义
- 7.2 异常关闭
- 7.3 正常关闭
- 7.4 状态码
- 8. 错误处理
- 8.1 处理 UTF-8 编码数据中的错误
- 9. 扩展
- 9.1 协商扩展
- 9.2 已知扩展
- 10. 安全考虑
- 10.1 非浏览器客户端
- 10.2 Origin 考虑
- 10.3 针对基础设施的攻击
- 10.4 实现特定限制
- 10.5 WebSocket 客户端认证
- 10.6 连接保密性和完整性
- 10.7 无效数据处理
- 10.8 SHA-1 的使用
- 11. IANA 考虑
- 12. 在其他规范中使用 WebSocket 协议
- 13. 致谢
- 14. 参考文献
- 14.1 规范性引用
- 14.2 资料性引用
相关资源
- 实现指南: WebSocket 协议实现指南 - 详细技术文档, 代码示例和最佳实践
- 官方 RFC: RFC 6455
- DataTracker: RFC 6455 DataTracker
- 勘误: RFC Editor Errata
相关 RFC
- RFC 6455: The WebSocket Protocol ← 本文档
- RFC 7692: WebSocket Compression Extensions
- RFC 8441: Bootstrapping WebSockets with HTTP/2
快速参考
WebSocket URI 格式
ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
ws://- 未加密连接, 默认端口 80wss://- TLS 加密连接, 默认端口 443, 生产环境推荐使用
握手示例
客户端请求:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: http://example.com
服务器响应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
帧类型 (Opcode)
| Opcode | 类型 | 描述 |
|---|---|---|
| 0x0 | Continuation | Continuation frame |
| 0x1 | Text | Text frame (UTF-8) |
| 0x2 | Binary | Binary frame |
| 0x8 | Close | Close frame |
| 0x9 | Ping | Ping frame (heartbeat) |
| 0xA | Pong | Pong frame (heartbeat response) |
关闭状态码
| Code | 名称 | 描述 |
|---|---|---|
| 1000 | Normal Closure | 正常关闭 |
| 1001 | Going Away | 端点离开, 例如页面导航 |
| 1002 | Protocol Error | 协议错误 |
| 1003 | Unsupported Data | 不支持的数据类型 |
| 1006 | Abnormal Closure | 异常关闭, 未发送 Close frame |
| 1009 | Message Too Big | 消息过大 |
| 1011 | Internal Error | 服务器内部错误 |
帧结构 (基础格式)
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 |
+-------------------------------+-------------------------------+
要点:
- FIN: 1 表示最终帧, 0 表示后续还有帧
- MASK: 客户端到服务器必须为 1, 服务器到客户端必须为 0
- Opcode: 帧类型标识符
- Payload Length: 数据长度, 0-125 直接表示, 126 使用 16-bit, 127 使用 64-bit
核心术语
| 英文术语 | 描述 |
|---|---|
| Full-Duplex Communication | 双向同时通信 |
| Opening Handshake | 升级到 WebSocket 的 HTTP 握手过程 |
| Closing Handshake | 优雅关闭连接的过程 |
| Frame | 数据传输的基本单元 |
| Message | 由一个或多个帧组成 |
| Masking | 客户端数据必须进行掩码处理的安全机制 |
| Fragmentation | 将大型消息拆分为多个帧传输 |
WebSocket 协议实现指南
本指南提供了实现 WebSocket 协议 (RFC 6455) 的详细技术文档, 最佳实践和关键考虑因素.
1. 协议概述
1.1 两部分架构
WebSocket 协议由两个主要部分组成:
- 开放握手 (Opening Handshake): 基于 HTTP 的升级握手
- 数据传输 (Data Transfer): 双向通信通道
1.2 关键特性
- 全双工通信 (Full-Duplex Communication): 双方可独立发送数据
- 基于帧的协议 (Frame-Based Protocol): 消息由一个或多个帧组成
- 低开销 (Low Overhead): 相比 HTTP 轮询显著降低网络负载
- 基于 TCP (Built on TCP): 利用可靠的传输层协议
2. 握手实现
2.1 客户端握手
HTTP Upgrade 请求:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
必需头部字段:
Upgrade: websocket- 请求协议升级Connection: Upgrade- 指示升级连接Sec-WebSocket-Key- Base64 编码的随机 16 字节值Sec-WebSocket-Version: 13- WebSocket 协议版本
可选头部字段:
Sec-WebSocket-Protocol- 子协议列表Sec-WebSocket-Extensions- 扩展列表Origin- 浏览器客户端必需
2.2 服务器握手
HTTP 101 响应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Accept 计算:
1. 连接客户端的 Sec-WebSocket-Key 和 GUID:
key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
2. 计算 SHA-1 哈希
3. Base64 编码结果
验证步骤:
- 检查 HTTP 状态码是否为 101
- 验证
Sec-WebSocket-Accept值 - 确认
Upgrade和Connection头部 - 验证协议和扩展协商
3. 帧格式
3.1 基础帧结构
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): 消息的最后一帧时为 1
- RSV1-3 (3 bits): 保留用于扩展
- Opcode (4 bits): 帧类型
- MASK (1 bit): 客户端发送时必须为 1
- Payload length (7/7+16/7+64 bits): 有效载荷长度
- Masking-key (0/32 bits): MASK=1 时存在
3.2 操作码 (Opcodes)
| Opcode | 含义 | 类型 |
|---|---|---|
| 0x0 | Continuation Frame (继续帧) | 数据帧 |
| 0x1 | Text Frame (文本帧) | 数据帧 |
| 0x2 | Binary Frame (二进制帧) | 数据帧 |
| 0x8 | Close frame (关闭帧) | 控制帧 |
| 0x9 | Ping | 控制帧 |
| 0xA | Pong | 控制帧 |
3.3 客户端到服务器的掩码
客户端必须对所有发送的帧进行掩码处理:
masked-data[i] = original-data[i] XOR masking-key[i mod 4]
实现注意事项:
- 掩码密钥必须是不可预测的随机值
- 服务器必须验证客户端帧已被掩码
- 服务器发送的帧不应被掩码
4. 消息分片
4.1 分片规则
- 首帧: FIN=0, opcode=0x1 或 0x2
- 中间帧: FIN=0, opcode=0x0
- 末帧: FIN=1, opcode=0x0
示例:
Frame 1: FIN=0, opcode=0x1 (text), data="Hello"
Frame 2: FIN=0, opcode=0x0 (continuation), data=" "
Frame 3: FIN=1, opcode=0x0 (continuation), data="World"
4.2 交错限制
- 控制帧可以插入分片消息之间
- 数据帧不能交错(必须完成当前消息)
5. 控制帧
5.1 Close frame (关闭帧)
Opcode: 0x8
有效载荷结构:
+--------+--------+---...---+
| Status | | |
| Code | Reason | (UTF-8) |
| (16) | | |
+--------+--------+---...---+
常见状态码:
1000: Normal Closure (正常关闭)1001: Going Away (端点离开)1002: Protocol Error (协议错误)1003: Unsupported Data (不支持的数据)1006: Abnormal Closure (异常关闭, 不应发送)1007: Invalid frame payload data (无效帧有效载荷数据)1008: Policy Violation (策略违规)1009: Message Too Big (消息过大)1010: Mandatory Extension (必需扩展)1011: Internal Server Error (内部服务器错误)
5.2 Ping/Pong 帧
Ping (Opcode: 0x9):
- 发送以检查连接是否存活
- 可包含应用数据
Pong (Opcode: 0xA):
- 响应 Ping 帧
- 必须包含 Ping 帧的相同有效载荷
6. 安全考虑
6.1 Origin 验证
服务器应验证 Origin 头部以防止跨源攻击:
// 服务器端示例
if (request.headers.origin !== 'https://trusted-domain.com') {
response.statusCode = 403;
response.end('Forbidden');
}
6.2 TLS 使用
- 强烈推荐使用 wss:// (WebSocket over TLS)
- 提供加密和完整性保护
- 防止中间人攻击
6.3 输入验证
- 验证所有接收的数据
- UTF-8 文本帧必须包含有效的 UTF-8
- 实施消息大小限制
6.4 拒绝服务保护
实施限制:
- 最大帧大小
- 最大消息大小
- 连接速率限制
- 超时机制
7. 实现最佳实践
7.1 连接管理
保持连接活跃:
- 实施 Ping/Pong 心跳机制
- 典型间隔: 30-60 秒
- 超时检测: 2-3 个周期
优雅关闭:
1. 发送 Close frame (状态码 1000)
2. 等待对端 Close 帧
3. 关闭底层 TCP 连接
7.2 错误处理
协议错误响应:
- 立即发送 Close frame (适当的状态码)
- 关闭连接
- 记录错误详情
应用错误:
- 通过应用级消息传达
- 考虑使用子协议定义的错误格式
7.3 性能优化
减少延迟:
- 禁用 TCP Nagle 算法(TCP_NODELAY)
- 使用适当的 TCP 缓冲区大小
消息批处理:
- 合并小消息以减少帧开销
- 平衡延迟与吞吐量
7.4 实现特定限制
必须保护的限制:
- 帧大小限制(防止内存耗尽)
- 消息大小限制(防止 DoS)
- 并发连接数限制
示例限制:
MAX_FRAME_SIZE: 1 MB
MAX_MESSAGE_SIZE: 16 MB
CONNECTION_TIMEOUT: 60 seconds
PING_INTERVAL: 30 seconds
8. 常见陷阱
8.1 客户端错误
错误: 忘记对帧进行掩码处理
- 客户端必须对所有帧进行掩码
错误: 不验证服务器握手
- 必须检查 Sec-WebSocket-Accept
错误: 假设消息立即发送
- TCP 缓冲可能延迟传输
8.2 服务器端错误
错误: 不验证客户端掩码
- 必须拒绝未掩码的客户端帧
错误: 不处理分片消息
- 必须正确重组分片消息
错误: 忽略 UTF-8 验证
- 文本帧必须包含有效的 UTF-8
8.3 协议违规
- 在握手期间发送数据帧
- 交错数据帧
- 分片控制帧
- 发送保留的操作码
9. 测试清单
9.1 握手测试
- 有效握手成功
- 无效密钥被拒绝
- 错误版本被拒绝
- 协议协商正常工作
- Origin 验证正确
9.2 帧处理
- 处理所有操作码
- 正确应用/验证掩码
- 处理各种有效载荷长度
- 分片消息重组
- UTF-8 验证
9.3 错误条件
- 协议错误触发关闭
- 超大消息被拒绝
- 无效 UTF-8 被拒绝
- 超时正确处理
10. 参考实现模式
10.1 状态机
States:
- CONNECTING: 握手进行中
- OPEN: 连接已建立
- CLOSING: 关闭握手进行中
- CLOSED: 连接已关闭
10.2 事件驱动架构
关键事件:
onopen- 连接建立onmessage- 接收消息onerror- 发生错误onclose- 连接关闭
10.3 缓冲区管理
- 使用环形缓冲区进行接收
- 实施反压机制
- 监控内存使用
11. 调试技巧
11.1 常用调试工具
- Browser DevTools: Network 标签的 WS 面板
- Wireshark: 分析原始帧
- WebSocket Echo Test: wss://echo.websocket.org
11.2 日志记录建议
记录内容:
- 握手头部
- 帧元数据(opcode, FIN, 长度)
- 关闭原因
- 错误和异常
避免记录:
- 敏感有效载荷数据
- 掩码密钥
12. 相关标准
- RFC 6455: The WebSocket Protocol (本规范)
- RFC 7692: WebSocket Compression Extensions
- RFC 8441: Bootstrapping WebSockets with HTTP/2
- RFC 6202: Known Issues with HTTP Polling
13. 其他资源
- W3C WebSocket API: 浏览器 JavaScript API 规范
- IANA WebSocket Registry: 扩展和子协议注册
- WebSocket.org: 教程和示例
注意: 本实现指南基于 RFC 6455 标准. 实现时应参考完整的 RFC 文档以了解所有规范细节.