feat: 添加ws调试信息 打印日志

This commit is contained in:
徐旦 2025-06-29 19:18:59 +08:00
parent 2bae00323c
commit 0558c9aa1b

View File

@ -9,6 +9,33 @@ const WS_CONFIG = {
heartbeatResponseType: 'pong', // 心跳响应类型
};
// WebSocket关闭码说明
const WS_CLOSE_CODES: Record<number, string> = {
1000: '正常关闭',
1001: '端点离开(如页面关闭)',
1002: '协议错误',
1003: '接收到不支持的数据类型',
1004: '保留码',
1005: '未收到预期的状态码',
1006: '连接异常关闭',
1007: '接收到无效的frame payload数据',
1008: '策略违规(服务器主动关闭)',
1009: '消息过大',
1010: '扩展协商失败',
1011: '服务器遇到意外情况',
1012: '服务重启',
1013: '稍后重试',
1014: '网关超时',
1015: 'TLS握手失败',
};
// 获取关闭原因描述
function getCloseReasonDescription(code: number, reason: string): string {
const codeDescription = WS_CLOSE_CODES[code] || '未知关闭码';
const reasonText = reason ? ` (原因: ${reason})` : '';
return `${code} - ${codeDescription}${reasonText}`;
}
// 增强的WebSocket包装器
class EnhancedWebSocket {
private ws: WebSocket;
@ -24,10 +51,16 @@ class EnhancedWebSocket {
private userOnClose: ((event: CloseEvent) => void) | null = null;
private userOnError: ((event: Event) => void) | null = null;
private userOnOpen: ((event: Event) => void) | null = null;
private connectionStartTime: number = 0;
private lastHeartbeatTime: number = 0;
private heartbeatSentCount: number = 0;
private heartbeatReceivedCount: number = 0;
constructor(path: string, baseUrl: string) {
this.path = path;
this.baseUrl = baseUrl;
this.connectionStartTime = Date.now();
console.log(`🔗 开始创建WebSocket连接: ${this.path}, 基础URL: ${baseUrl}`);
this.ws = new WebSocket(baseUrl + path);
this.setupHandlers();
}
@ -35,13 +68,19 @@ class EnhancedWebSocket {
// 设置事件处理器
private setupHandlers(): void {
this.ws.onopen = (event) => {
console.log(`WebSocket连接已建立: ${this.path}`);
const connectionDuration = Date.now() - this.connectionStartTime;
console.log(`✅ WebSocket连接已建立: ${this.path}, 耗时: ${connectionDuration}ms`);
this.reconnectAttempts = 0;
this.heartbeatSentCount = 0;
this.heartbeatReceivedCount = 0;
this.clearReconnectTimer();
// 🔧 优化:连接建立后立即发送一次心跳,然后开始定期心跳
if (this.ws.readyState === WebSocket.OPEN) {
console.log(`💓 连接建立后发送初始心跳: ${this.path}`);
this.ws.send(WS_CONFIG.heartbeatMessage);
this.heartbeatSentCount++;
this.lastHeartbeatTime = Date.now();
this.startHeartbeatTimeout();
}
this.startHeartbeat();
@ -72,12 +111,15 @@ class EnhancedWebSocket {
if (data.type === WS_CONFIG.heartbeatResponseType) {
isHeartbeatResponse = true;
}
} catch (e) {
} catch {
// JSON解析失败不是JSON格式的心跳响应
}
}
if (isHeartbeatResponse) {
this.heartbeatReceivedCount++;
const responseTime = Date.now() - this.lastHeartbeatTime;
console.log(`💗 收到心跳响应: ${this.path}, 响应时间: ${responseTime}ms, 已发送: ${this.heartbeatSentCount}, 已接收: ${this.heartbeatReceivedCount}`);
// 心跳响应,不传递给业务代码
return;
}
@ -89,7 +131,19 @@ class EnhancedWebSocket {
};
this.ws.onclose = (event) => {
console.log(`WebSocket连接关闭: ${this.path}`, event.code, event.reason);
const connectionDuration = Date.now() - this.connectionStartTime;
const closeReason = getCloseReasonDescription(event.code, event.reason);
console.log(`❌ WebSocket连接关闭: ${this.path}`);
console.log(` └─ 关闭原因: ${closeReason}`);
console.log(` └─ 连接持续时间: ${connectionDuration}ms`);
console.log(` └─ 心跳统计: 发送${this.heartbeatSentCount}次, 接收${this.heartbeatReceivedCount}`);
console.log(` └─ 是否手动关闭: ${this.isManualClose}`);
console.log(` └─ 是否心跳超时: ${this.isHeartbeatTimeout}`);
// 分析断连原因
this.analyzeDisconnectionReason(event.code, event.reason);
this.stopHeartbeat();
// 先调用业务代码的关闭处理
@ -107,7 +161,9 @@ class EnhancedWebSocket {
};
this.ws.onerror = (event) => {
console.error(`WebSocket连接错误: ${this.path}`, event);
console.error(`🔥 WebSocket连接错误: ${this.path}`, event);
console.log(` └─ 连接状态: ${this.getReadyStateText()}`);
console.log(` └─ 连接URL: ${this.ws.url}`);
this.stopHeartbeat();
if (this.userOnError) {
@ -116,18 +172,81 @@ class EnhancedWebSocket {
};
}
// 分析断连原因
private analyzeDisconnectionReason(code: number, reason: string): void {
console.log(`🔍 断连原因分析: ${this.path}`);
if (reason) {
console.log(` └─ 服务器提供的关闭原因: ${reason}`);
}
switch (code) {
case 1000:
console.log(' └─ 正常关闭,可能是服务器主动关闭或客户端主动关闭');
break;
case 1001:
console.log(' └─ 端点离开,可能是页面关闭或刷新');
break;
case 1006:
console.log(' └─ 连接异常关闭,可能是网络问题或服务器崩溃');
break;
case 1008:
console.log(' └─ 策略违规,服务器主动关闭连接');
console.log(' └─ 可能原因: 1) 服务器负载过高 2) 连接频率过快 3) 服务器重启 4) 业务逻辑限制');
break;
case 1011:
console.log(' └─ 服务器遇到意外情况,可能是服务器内部错误');
break;
case 1012:
console.log(' └─ 服务重启,服务器正在重启');
break;
case 1013:
console.log(' └─ 稍后重试,服务器暂时无法处理请求');
break;
default:
if (code >= 3000 && code <= 3999) {
console.log(' └─ 应用程序定义的关闭码,可能是业务逻辑关闭');
} else if (code >= 4000 && code <= 4999) {
console.log(' └─ 私有关闭码,可能是框架或库定义的关闭原因');
} else {
console.log(' └─ 未知关闭码,需要进一步调查');
}
}
// 心跳统计分析
if (this.heartbeatSentCount > this.heartbeatReceivedCount) {
const missedHeartbeats = this.heartbeatSentCount - this.heartbeatReceivedCount;
console.log(` └─ 心跳分析: 有${missedHeartbeats}个心跳未收到响应,可能存在网络延迟或服务器响应问题`);
}
}
// 获取连接状态文本
private getReadyStateText(): string {
switch (this.ws.readyState) {
case WebSocket.CONNECTING: return 'CONNECTING(0)';
case WebSocket.OPEN: return 'OPEN(1)';
case WebSocket.CLOSING: return 'CLOSING(2)';
case WebSocket.CLOSED: return 'CLOSED(3)';
default: return `UNKNOWN(${this.ws.readyState})`;
}
}
// 开始心跳检测
private startHeartbeat(): void {
this.stopHeartbeat();
console.log(`开始心跳检测: ${this.path}, 间隔: ${WS_CONFIG.heartbeatInterval}ms`);
console.log(`💓 开始心跳检测: ${this.path}, 间隔: ${WS_CONFIG.heartbeatInterval}ms`);
this.heartbeatTimer = setInterval(() => {
if (this.ws.readyState === WebSocket.OPEN) {
this.heartbeatSentCount++;
this.lastHeartbeatTime = Date.now();
console.log(`💓 发送心跳: ${this.path} (#${this.heartbeatSentCount})`);
this.ws.send(WS_CONFIG.heartbeatMessage);
// 只有在没有进行超时检测时才设置新的超时检测
if (!this.heartbeatTimeoutTimer) {
this.startHeartbeatTimeout();
}
} else {
console.log(`⚠️ 心跳检测时发现连接状态异常: ${this.path}, 状态: ${this.getReadyStateText()}`);
}
}, WS_CONFIG.heartbeatInterval);
}
@ -135,6 +254,7 @@ class EnhancedWebSocket {
// 停止心跳检测
private stopHeartbeat(): void {
if (this.heartbeatTimer) {
console.log(`🛑 停止心跳检测: ${this.path}`);
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = undefined;
}
@ -145,7 +265,8 @@ class EnhancedWebSocket {
private startHeartbeatTimeout(): void {
// 不再自动清除,只在收到响应时清除
this.heartbeatTimeoutTimer = setTimeout(() => {
console.log(`心跳响应超时: ${this.path}, ${WS_CONFIG.heartbeatTimeout}ms内未收到响应主动断开连接`);
console.log(`💔 心跳响应超时: ${this.path}, ${WS_CONFIG.heartbeatTimeout}ms内未收到响应主动断开连接`);
console.log(` └─ 心跳统计: 发送${this.heartbeatSentCount}次, 接收${this.heartbeatReceivedCount}`);
// 设置心跳超时标志,触发重连
this.isHeartbeatTimeout = true;
this.ws.close(1000, 'Heartbeat timeout'); // 使用正常关闭状态码,通过标志来判断是否重连
@ -163,7 +284,7 @@ class EnhancedWebSocket {
// 安排重连
private scheduleReconnect(): void {
if (this.isManualClose || this.reconnectAttempts >= WS_CONFIG.maxReconnectAttempts) {
console.log(`停止重连: ${this.path}, 手动关闭: ${this.isManualClose}, 重连次数: ${this.reconnectAttempts}`);
console.log(`🚫 停止重连: ${this.path}, 手动关闭: ${this.isManualClose}, 重连次数: ${this.reconnectAttempts}`);
return;
}
@ -176,7 +297,7 @@ class EnhancedWebSocket {
);
console.log(
`WebSocket将在${delay}ms后重连: ${this.path} (${this.reconnectAttempts}/${WS_CONFIG.maxReconnectAttempts})`,
`🔄 WebSocket将在${delay}ms后重连: ${this.path} (${this.reconnectAttempts}/${WS_CONFIG.maxReconnectAttempts})`,
);
this.reconnectTimer = setTimeout(() => {
@ -188,7 +309,8 @@ class EnhancedWebSocket {
private reconnect(): void {
if (this.isManualClose) return;
console.log(`WebSocket重连尝试: ${this.path} (${this.reconnectAttempts}/${WS_CONFIG.maxReconnectAttempts})`);
console.log(`🔄 WebSocket重连尝试: ${this.path} (${this.reconnectAttempts}/${WS_CONFIG.maxReconnectAttempts})`);
this.connectionStartTime = Date.now();
// 创建新的WebSocket连接
this.ws = new WebSocket(this.baseUrl + this.path);
@ -271,7 +393,7 @@ class EnhancedWebSocket {
}
close(code?: number, reason?: string): void {
console.log(`手动关闭WebSocket: ${this.path}`);
console.log(`👋 手动关闭WebSocket: ${this.path}`);
this.isManualClose = true;
this.isHeartbeatTimeout = false; // 手动关闭时重置心跳超时标志
this.stopHeartbeat();
@ -281,7 +403,7 @@ class EnhancedWebSocket {
addEventListener<K extends keyof WebSocketEventMap>(
type: K,
listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,
listener: (this: WebSocket, ev: WebSocketEventMap[K]) => void,
options?: boolean | AddEventListenerOptions,
): void {
this.ws.addEventListener(type, listener, options);
@ -289,7 +411,7 @@ class EnhancedWebSocket {
removeEventListener<K extends keyof WebSocketEventMap>(
type: K,
listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,
listener: (this: WebSocket, ev: WebSocketEventMap[K]) => void,
options?: boolean | EventListenerOptions,
): void {
this.ws.removeEventListener(type, listener, options);
@ -313,7 +435,7 @@ class EnhancedWebSocket {
function create(path: string): Promise<WebSocket> {
const baseUrl = import.meta.env.ENV_WEBSOCKET_BASE ?? '';
const ws = new EnhancedWebSocket(path, baseUrl) as any;
const ws = new EnhancedWebSocket(path, baseUrl) as WebSocket;
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
@ -326,9 +448,9 @@ function create(path: string): Promise<WebSocket> {
resolve(ws);
});
ws.addEventListener('error', (e: any) => {
ws.addEventListener('error', (error: Event) => {
clearTimeout(timeout);
reject(e);
reject(error);
});
});
}