WebSocket 异常处理需要从连接、通信、断开等各个环节进行全面的错误管理。以下是详细的处理方案:
一、连接阶段异常处理
1. 连接建立失败
const socket = new WebSocket('ws://example.com'); // 连接错误 socket.onerror = (error) => { console.error('WebSocket连接错误:', error); // 执行重连逻辑 reconnect(); }; // 连接超时处理 const connectTimeout = setTimeout(() => { if (socket.readyState !== WebSocket.OPEN) { socket.close(); console.error('连接超时'); } }, 5000); socket.onopen = () => { clearTimeout(connectTimeout); console.log('连接成功'); };2. 重连机制
class WebSocketManager { constructor(url, options = {}) { this.url = url; this.maxRetries = options.maxRetries || 5; this.retryCount = 0; this.reconnectDelay = options.reconnectDelay || 1000; this.socket = null; } connect() { this.socket = new WebSocket(this.url); this.socket.onopen = () => { console.log('WebSocket连接成功'); this.retryCount = 0; // 重置重试计数 }; this.socket.onerror = (error) => { console.error('WebSocket错误:', error); }; this.socket.onclose = (event) => { console.log(`连接关闭,代码: ${event.code}, 原因: ${event.reason}`); if (this.shouldReconnect(event)) { this.scheduleReconnect(); } }; } shouldReconnect(closeEvent) { // 非正常关闭且未超过最大重试次数 return closeEvent.code !== 1000 && this.retryCount < this.maxRetries; } scheduleReconnect() { this.retryCount++; const delay = this.reconnectDelay * Math.pow(2, this.retryCount - 1); console.log(`${delay/1000}秒后尝试第${this.retryCount}次重连`); setTimeout(() => { this.connect(); }, delay); } }二、通信阶段异常处理
1. 发送消息异常
class WebSocketService { sendMessage(message) { if (this.socket.readyState === WebSocket.OPEN) { try { // 序列化消息 const data = typeof message === 'object' ? JSON.stringify(message) : message; this.socket.send(data); return true; } catch (error) { console.error('发送消息失败:', error); this.queueMessage(message); // 进入消息队列 return false; } } else { console.warn('WebSocket未连接,消息被缓存'); this.queueMessage(message); return false; } } queueMessage(message) { // 实现消息队列 this.messageQueue.push(message); } // 连接恢复后发送队列中的消息 flushMessageQueue() { while (this.messageQueue.length > 0 && this.socket.readyState === WebSocket.OPEN) { const message = this.messageQueue.shift(); this.sendMessage(message); } } }2. 接收消息异常
socket.onmessage = (event) => { try { let data; // 处理不同类型的数据 if (typeof event.data === 'string') { data = JSON.parse(event.data); } else if (event.data instanceof Blob) { // 处理二进制数据 this.handleBlobData(event.data); return; } else if (event.data instanceof ArrayBuffer) { // 处理ArrayBuffer this.handleArrayBuffer(event.data); return; } // 处理数据 this.processMessage(data); } catch (error) { console.error('处理消息失败:', error); // 发送错误确认 this.sendErrorAck(error.message); // 记录错误日志 this.logError({ type: 'MESSAGE_PARSE_ERROR', error: error.message, rawData: event.data }); } };三、心跳机制与超时处理
1. 心跳检测
class HeartbeatManager { constructor(socket, interval = 30000) { this.socket = socket; this.interval = interval; this.pingInterval = null; this.pongTimeout = null; this.lastPongTime = null; } start() { this.pingInterval = setInterval(() => { if (this.socket.readyState === WebSocket.OPEN) { this.sendPing(); this.waitForPong(); } }, this.interval); } sendPing() { try { this.socket.send(JSON.stringify({ type: 'ping', timestamp: Date.now() })); } catch (error) { console.error('发送心跳失败:', error); } } waitForPong() { // 设置pong超时 this.pongTimeout = setTimeout(() => { console.error('心跳响应超时'); this.socket.close(4000, 'Heartbeat timeout'); }, 5000); } handlePong() { clearTimeout(this.pongTimeout); this.lastPongTime = Date.now(); } stop() { clearInterval(this.pingInterval); clearTimeout(this.pongTimeout); } }四、完整封装示例
class RobustWebSocket { constructor(url, options = {}) { this.url = url; this.options = { maxRetries: 5, reconnectDelay: 1000, heartbeatInterval: 30000, ...options }; this.socket = null; this.retryCount = 0; this.messageQueue = []; this.heartbeat = null; this.eventHandlers = new Map(); this.connect(); } connect() { try { this.socket = new WebSocket(this.url); this.setupEventListeners(); } catch (error) { console.error('创建WebSocket失败:', error); this.handleConnectionError(error); } } setupEventListeners() { this.socket.onopen = this.handleOpen.bind(this); this.socket.onmessage = this.handleMessage.bind(this); this.socket.onerror = this.handleError.bind(this); this.socket.onclose = this.handleClose.bind(this); } handleOpen() { console.log('WebSocket连接成功'); this.retryCount = 0; // 启动心跳 this.startHeartbeat(); // 发送队列中的消息 this.flushMessageQueue(); // 触发事件 this.emit('connected'); } handleMessage(event) { try { const message = this.parseMessage(event.data); // 处理心跳响应 if (message.type === 'pong') { this.heartbeat?.handlePong(); return; } // 触发消息事件 this.emit('message', message); } catch (error) { console.error('消息处理错误:', error); this.emit('error', { type: 'MESSAGE_ERROR', error: error.message }); } } handleError(error) { console.error('WebSocket错误:', error); this.emit('error', { type: 'WEBSOCKET_ERROR', error: error }); } handleClose(event) { console.log(`连接关闭,代码: ${event.code}, 原因: ${event.reason}`); // 停止心跳 this.stopHeartbeat(); // 触发断开事件 this.emit('disconnected', { code: event.code, reason: event.reason }); // 判断是否需要重连 if (this.shouldReconnect(event)) { this.scheduleReconnect(); } } shouldReconnect(closeEvent) { // 1000-1004 是正常关闭代码,不重连 const normalCloseCodes = [1000, 1001, 1005]; return !normalCloseCodes.includes(closeEvent.code) && this.retryCount < this.options.maxRetries; } send(data) { return new Promise((resolve, reject) => { if (this.socket?.readyState === WebSocket.OPEN) { try { const message = typeof data === 'object' ? JSON.stringify(data) : data; this.socket.send(message); resolve(); } catch (error) { console.error('发送失败:', error); this.queueMessage(data); reject(error); } } else { console.warn('连接未就绪,消息进入队列'); this.queueMessage(data); reject(new Error('WebSocket not connected')); } }); } close(code = 1000, reason = 'Normal closure') { this.stopHeartbeat(); this.socket?.close(code, reason); } // 事件系统 on(event, handler) { if (!this.eventHandlers.has(event)) { this.eventHandlers.set(event, []); } this.eventHandlers.get(event).push(handler); } emit(event, data) { const handlers = this.eventHandlers.get(event) || []; handlers.forEach(handler => handler(data)); } // 其他辅助方法... } // 使用示例 const ws = new RobustWebSocket('ws://example.com', { maxRetries: 3, reconnectDelay: 2000 }); ws.on('connected', () => { console.log('已连接'); }); ws.on('message', (data) => { console.log('收到消息:', data); }); ws.on('error', (error) => { console.error('发生错误:', error); }); ws.on('disconnected', (reason) => { console.log('断开连接:', reason); });五、服务器端异常处理(Node.js示例)
const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', (ws, req) => { console.log('客户端连接'); // 设置消息处理 ws.on('message', (message) => { try { // 验证消息格式 if (this.isValidMessage(message)) { // 处理消息 this.handleMessage(ws, message); } else { throw new Error('Invalid message format'); } } catch (error) { console.error('处理客户端消息错误:', error); // 发送错误响应 ws.send(JSON.stringify({ type: 'error', error: error.message })); } }); // 错误处理 ws.on('error', (error) => { console.error('WebSocket错误:', error); }); // 连接关闭 ws.on('close', (code, reason) => { console.log(`客户端断开,代码: ${code}, 原因: ${reason}`); // 清理资源 this.cleanupClientResources(ws); }); // 设置心跳 const heartbeatInterval = setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.ping(); } }, 30000); ws.on('pong', () => { // 更新最后活动时间 ws.isAlive = true; }); // 清理定时器 ws.on('close', () => { clearInterval(heartbeatInterval); }); });六、最佳实践总结
分级处理错误:区分连接错误、通信错误、业务错误
优雅降级:WebSocket失败时降级到轮询或SSE
监控告警:记录关键指标,设置阈值告警
资源清理:确保连接关闭时释放所有资源
安全考虑:验证消息来源,防止注入攻击
性能优化:合理设置缓冲区,避免内存泄漏
兼容性处理:考虑不同浏览器的实现差异
通过以上全面的异常处理机制,可以构建健壮的WebSocket应用,提供稳定的实时通信能力。