websocket.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import ReconnectingWebSocket from 'reconnecting-websocket';
  2. import { v4 as uuidv4 } from 'uuid';
  3. import type { Message } from '@/types';
  4. import { performanceLogger } from './logger';
  5. /**
  6. * WebSocket管理器
  7. */
  8. export class WebSocketManager {
  9. private ws: ReconnectingWebSocket | null = null;
  10. private messageHandlers: Map<string, (message: Message) => void> = new Map();
  11. private pingInterval: ReturnType<typeof setInterval> | null = null;
  12. private readonly PING_INTERVAL = 30000; // 30秒
  13. connect(url: string, onConnectionChange?: (connected: boolean) => void): void {
  14. this.ws = new ReconnectingWebSocket(url, [], {
  15. maxRetries: 10,
  16. connectionTimeout: 5000,
  17. maxReconnectionDelay: 10000,
  18. minReconnectionDelay: 1000,
  19. reconnectionDelayGrowFactor: 1.3,
  20. });
  21. this.ws.onopen = () => {
  22. console.log('[WebSocket] 连接已建立');
  23. // 记录WebSocket连接建立时间
  24. performanceLogger.recordWebSocketConnected();
  25. this.startHeartbeat();
  26. if (onConnectionChange) {
  27. onConnectionChange(true);
  28. }
  29. };
  30. this.ws.onmessage = (event) => {
  31. try {
  32. const message: Message = JSON.parse(event.data);
  33. // 简化日志:只记录关键消息类型
  34. if (message.type === 'error') {
  35. console.error(`[WebSocket] [${message.requestId?.substring(0, 8) || ''}] 错误:`, message);
  36. }
  37. this.handleMessage(message);
  38. } catch (error) {
  39. console.error('[WebSocket] 解析消息失败:', error);
  40. }
  41. };
  42. this.ws.onerror = (error) => {
  43. console.error('[WebSocket] 连接错误:', error);
  44. };
  45. this.ws.onclose = (event) => {
  46. console.log(`[WebSocket] 连接已关闭 (code: ${event.code})`);
  47. this.stopHeartbeat();
  48. if (onConnectionChange) {
  49. onConnectionChange(false);
  50. }
  51. };
  52. }
  53. private handleMessage(message: Message): void {
  54. this.messageHandlers.forEach((handler) => {
  55. handler(message);
  56. });
  57. }
  58. onMessage(id: string, handler: (message: Message) => void): void {
  59. this.messageHandlers.set(id, handler);
  60. }
  61. offMessage(id: string): void {
  62. this.messageHandlers.delete(id);
  63. }
  64. send(message: Partial<Message>): void {
  65. if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
  66. console.warn(`[WebSocket] [${message.requestId?.substring(0, 8) || ''}] 发送失败: WebSocket未连接`, message.type);
  67. return;
  68. }
  69. const fullMessage = {
  70. ...message,
  71. timestamp: Date.now(),
  72. };
  73. // 简化日志:只记录错误和取消操作
  74. if (message.type === 'cancel' || message.type === 'error') {
  75. console.log(`[WebSocket] [${message.requestId?.substring(0, 8) || ''}] 发送: ${message.type}`);
  76. }
  77. this.ws.send(JSON.stringify(fullMessage));
  78. }
  79. sendAudioChunk(requestId: string, data: string, chunkIndex: number): void {
  80. this.send({
  81. type: 'audio_chunk',
  82. requestId,
  83. data,
  84. sampleRate: 16000,
  85. channels: 1,
  86. bitDepth: 16,
  87. chunkIndex,
  88. });
  89. }
  90. sendAudioEnd(requestId: string, totalChunks: number, speechDuration?: number): void {
  91. this.send({
  92. type: 'audio_end',
  93. requestId,
  94. totalChunks,
  95. speechDuration,
  96. });
  97. }
  98. sendCancel(requestId: string): void {
  99. this.send({
  100. type: 'cancel',
  101. requestId,
  102. });
  103. }
  104. private startHeartbeat(): void {
  105. this.pingInterval = setInterval(() => {
  106. this.send({
  107. type: 'ping',
  108. requestId: uuidv4(),
  109. });
  110. }, this.PING_INTERVAL);
  111. }
  112. private stopHeartbeat(): void {
  113. if (this.pingInterval) {
  114. clearInterval(this.pingInterval);
  115. this.pingInterval = null;
  116. }
  117. }
  118. disconnect(): void {
  119. this.stopHeartbeat();
  120. if (this.ws) {
  121. this.ws.close();
  122. this.ws = null;
  123. }
  124. this.messageHandlers.clear();
  125. }
  126. }