New file |
| | |
| | | /** |
| | | * UniApp WebSocket封装类(局部使用版本) |
| | | * 功能: |
| | | * 1. 自动重连 |
| | | * 2. 心跳检测 |
| | | * 3. 消息发送队列 |
| | | */ |
| | | class WebSocketUtil { |
| | | constructor(url, options = {}) { |
| | | // WebSocket服务地址 |
| | | this.url = url; |
| | | // 配置选项 |
| | | this.options = Object.assign( |
| | | { |
| | | // 心跳检测间隔(毫秒) |
| | | heartbeatInterval: 30000, |
| | | // 重连间隔(毫秒) |
| | | reconnectInterval: 3000, |
| | | // 最大重连次数,-1表示无限重连 |
| | | maxReconnectAttempts: -1, |
| | | // 心跳消息内容 |
| | | heartbeatMessage: JSON.stringify({ to: 'heartbeat' }), |
| | | // 消息回调函数 |
| | | onMessage: null, |
| | | // 连接打开回调 |
| | | onOpen: null, |
| | | // 连接关闭回调 |
| | | onClose: null, |
| | | // 连接错误回调 |
| | | onError: null, |
| | | // 重连回调 |
| | | onReconnect: null |
| | | }, |
| | | options |
| | | ); |
| | | |
| | | // 内部状态 |
| | | this.socketTask = null; // WebSocket实例 |
| | | this.isConnected = false; // 连接状态 |
| | | this.isConnecting = false; // 是否正在连接中 |
| | | this.reconnectAttempts = 0; // 重连尝试次数 |
| | | this.heartbeatTimer = null; // 心跳定时器 |
| | | this.reconnectTimer = null; // 重连定时器 |
| | | this.messageQueue = []; // 消息队列 |
| | | this.isManualClose = false; // 是否手动关闭 |
| | | } |
| | | |
| | | /** |
| | | * 建立WebSocket连接 |
| | | */ |
| | | connect() { |
| | | if (this.isConnecting) return; |
| | | |
| | | this.isConnecting = true; |
| | | this.socketTask = uni.connectSocket({ |
| | | url: this.url, |
| | | header: this.options.header || {}, |
| | | protocols: this.options.protocols || [], |
| | | success: () => { |
| | | console.log('WebSocket连接创建成功'); |
| | | }, |
| | | fail: (error) => { |
| | | console.error('WebSocket连接创建失败', error); |
| | | this.reconnect(); |
| | | }, |
| | | complete: () => { |
| | | this.isConnecting = false; |
| | | } |
| | | }); |
| | | |
| | | // 监听WebSocket连接打开事件 |
| | | this.socketTask.onOpen((res) => { |
| | | console.log('WebSocket连接已打开', res); |
| | | this.isConnected = true; |
| | | this.reconnectAttempts = 0; |
| | | |
| | | // 清除重连定时器 |
| | | if (this.reconnectTimer) { |
| | | clearTimeout(this.reconnectTimer); |
| | | this.reconnectTimer = null; |
| | | } |
| | | |
| | | // 开始心跳检测 |
| | | this.startHeartbeat(); |
| | | |
| | | // 发送队列中的消息 |
| | | this.flushMessageQueue(); |
| | | |
| | | // 调用回调函数 |
| | | if (typeof this.options.onOpen === 'function') { |
| | | this.options.onOpen(res); |
| | | } |
| | | }); |
| | | |
| | | // 监听WebSocket接收到服务器的消息事件 |
| | | this.socketTask.onMessage((res) => { |
| | | // 调用回调函数 |
| | | if (typeof this.options.onMessage === 'function') { |
| | | this.options.onMessage(res); |
| | | } |
| | | }); |
| | | |
| | | // 监听WebSocket错误事件 |
| | | this.socketTask.onError((error) => { |
| | | console.error('WebSocket发生错误', error); |
| | | this.isConnected = false; |
| | | |
| | | // 调用回调函数 |
| | | if (typeof this.options.onError === 'function') { |
| | | this.options.onError(error); |
| | | } |
| | | |
| | | // 尝试重连 |
| | | if (!this.isManualClose) { |
| | | this.reconnect(); |
| | | } |
| | | }); |
| | | |
| | | // 监听WebSocket关闭事件 |
| | | this.socketTask.onClose((res) => { |
| | | console.log('WebSocket连接已关闭', res); |
| | | this.isConnected = false; |
| | | |
| | | // 停止心跳检测 |
| | | this.stopHeartbeat(); |
| | | |
| | | // 调用回调函数 |
| | | if (typeof this.options.onClose === 'function') { |
| | | this.options.onClose(res); |
| | | } |
| | | |
| | | // 尝试重连 |
| | | if (!this.isManualClose) { |
| | | this.reconnect(); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 重新连接 |
| | | */ |
| | | reconnect() { |
| | | // 如果是手动关闭或已达到最大重连次数,则不再重连 |
| | | if ( |
| | | this.isManualClose || |
| | | (this.options.maxReconnectAttempts !== -1 && |
| | | this.reconnectAttempts >= this.options.maxReconnectAttempts) |
| | | ) { |
| | | return; |
| | | } |
| | | |
| | | // 清除之前的重连定时器 |
| | | if (this.reconnectTimer) { |
| | | clearTimeout(this.reconnectTimer); |
| | | } |
| | | |
| | | // 增加重连次数 |
| | | this.reconnectAttempts++; |
| | | |
| | | console.log(`WebSocket尝试第${this.reconnectAttempts}次重连...`); |
| | | |
| | | // 设置重连定时器 |
| | | this.reconnectTimer = setTimeout(() => { |
| | | this.connect(); |
| | | }, this.options.reconnectInterval); |
| | | |
| | | // 调用回调函数 |
| | | if (typeof this.options.onReconnect === 'function') { |
| | | this.options.onReconnect({ |
| | | attempts: this.reconnectAttempts, |
| | | maxAttempts: this.options.maxReconnectAttempts |
| | | }); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 开始心跳检测 |
| | | */ |
| | | startHeartbeat() { |
| | | // 清除之前的心跳定时器 |
| | | this.stopHeartbeat(); |
| | | |
| | | // 设置新的心跳定时器 |
| | | this.heartbeatTimer = setInterval(() => { |
| | | if (this.isConnected) { |
| | | console.log('WebSocket发送心跳'); |
| | | this.send(this.options.heartbeatMessage); |
| | | } |
| | | }, this.options.heartbeatInterval); |
| | | } |
| | | |
| | | /** |
| | | * 停止心跳检测 |
| | | */ |
| | | stopHeartbeat() { |
| | | if (this.heartbeatTimer) { |
| | | clearInterval(this.heartbeatTimer); |
| | | this.heartbeatTimer = null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 发送消息 |
| | | * @param {String|Object} data 消息内容 |
| | | * @returns {Promise} 发送结果 |
| | | */ |
| | | send(data) { |
| | | return new Promise((resolve, reject) => { |
| | | // 如果消息是对象,则转为JSON字符串 |
| | | if (typeof data === 'object') { |
| | | data = JSON.stringify(data); |
| | | } |
| | | |
| | | // 如果连接已打开,直接发送 |
| | | if (this.isConnected && this.socketTask) { |
| | | this.socketTask.send({ |
| | | data, |
| | | success: (res) => { |
| | | console.log('WebSocket消息发送成功', data); |
| | | resolve(res); |
| | | }, |
| | | fail: (error) => { |
| | | console.error('WebSocket消息发送失败', error); |
| | | reject(error); |
| | | } |
| | | }); |
| | | } else { |
| | | // 否则加入消息队列 |
| | | console.log('WebSocket连接未打开,消息加入队列', data); |
| | | this.messageQueue.push({ |
| | | data, |
| | | resolve, |
| | | reject |
| | | }); |
| | | |
| | | // 如果未连接,则尝试连接 |
| | | if (!this.isConnected && !this.isConnecting) { |
| | | this.connect(); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 发送队列中的消息 |
| | | */ |
| | | flushMessageQueue() { |
| | | if (this.messageQueue.length === 0) return; |
| | | |
| | | console.log(`WebSocket发送队列中的${this.messageQueue.length}条消息`); |
| | | |
| | | // 复制队列并清空原队列 |
| | | const queue = [...this.messageQueue]; |
| | | this.messageQueue = []; |
| | | |
| | | // 发送队列中的消息 |
| | | queue.forEach(item => { |
| | | this.socketTask.send({ |
| | | data: item.data, |
| | | success: (res) => { |
| | | console.log('WebSocket队列消息发送成功', item.data); |
| | | item.resolve(res); |
| | | }, |
| | | fail: (error) => { |
| | | console.error('WebSocket队列消息发送失败', error); |
| | | item.reject(error); |
| | | } |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 关闭WebSocket连接 |
| | | */ |
| | | close() { |
| | | this.isManualClose = true; |
| | | |
| | | // 停止心跳检测 |
| | | this.stopHeartbeat(); |
| | | |
| | | // 清除重连定时器 |
| | | if (this.reconnectTimer) { |
| | | clearTimeout(this.reconnectTimer); |
| | | this.reconnectTimer = null; |
| | | } |
| | | |
| | | // 关闭连接 |
| | | if (this.socketTask && this.isConnected) { |
| | | this.socketTask.close({ |
| | | code: 1000, |
| | | reason: 'Manual close', |
| | | success: () => { |
| | | console.log('WebSocket连接已手动关闭'); |
| | | }, |
| | | fail: (error) => { |
| | | console.error('WebSocket关闭失败', error); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 重置连接状态,允许重新连接 |
| | | */ |
| | | reset() { |
| | | this.isManualClose = false; |
| | | this.reconnectAttempts = 0; |
| | | |
| | | if (!this.isConnected) { |
| | | this.connect(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | export default WebSocketUtil; |