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文件中的变量
|
// 获取.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组件中
|
// 更新 access_token 的逻辑 - 用于React组件中
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { TFunction } from 'i18next';
|
|||||||
import { Platform } from 'react-native';
|
import { Platform } from 'react-native';
|
||||||
|
|
||||||
// 从环境变量或默认值中定义 WebSocket 端点
|
// 从环境变量或默认值中定义 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';
|
export type WebSocketStatus = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';
|
||||||
|
|
||||||
@ -34,6 +34,8 @@ class WebSocketManager {
|
|||||||
private readonly reconnectInterval = 1000; // 初始重连间隔为1秒
|
private readonly reconnectInterval = 1000; // 初始重连间隔为1秒
|
||||||
private pingIntervalId: ReturnType<typeof setInterval> | null = null;
|
private pingIntervalId: ReturnType<typeof setInterval> | null = null;
|
||||||
private readonly pingInterval = 30000; // 30秒发送一次心跳
|
private readonly pingInterval = 30000; // 30秒发送一次心跳
|
||||||
|
private isConnecting = false; // 防止并发重复连接
|
||||||
|
private connectTimeoutId: ReturnType<typeof setTimeout> | null = null; // 连接超时
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// 这是一个单例类,连接通过调用 connect() 方法来启动
|
// 这是一个单例类,连接通过调用 connect() 方法来启动
|
||||||
@ -51,66 +53,91 @@ class WebSocketManager {
|
|||||||
* 会自动获取并使用存储的认证 token。
|
* 会自动获取并使用存储的认证 token。
|
||||||
*/
|
*/
|
||||||
public async connect() {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
this.isConnecting = true;
|
||||||
this.setStatus('connecting');
|
this.setStatus('connecting');
|
||||||
|
|
||||||
let token = "";
|
let token = "";
|
||||||
|
try {
|
||||||
if (Platform.OS === 'web') {
|
if (Platform.OS === 'web') {
|
||||||
token = localStorage.getItem('token') || "";
|
token = localStorage.getItem('token') || "";
|
||||||
} else {
|
} else {
|
||||||
token = await SecureStore.getItemAsync('token') || "";
|
token = await SecureStore.getItemAsync('token') || "";
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('WebSocket: 获取 token 失败:', e);
|
||||||
|
}
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
console.error('WebSocket: 未找到认证 token,无法连接。');
|
console.error('WebSocket: 未找到认证 token,无法连接。');
|
||||||
|
this.isConnecting = false;
|
||||||
this.setStatus('disconnected');
|
this.setStatus('disconnected');
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
console.log('WebSocket: 认证 token:', token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `${WEBSOCKET_ENDPOINT}?token=${token}`;
|
const url = `${WEBSOCKET_ENDPOINT}?token=${encodeURIComponent(token)}`;
|
||||||
console.log('WebSocket: 连接 URL:', url);
|
console.log('WebSocket: 开始连接到服务器');
|
||||||
this.ws = new WebSocket(url);
|
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');
|
console.log('WebSocket connected');
|
||||||
this.setStatus('connected');
|
this.setStatus('connected');
|
||||||
this.reconnectAttempts = 0; // 重置重连尝试次数
|
this.reconnectAttempts = 0; // 重置重连尝试次数
|
||||||
|
this.isConnecting = false;
|
||||||
this.startPing();
|
this.startPing();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ws.onmessage = (event) => {
|
ws.onmessage = (event) => {
|
||||||
try {
|
try {
|
||||||
const message: WsMessage = JSON.parse(event.data);
|
const message: WsMessage = JSON.parse(event.data);
|
||||||
// console.log('WebSocket received message:', message)
|
|
||||||
// 根据消息类型分发
|
|
||||||
const eventListeners = this.messageListeners.get(message.type);
|
const eventListeners = this.messageListeners.get(message.type);
|
||||||
if (eventListeners) {
|
if (eventListeners) {
|
||||||
eventListeners.forEach(callback => callback(message));
|
eventListeners.forEach(callback => callback(message));
|
||||||
}
|
}
|
||||||
// 可以在这里处理通用的消息,比如 Pong
|
|
||||||
if (message.type === 'Pong') {
|
|
||||||
// console.log('Received Pong');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('处理 WebSocket 消息失败:', 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);
|
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');
|
console.log('WebSocket disconnected');
|
||||||
this.ws = null;
|
this.ws = null;
|
||||||
this.stopPing();
|
this.stopPing();
|
||||||
|
this.isConnecting = false;
|
||||||
// 只有在不是手动断开连接时才重连
|
// 只有在不是手动断开连接时才重连
|
||||||
if (this.status !== 'disconnected') {
|
if (this.status !== 'disconnected') {
|
||||||
this.setStatus('reconnecting');
|
this.setStatus('reconnecting');
|
||||||
@ -141,8 +168,8 @@ class WebSocketManager {
|
|||||||
* @param message 要发送的消息对象,必须包含 type 字段。
|
* @param message 要发送的消息对象,必须包含 type 字段。
|
||||||
*/
|
*/
|
||||||
public send(message: WsMessage) {
|
public send(message: WsMessage) {
|
||||||
if (this.status !== 'connected' || !this.ws) {
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
||||||
console.error('WebSocket 未连接,无法发送消息。');
|
console.error('WebSocket 未连接或未就绪,无法发送消息。');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.ws.send(JSON.stringify(message));
|
this.ws.send(JSON.stringify(message));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user