From ac80003c5d6614a8aef26022052f2602dc5a5944 Mon Sep 17 00:00:00 2001 From: Junhui Chen Date: Thu, 7 Aug 2025 00:21:39 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/websocket-util.ts | 179 +++++++++++++++++++++++++++++++----------- 1 file changed, 133 insertions(+), 46 deletions(-) diff --git a/lib/websocket-util.ts b/lib/websocket-util.ts index fa68f1c..c7ca8fb 100644 --- a/lib/websocket-util.ts +++ b/lib/websocket-util.ts @@ -51,19 +51,24 @@ class WebSocketManager { * 会自动获取并使用存储的认证 token。 */ public async connect() { + // 防止重复连接 if (this.ws && (this.status === 'connected' || this.status === 'connecting')) { - if (this.status === 'connected' || this.status === 'connecting') { - return; - } + return; } this.setStatus('connecting'); let token = ""; - if (Platform.OS === 'web') { - token = localStorage.getItem('token') || ""; - } else { - token = await SecureStore.getItemAsync('token') || ""; + try { + if (Platform.OS === 'web') { + token = localStorage.getItem('token') || ""; + } else { + token = await SecureStore.getItemAsync('token') || ""; + } + } catch (error) { + console.error('获取认证 token 时出错:', error); + this.setStatus('disconnected'); + return; } if (!token) { @@ -74,9 +79,23 @@ class WebSocketManager { console.log('WebSocket: 认证 token:', token); } + // 检查 WebSocket 端点是否已定义 + if (!WEBSOCKET_ENDPOINT) { + console.error('WebSocket: 未定义端点 URL。'); + this.setStatus('disconnected'); + return; + } + const url = `${WEBSOCKET_ENDPOINT}?token=${token}`; console.log('WebSocket: 连接 URL:', url); - this.ws = new WebSocket(url); + + try { + this.ws = new WebSocket(url); + } catch (error) { + console.error('创建 WebSocket 连接时出错:', error); + this.setStatus('disconnected'); + return; + } this.ws.onopen = () => { console.log('WebSocket connected'); @@ -87,12 +106,24 @@ class WebSocketManager { this.ws.onmessage = (event) => { try { + // 检查事件数据是否存在 + if (!event.data) { + console.warn('WebSocket received empty message'); + return; + } + const message: WsMessage = JSON.parse(event.data); // console.log('WebSocket received message:', message) // 根据消息类型分发 const eventListeners = this.messageListeners.get(message.type); if (eventListeners) { - eventListeners.forEach(callback => callback(message)); + eventListeners.forEach(callback => { + try { + callback(message); + } catch (error) { + console.error(`处理消息类型 ${message.type} 时出错:`, error); + } + }); } // 可以在这里处理通用的消息,比如 Pong if (message.type === 'Pong') { @@ -104,7 +135,9 @@ class WebSocketManager { }; this.ws.onerror = (error) => { - console.error('WebSocket 发生错误:', error); + console.error('WebSocket error:', error); + this.setStatus('disconnected'); + this.handleReconnect(); }; this.ws.onclose = () => { @@ -123,15 +156,20 @@ class WebSocketManager { * 处理自动重连逻辑,使用指数退避策略。 */ private handleReconnect() { - if (this.reconnectAttempts < this.maxReconnectAttempts) { - this.reconnectAttempts++; - const delay = this.reconnectInterval * Math.pow(2, this.reconnectAttempts - 1); - console.log(`${delay / 1000}秒后尝试重新连接 (第 ${this.reconnectAttempts} 次)...`); - setTimeout(() => { - this.connect(); - }, delay); - } else { - console.error('WebSocket 重连失败,已达到最大尝试次数。'); + try { + if (this.reconnectAttempts < this.maxReconnectAttempts) { + this.reconnectAttempts++; + const delay = this.reconnectInterval * Math.pow(2, this.reconnectAttempts - 1); + console.log(`${delay / 1000}秒后尝试重新连接 (第 ${this.reconnectAttempts} 次)...`); + setTimeout(() => { + this.connect(); + }, delay); + } else { + console.error('WebSocket 重连失败,已达到最大尝试次数。'); + this.setStatus('disconnected'); + } + } catch (error) { + console.error('处理 WebSocket 重连时出错:', error); this.setStatus('disconnected'); } } @@ -145,7 +183,13 @@ class WebSocketManager { console.error('WebSocket 未连接,无法发送消息。'); return; } - this.ws.send(JSON.stringify(message)); + + try { + const messageString = JSON.stringify(message); + this.ws.send(messageString); + } catch (error) { + console.error('发送 WebSocket 消息时出错:', error); + } } /** @@ -154,10 +198,14 @@ class WebSocketManager { * @param callback 收到消息时的回调函数。 */ public subscribe(type: WsMessage['type'], callback: (message: WsMessage) => void) { - if (!this.messageListeners.has(type)) { - this.messageListeners.set(type, new Set()); + try { + if (!this.messageListeners.has(type)) { + this.messageListeners.set(type, new Set()); + } + this.messageListeners.get(type)?.add(callback); + } catch (error) { + console.error(`订阅消息类型 ${type} 时出错:`, error); } - this.messageListeners.get(type)?.add(callback); } /** @@ -166,12 +214,16 @@ class WebSocketManager { * @param callback 要移除的回调函数。 */ public unsubscribe(type: WsMessage['type'], callback: (message: WsMessage) => void) { - const eventListeners = this.messageListeners.get(type); - if (eventListeners) { - eventListeners.delete(callback); - if (eventListeners.size === 0) { - this.messageListeners.delete(type); + try { + const eventListeners = this.messageListeners.get(type); + if (eventListeners) { + eventListeners.delete(callback); + if (eventListeners.size === 0) { + this.messageListeners.delete(type); + } } + } catch (error) { + console.error(`取消订阅消息类型 ${type} 时出错:`, error); } } @@ -179,47 +231,82 @@ class WebSocketManager { * 手动断开 WebSocket 连接。 */ public disconnect() { - this.setStatus('disconnected'); - if (this.ws) { - this.ws.close(); + try { + this.setStatus('disconnected'); + if (this.ws) { + this.ws.close(); + } + } catch (error) { + console.error('断开 WebSocket 连接时出错:', error); + } finally { + this.stopPing(); } - this.stopPing(); } private setStatus(status: WebSocketStatus) { - if (this.status !== status) { - this.status = status; - this.statusListeners.forEach(listener => listener(status)); + try { + if (this.status !== status) { + this.status = status; + this.statusListeners.forEach(listener => { + try { + listener(status); + } catch (error) { + console.error('调用状态监听器时出错:', error); + } + }); + } + } catch (error) { + console.error('设置 WebSocket 状态时出错:', error); } } public subscribeStatus(listener: StatusListener) { - this.statusListeners.add(listener); - // Immediately invoke with current status - listener(this.status); + try { + this.statusListeners.add(listener); + // Immediately invoke with current status + try { + listener(this.status); + } catch (error) { + console.error('调用状态监听器时出错:', error); + } + } catch (error) { + console.error('订阅状态监听器时出错:', error); + } } public unsubscribeStatus(listener: StatusListener) { - this.statusListeners.delete(listener); + try { + this.statusListeners.delete(listener); + } catch (error) { + console.error('取消订阅状态监听器时出错:', error); + } } /** * 启动心跳机制。 */ private startPing() { - this.stopPing(); // 先停止任何可能正在运行的计时器 - this.pingIntervalId = setInterval(() => { - this.send({ type: 'Ping' }); - }, this.pingInterval); + try { + this.stopPing(); // 先停止任何可能正在运行的计时器 + this.pingIntervalId = setInterval(() => { + this.send({ type: 'Ping' }); + }, this.pingInterval); + } catch (error) { + console.error('启动心跳机制时出错:', error); + } } /** * 停止心跳机制。 */ private stopPing() { - if (this.pingIntervalId) { - clearInterval(this.pingIntervalId); - this.pingIntervalId = null; + try { + if (this.pingIntervalId) { + clearInterval(this.pingIntervalId); + this.pingIntervalId = null; + } + } catch (error) { + console.error('停止心跳机制时出错:', error); } } }