WebSocket 实时通信:从协议原理到工程实践 协议原理 WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,RFC 6455 规范了其标准。与传统的 HTTP 轮询相比,WebSocket 通过一次握手建立持久连接,显著降低了通信延迟和服务器负载。 握手流程 客户端发起升级请求: 服务器返回 101 状态码并完成握手: 帧结构解析 WebSocket 使用帧(Frame)传输数据,基本结构包括: FIN (1 bit): 标识是否为最后一个分片 Opcode (4 bits): 定义帧类型(文本、二进制、关闭、Ping 等) MASK (1 bit): 标识载荷是否经过掩码处理(客户端必须掩码) Payload Length: 7/7+16/7+64 位变长编码
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,RFC 6455 规范了其标准。与传统的 HTTP 轮询相比,WebSocket 通过一次握手建立持久连接,显著降低了通信延迟和服务器负载。
客户端发起升级请求:
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13
服务器返回 101 状态码并完成握手:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
WebSocket 使用帧(Frame)传输数据,基本结构包括:
const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', (ws) => { console.log('新客户端连接'); // 心跳检测 ws.isAlive = true; ws.on('pong', () => { ws.isAlive = true; }); ws.on('message', (message) => { console.log('收到消息:', message.toString()); // 广播给所有客户端 wss.clients.forEach((client) => { if (client !== ws && client.readyState === WebSocket.OPEN) { client.send(message); } }); }); ws.on('close', () => { console.log('客户端断开连接'); }); }); // 30秒心跳检查 const interval = setInterval(() => { wss.clients.forEach((ws) => { if (ws.isAlive === false) { return ws.terminate(); } ws.isAlive = false; ws.ping(); }); }, 30000);
const socket = new WebSocket('ws://localhost:8080'); socket.addEventListener('open', (event) => { socket.send('Hello Server!'); }); socket.addEventListener('message', (event) => { console.log('收到消息:', event.data); }); socket.addEventListener('close', (event) => { console.log('连接关闭:', event.code, event.reason); });
class WebSocketClient { constructor(url) { this.url = url; this.reconnectDelay = 1000; this.maxReconnectDelay = 30000; this.connect(); } connect() { this.ws = new WebSocket(this.url); this.ws.onopen = () => { console.log('连接成功'); this.reconnectDelay = 1000; }; this.ws.onclose = () => { console.log('连接断开,准备重连...'); setTimeout(() => this.connect(), this.reconnectDelay); this.reconnectDelay = Math.min( this.reconnectDelay * 2, this.maxReconnectDelay ); }; } send(data) { if (this.ws.readyState === WebSocket.OPEN) { this.ws.send(JSON.stringify(data)); } else { console.warn('连接未建立,无法发送消息'); } } }
class ReliableWebSocket extends WebSocketClient { constructor(url) { super(url); this.messageQueue = []; this.ackedMessages = new Set(); } send(data) { const messageId = Date.now() + Math.random(); const message = { id: messageId, data }; this.messageQueue.push(message); super.send(message); } handleMessage(event) { const { type, id } = JSON.parse(event.data); if (type === 'ACK') { this.ackedMessages.add(id); this.messageQueue = this.messageQueue.filter(m => m.id !== id); } } resendPending() { this.messageQueue.forEach(msg => { if (!this.ackedMessages.has(msg.id)) { super.send(msg); } }); } }
使用 Nginx 反向代理:
upstream websocket_backend { ip_hash; server backend1:8080; server backend2:8080; } server { listen 80; location /ws { proxy_pass http://websocket_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } }
关键点:
ip_hash 确保同一客户端连接到同一后端proxy_read_timeout 设置长连接超时Upgrade 头完成握手const pako = require('pako'); ws.send(JSON.stringify({ large: 'data' }), { binary: true }); // 接收端解压 ws.on('message', (data) => { if (data instanceof ArrayBuffer) { const decompressed = pako.inflate(new Uint8Array(data)); const text = new TextDecoder().decode(decompressed); console.log(JSON.parse(text)); } });
function sendLargeFile(ws, file, chunkSize = 16 * 1024) { const reader = file.stream().getReader(); let isFirstChunk = true; while (true) { const { done, value } = await reader.read(); if (done) break; const frame = { type: 'chunk', data: value, isLast: false, isFirst: isFirstChunk }; ws.send(JSON.stringify(frame)); isFirstChunk = false; } // 发送结束标记 ws.send(JSON.stringify({ type: 'chunk', isLast: true })); }
const wss = new WebSocket.Server({ port: 8080, verifyClient: (info, cb) => { const origin = info.origin; if (allowedOrigins.includes(origin)) { cb(true); } else { cb(false, 403, 'Origin not allowed'); } } });
WebSocket 通过持久连接和双向通信能力,为实时应用提供了高效解决方案。理解协议原理、合理设计状态管理、实现可靠的消息传输机制,是构建生产级 WebSocket 应用的关键。在微服务架构中,结合消息队列和负载均衡,可以进一步扩展系统的实时通信能力。