enhance: ws util健壮性优化
All checks were successful
Dev Deploy / Explore-Gitea-Actions (push) Successful in 31s

This commit is contained in:
Junhui Chen 2025-08-09 15:17:22 +08:00
parent c89b1df10a
commit 665ccc6132
2 changed files with 54 additions and 27 deletions

View File

@ -25,7 +25,7 @@ export interface PagedResult<T> {
// 获取.env文件中的变量
export const API_ENDPOINT = Constants.expoConfig?.extra?.API_ENDPOINT || "http://192.168.31.16:31646/api";
export const API_ENDPOINT = process.env.EXPO_PUBLIC_API_ENDPOINT || Constants.expoConfig?.extra?.API_ENDPOINT;
// 更新 access_token 的逻辑 - 用于React组件中

View File

@ -4,7 +4,7 @@ import { TFunction } from 'i18next';
import { Platform } from 'react-native';
// 从环境变量或默认值中定义 WebSocket 端点
export const WEBSOCKET_ENDPOINT = Constants.expoConfig?.extra?.WEBSOCKET_ENDPOINT || "ws://192.168.31.16:31646/ws/chat";
export const WEBSOCKET_ENDPOINT = process.env.EXPO_PUBLIC_WEBSOCKET_ENDPOINT || Constants.expoConfig?.extra?.WEBSOCKET_ENDPOINT;
export type WebSocketStatus = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';
@ -34,6 +34,8 @@ class WebSocketManager {
private readonly reconnectInterval = 1000; // 初始重连间隔为1秒
private pingIntervalId: ReturnType<typeof setInterval> | null = null;
private readonly pingInterval = 30000; // 30秒发送一次心跳
private isConnecting = false; // 防止并发重复连接
private connectTimeoutId: ReturnType<typeof setTimeout> | null = null; // 连接超时
constructor() {
// 这是一个单例类,连接通过调用 connect() 方法来启动
@ -51,66 +53,91 @@ class WebSocketManager {
* 使 token
*/
public async connect() {
if (this.ws && (this.status === 'connected' || this.status === 'connecting')) {
if (this.status === 'connected' || this.status === 'connecting') {
// 已连或正在连,直接返回(基于状态的幂等)
if (this.status === 'connected' || this.status === 'connecting' || this.isConnecting) {
return;
}
}
this.isConnecting = true;
this.setStatus('connecting');
let token = "";
try {
if (Platform.OS === 'web') {
token = localStorage.getItem('token') || "";
} else {
token = await SecureStore.getItemAsync('token') || "";
}
} catch (e) {
console.error('WebSocket: 获取 token 失败:', e);
}
if (!token) {
console.error('WebSocket: 未找到认证 token无法连接。');
this.isConnecting = false;
this.setStatus('disconnected');
return;
} else {
console.log('WebSocket: 认证 token:', token);
}
const url = `${WEBSOCKET_ENDPOINT}?token=${token}`;
console.log('WebSocket: 连接 URL:', url);
this.ws = new WebSocket(url);
const url = `${WEBSOCKET_ENDPOINT}?token=${encodeURIComponent(token)}`;
console.log('WebSocket: 开始连接到服务器');
const ws = new WebSocket(url);
this.ws = ws;
this.ws.onopen = () => {
// 连接超时15s
if (this.connectTimeoutId) {
clearTimeout(this.connectTimeoutId);
this.connectTimeoutId = null;
}
this.connectTimeoutId = setTimeout(() => {
if (this.ws === ws && ws.readyState !== WebSocket.OPEN) {
try { ws.close(); } catch { /* noop */ }
}
}, 15000);
ws.onopen = () => {
if (this.connectTimeoutId) {
clearTimeout(this.connectTimeoutId);
this.connectTimeoutId = null;
}
console.log('WebSocket connected');
this.setStatus('connected');
this.reconnectAttempts = 0; // 重置重连尝试次数
this.isConnecting = false;
this.startPing();
};
this.ws.onmessage = (event) => {
ws.onmessage = (event) => {
try {
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));
}
// 可以在这里处理通用的消息,比如 Pong
if (message.type === 'Pong') {
// console.log('Received Pong');
}
} catch (error) {
console.error('处理 WebSocket 消息失败:', error);
}
};
this.ws.onerror = (error) => {
ws.onerror = (error) => {
if (this.connectTimeoutId) {
clearTimeout(this.connectTimeoutId);
this.connectTimeoutId = null;
}
console.error('WebSocket 发生错误:', error);
this.stopPing();
this.isConnecting = false;
};
this.ws.onclose = () => {
ws.onclose = () => {
if (this.connectTimeoutId) {
clearTimeout(this.connectTimeoutId);
this.connectTimeoutId = null;
}
console.log('WebSocket disconnected');
this.ws = null;
this.stopPing();
this.isConnecting = false;
// 只有在不是手动断开连接时才重连
if (this.status !== 'disconnected') {
this.setStatus('reconnecting');
@ -141,8 +168,8 @@ class WebSocketManager {
* @param message type
*/
public send(message: WsMessage) {
if (this.status !== 'connected' || !this.ws) {
console.error('WebSocket 未连接,无法发送消息。');
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
console.error('WebSocket 未连接或未就绪,无法发送消息。');
return;
}
this.ws.send(JSON.stringify(message));