本文作者老前端的功夫,即时通讯网有修订和改动。
cover_opti.png (8.27 KB, 下载次数: 0)
下载附件 保存到相册
昨天 21:23 上传
1.png (13.03 KB, 下载次数: 0)
昨天 22:19 上传
class ReconnectingWebSocket { constructor(url, options = {}) { this.url = url this.options = { heartbeatInterval: 30000, // 心跳间隔 30秒 heartbeatTimeout: 10000, // 心跳超时 10秒 reconnectDelay: 1000, // 初始重连延迟 1秒 maxReconnectDelay: 30000, // 最大重连延迟 30秒 reconnectDecay: 1.5, // 退避指数 maxReconnectAttempts: Infinity, // 最大重试次数 ...options } this.ws = null this.reconnectAttempts = 0 this.heartbeatTimer = null this.heartbeatTimeoutTimer = null this.manualClose = false // 是否手动关闭(手动关不重连) this.messageQueue = [] // 重连期间的离线消息队列 this.listeners = new Map() // 事件监听器 this.connect() } // 建立连接 connect() { this.ws = new WebSocket(this.url) this.ws.onopen = (event) => { console.log('WebSocket 连接成功') this.reconnectAttempts = 0 this.startHeartbeat() this.flushMessageQueue() // 重连成功后发送队列中的消息 this.emit('open', event) } this.ws.onmessage = (event) => { // 收到任何消息都重置心跳超时计时器 this.resetHeartbeatTimeout() this.emit('message', event.data) } this.ws.onclose = (event) => { console.warn('WebSocket 连接关闭', event.code, event.reason) this.stopHeartbeat() if (!this.manualClose) { this.scheduleReconnect() } this.emit('close', event) } this.ws.onerror = (error) => { console.error('WebSocket 错误', error) this.emit('error', error) // onerror之后通常会触发onclose,所以这里不额外处理重连 } } // 启动心跳 startHeartbeat() { this.stopHeartbeat() // 清理旧定时器 this.heartbeatTimer = setInterval(() => { if (this.ws.readyState === WebSocket.OPEN) { // 发送心跳包,格式和后端约定好 this.ws.send(JSON.stringify({ type: 'PING' })) // 设置心跳超时定时器 this.heartbeatTimeoutTimer = setTimeout(() => { console.warn('心跳超时,主动断开连接') this.ws.close() // 触发onclose,进而触发重连 }, this.options.heartbeatTimeout) } }, this.options.heartbeatInterval) } // 重置心跳超时(收到消息时调用) resetHeartbeatTimeout() { if (this.heartbeatTimeoutTimer) { clearTimeout(this.heartbeatTimeoutTimer) this.heartbeatTimeoutTimer = null } } // 停止心跳 stopHeartbeat() { if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer) this.heartbeatTimer = null } this.resetHeartbeatTimeout() } // 指数退避重连调度 scheduleReconnect() { if (this.reconnectAttempts >= this.options.maxReconnectAttempts) { console.error('已达最大重连次数,停止重连') this.emit('reconnectFailed') return } const delay = Math.min( this.options.reconnectDelay * Math.pow(this.options.reconnectDecay, this.reconnectAttempts), this.options.maxReconnectDelay ) console.log(`${delay}ms 后尝试第 ${this.reconnectAttempts + 1} 次重连`) setTimeout(() => { if (!this.manualClose) { this.reconnectAttempts++ this.emit('reconnecting', this.reconnectAttempts) this.connect() } }, delay) } // 发送消息(支持离线队列) send(data) { const message = typeof data === 'string' ? data : JSON.stringify(data) if (this.ws.readyState === WebSocket.OPEN) { this.ws.send(message) } else { console.warn('连接未就绪,消息进入队列') this.messageQueue.push(message) } } // 连接恢复后发送队列中的消息 flushMessageQueue() { while (this.messageQueue.length > 0) { const msg = this.messageQueue.shift() if (this.ws.readyState === WebSocket.OPEN) { this.ws.send(msg) } } } // 手动关闭(不再重连) close() { this.manualClose = true this.stopHeartbeat() this.ws.close() } // 事件监听系统 on(event, callback) { if (!this.listeners.has(event)) { this.listeners.set(event, new Set()) } this.listeners.get(event).add(callback) } off(event, callback) { this.listeners.get(event)?.delete(callback) } emit(event, ...args) { this.listeners.get(event)?.forEach(cb => cb(...args)) } } // 使用示例 const ws = new ReconnectingWebSocket('wss://chat.example.com/ws', { heartbeatInterval: 20000, maxReconnectAttempts: 10 }) ws.on('open', () => { console.log('连接建立,发送鉴权信息') ws.send({ type: 'AUTH', token: localStorage.getItem('token') }) }) ws.on('message', (data) => { const msg = JSON.parse(data) if (msg.type === 'PONG') { console.log('收到心跳响应') } else { // 处理业务消息 handleChatMessage(msg) } }) ws.on('reconnecting', (attempt) => { showToast(`网络不稳定,正在重连... (${attempt}/10)`) }) ws.on('reconnectFailed', () => { showToast('连接失败,请检查网络后刷新页面') })
// 前端发 { "type": "PING" } // 后端回 { "type": "PONG" }
// 页面可见性优化示例 document.addEventListener('visibilitychange', () => { if (document.hidden) { ws.options.heartbeatInterval = 60000 // 后台降频 } else { ws.options.heartbeatInterval = 20000 if (ws.ws.readyState !== WebSocket.OPEN) { ws.connect() // 立即重连 } } })
来源:即时通讯网 - 即时通讯开发者社区!
轻量级开源移动端即时通讯框架。
快速入门 / 性能 / 指南 / 提问
轻量级Web端即时通讯框架。
详细介绍 / 精编源码 / 手册教程
移动端实时音视频框架。
详细介绍 / 性能测试 / 安装体验
基于MobileIMSDK的移动IM系统。
详细介绍 / 产品截图 / 安装体验
一套产品级Web端IM系统。
详细介绍 / 产品截图 / 演示视频
一套纯血鸿蒙NEXT产品级IM系统。
详细介绍 / 产品截图 / 安装
精华主题数超过100个。
积极发起、参与各类话题的讨论等,主题、发帖内容较有价值。
连续任职达1年以上的合格正式版主
为论区做出突出贡献的开发者、版主等。
Copyright © 2014-2026 即时通讯网 - 即时通讯开发者社区 / 版本 V4.4
苏州网际时代信息科技有限公司 (苏ICP备16005070号-1)
Processed in 0.156250 second(s), 37 queries , Gzip On.