MDN WebSocket API 指南:手把手教你编写 WebSocket 服务器
2025-07-07 03:14:49作者:丁柯新Fawn
WebSocket 技术为现代 Web 应用提供了全双工通信能力,使得客户端和服务器可以随时互发消息。本文将深入讲解如何从零开始实现一个符合规范的 WebSocket 服务器。
理解 WebSocket 基础
WebSocket 服务器本质上是一个遵循特定协议的 TCP 服务器应用程序。与 HTTP 不同,WebSocket 在初始握手后会保持长连接,允许双向实时数据传输。
核心特点
- 基于 TCP 协议
- 全双工通信
- 低延迟
- 轻量级协议头
握手过程详解
WebSocket 连接始于一个特殊的 HTTP 升级请求,这是整个通信过程中唯一的 HTTP 交互。
客户端握手请求
客户端会发送如下格式的 HTTP 请求:
GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
关键字段说明:
Upgrade
和Connection
表明要升级到 WebSocket 协议Sec-WebSocket-Key
是随机生成的 base64 编码字符串Sec-WebSocket-Version
指定协议版本(当前为 13)
服务器响应握手
服务器必须正确计算并返回 Sec-WebSocket-Accept
值:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
计算步骤:
- 拼接客户端 key 和固定 GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
- 计算 SHA-1 哈希值
- 返回 base64 编码结果
数据帧格式解析
握手成功后,所有通信都通过数据帧进行。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| | |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+-------------------------------+
关键字段说明:
- FIN (1bit):是否为消息的最后一帧
- RSV1-3 (各1bit):保留位,通常为0
- Opcode (4bits):帧类型(1=文本,2=二进制,8=关闭连接等)
- MASK (1bit):客户端到服务器的消息必须掩码
- Payload length (7/7+16/7+64bits):有效载荷长度
载荷长度解码
根据初始7位长度值:
- ≤125:直接作为长度
- =126:后续2字节为长度
- =127:后续8字节为长度(最高位必须为0)
数据解掩码
客户端发送的数据使用32位掩码密钥进行XOR加密。解密算法:
def unmask(payload, mask):
return bytes([payload[i] ^ mask[i % 4] for i in range(len(payload))])
消息分片机制
大消息可以分割为多个帧传输:
- 第一帧:FIN=0,指定opcode(文本或二进制)
- 中间帧:FIN=0,opcode=0(延续帧)
- 最后一帧:FIN=1,opcode=0
心跳检测:Ping/Pong
WebSocket 通过Ping/Pong帧维持连接:
- Ping (opcode=9):可包含应用数据
- Pong (opcode=10):必须回显Ping的数据
实现建议:
def handle_ping(self, data):
self.send_frame(opcode=0xA, payload=data) # 发送Pong
连接关闭流程
正常关闭需交换关闭帧:
- 收到关闭帧(opcode=8)后应停止发送数据
- 回送关闭帧
- 关闭TCP连接
关闭帧可包含状态码和原因:
- 1000:正常关闭
- 1001:端点离开
- 1002:协议错误
安全注意事项
- 必须验证Origin头防CSRF攻击
- 限制最大帧大小防内存耗尽
- 实现速率限制防DDoS
- 始终验证客户端消息的掩码位
- 对文本帧强制UTF-8编码验证
性能优化建议
- 使用非阻塞I/O
- 实现连接池
- 考虑使用二进制协议
- 启用压缩扩展(如permessage-deflate)
- 支持多路复用(需要扩展)
示例代码结构
以下是Python伪代码示例:
class WebSocketServer:
def __init__(self, host, port):
self.socket = create_tcp_socket(host, port)
def run(self):
while True:
conn = accept_connection()
threading.Thread(target=self.handle_client, args=(conn,)).start()
def handle_client(self, conn):
try:
self.handshake(conn)
while True:
frame = self.read_frame(conn)
self.process_frame(conn, frame)
except ConnectionError:
pass
finally:
conn.close()
通过本文的详细讲解,您应该已经掌握了实现WebSocket服务器的核心要点。实际开发中还需考虑平台特性、错误处理和性能优化等因素。建议参考RFC6455规范实现完整的协议支持。