首页
/ MDN WebSocket API 指南:手把手教你编写 WebSocket 服务器

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

关键字段说明:

  • UpgradeConnection 表明要升级到 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=

计算步骤:

  1. 拼接客户端 key 和固定 GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
  2. 计算 SHA-1 哈希值
  3. 返回 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))])

消息分片机制

大消息可以分割为多个帧传输:

  1. 第一帧:FIN=0,指定opcode(文本或二进制)
  2. 中间帧:FIN=0,opcode=0(延续帧)
  3. 最后一帧: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

连接关闭流程

正常关闭需交换关闭帧:

  1. 收到关闭帧(opcode=8)后应停止发送数据
  2. 回送关闭帧
  3. 关闭TCP连接

关闭帧可包含状态码和原因:

  • 1000:正常关闭
  • 1001:端点离开
  • 1002:协议错误

安全注意事项

  1. 必须验证Origin头防CSRF攻击
  2. 限制最大帧大小防内存耗尽
  3. 实现速率限制防DDoS
  4. 始终验证客户端消息的掩码位
  5. 对文本帧强制UTF-8编码验证

性能优化建议

  1. 使用非阻塞I/O
  2. 实现连接池
  3. 考虑使用二进制协议
  4. 启用压缩扩展(如permessage-deflate)
  5. 支持多路复用(需要扩展)

示例代码结构

以下是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规范实现完整的协议支持。