news 2026/1/7 18:14:03

websocket的异常处理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
websocket的异常处理

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); }); });

六、最佳实践总结

  1. 分级处理错误:区分连接错误、通信错误、业务错误

  2. 优雅降级:WebSocket失败时降级到轮询或SSE

  3. 监控告警:记录关键指标,设置阈值告警

  4. 资源清理:确保连接关闭时释放所有资源

  5. 安全考虑:验证消息来源,防止注入攻击

  6. 性能优化:合理设置缓冲区,避免内存泄漏

  7. 兼容性处理:考虑不同浏览器的实现差异

通过以上全面的异常处理机制,可以构建健壮的WebSocket应用,提供稳定的实时通信能力。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/4 13:48:21

周大生钻石镶嵌:HeyGem制作情侣对戒诞生过程短片

HeyGem 数字人视频生成系统在周大生情侣对戒项目中的工程实践 在品牌营销内容日益“内卷”的今天&#xff0c;一条高质量的宣传短片动辄需要数日拍摄、反复调试灯光与演员表现力&#xff0c;尤其对于珠宝这类高度依赖情感表达和细节呈现的行业&#xff0c;传统制作流程的成本与…

作者头像 李华
网站建设 2026/1/4 13:47:49

HeyGem运行实时日志.log内容结构解读

HeyGem运行实时日志.log内容结构解读 在数字人视频生成系统逐渐从实验走向落地的今天&#xff0c;一个常被忽视但至关重要的组件正悄然发挥着核心作用——日志系统。HeyGem 作为一款基于大模型驱动的音视频融合工具&#xff0c;在批量处理场景下面临着任务复杂度高、执行周期长…

作者头像 李华
网站建设 2026/1/4 13:47:11

HeyGem数字人视频合成系统安装与启动详细教程(附日志查看方法)

HeyGem数字人视频合成系统安装与启动深度指南&#xff08;含日志调试技巧&#xff09; 在内容生产节奏日益加快的今天&#xff0c;企业对高效、低成本生成专业级视频的需求愈发迫切。传统依赖真人出镜和后期剪辑的方式&#xff0c;不仅耗时耗力&#xff0c;还难以实现大规模个性…

作者头像 李华
网站建设 2026/1/4 13:45:48

璞泰来负极材料:HeyGem制作快充技术背后的科学解释

HeyGem数字人视频生成技术解析&#xff1a;从语音驱动到批量生产的AI实践 在内容爆炸的时代&#xff0c;企业对高质量视频的需求呈指数级增长。无论是电商平台的产品讲解、金融机构的每日播报&#xff0c;还是教育机构的在线课程&#xff0c;传统真人拍摄模式早已不堪重负——成…

作者头像 李华
网站建设 2026/1/4 13:45:34

进度X/总数显示错误?可能是多线程计数冲突

进度X/总数显示错误&#xff1f;可能是多线程计数冲突 在开发AI驱动的批量处理系统时&#xff0c;一个看似简单的功能——“当前进度&#xff1a;3/10”——往往会在高并发场景下暴露出令人头疼的问题。你有没有遇到过这样的情况&#xff1a;前端界面中的进度条突然从 4/10 跳到…

作者头像 李华
网站建设 2026/1/4 13:44:52

HeyGem启动脚本start_app.sh执行失败常见原因排查

HeyGem启动脚本start_app.sh执行失败常见原因排查 在部署AI数字人系统时&#xff0c;一个看似简单的命令却可能卡住整个上线流程——当你满怀期待地输入 bash start_app.sh&#xff0c;终端却返回一串令人困惑的错误信息&#xff0c;或者干脆“静默退出”&#xff0c;浏览器也无…

作者头像 李华