Files
template-10556/src/utils/websocket.ts
2025-07-09 13:47:01 +08:00

185 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* WebSocket连接管理类
*/
export class WebSocketManager {
private ws: WebSocket | null = null;
private url: string;
private reconnectAttempts = 0;
private maxReconnectAttempts = 5;
private reconnectInterval = 3000;
private heartbeatInterval: NodeJS.Timeout | null = null;
private heartbeatTimer = 30000; // 30秒心跳
// 事件回调
private onMessageCallback?: (data: any) => void;
private onOpenCallback?: () => void;
private onCloseCallback?: () => void;
private onErrorCallback?: (error: Event) => void;
constructor(url: string) {
this.url = url;
}
/**
* 连接WebSocket
*/
connect(): Promise<void> {
return new Promise((resolve, reject) => {
try {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('WebSocket连接成功');
this.reconnectAttempts = 0;
this.startHeartbeat();
this.onOpenCallback?.();
resolve();
};
this.ws.onmessage = (event) => {
try {
if(event.data !== '连接成功'){
const data = JSON.parse(event.data);
console.log(data,'data>>>')
this.onMessageCallback?.(data);
}
} catch (error) {
console.error('解析WebSocket消息失败:', error);
}
};
this.ws.onclose = () => {
console.log('WebSocket连接关闭');
this.stopHeartbeat();
this.onCloseCallback?.();
this.handleReconnect();
};
this.ws.onerror = (error) => {
console.error('WebSocket连接错误:', error);
this.onErrorCallback?.(error);
reject(error);
};
} catch (error) {
reject(error);
}
});
}
/**
* 发送消息
*/
send(message: any): void {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(typeof message === 'string' ? message : JSON.stringify(message));
} else {
console.warn('WebSocket未连接无法发送消息');
}
}
/**
* 关闭连接
*/
close(): void {
this.stopHeartbeat();
if (this.ws) {
this.ws.close();
this.ws = null;
}
}
/**
* 重连处理
*/
private handleReconnect(): void {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
console.log(`尝试重连WebSocket (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
setTimeout(() => {
this.connect().catch(error => {
console.error('重连失败:', error);
// 继续尝试重连
this.handleReconnect();
});
}, this.reconnectInterval * this.reconnectAttempts); // 递增重连间隔
} else {
console.error('WebSocket重连次数已达上限');
// 重置重连次数,以便后续手动重连
setTimeout(() => {
this.reconnectAttempts = 0;
}, 60000); // 1分钟后重置
}
}
/**
* 开始心跳
*/
private startHeartbeat(): void {
this.heartbeatInterval = setInterval(() => {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type: 'ping' }));
}
}, this.heartbeatTimer);
}
/**
* 停止心跳
*/
private stopHeartbeat(): void {
if (this.heartbeatInterval) {
clearInterval(this.heartbeatInterval);
this.heartbeatInterval = null;
}
}
/**
* 设置消息回调
*/
onMessage(callback: (data: any) => void): void {
this.onMessageCallback = callback;
}
/**
* 设置连接成功回调
*/
onOpen(callback: () => void): void {
this.onOpenCallback = callback;
}
/**
* 设置连接关闭回调
*/
onClose(callback: () => void): void {
this.onCloseCallback = callback;
}
/**
* 设置错误回调
*/
onError(callback: (error: Event) => void): void {
this.onErrorCallback = callback;
}
/**
* 获取连接状态
*/
getReadyState(): number {
return this.ws?.readyState ?? WebSocket.CLOSED;
}
/**
* 是否已连接
*/
isConnected(): boolean {
return this.ws?.readyState === WebSocket.OPEN;
}
}
/**
* 创建WebSocket连接
*/
export function createWebSocket(url: string): WebSocketManager {
return new WebSocketManager(url);
}