enhance: ws util健壮性优化
All checks were successful
Dev Deploy / Explore-Gitea-Actions (push) Successful in 31s
All checks were successful
Dev Deploy / Explore-Gitea-Actions (push) Successful in 31s
This commit is contained in:
parent
c89b1df10a
commit
665ccc6132
@ -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组件中
|
||||
|
||||
@ -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));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user