webSocket.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // WebSocket 工具类
  2. type MessageCallback = (message: string) => void;
  3. type ErrorCallback = (error: Event) => void;
  4. let ws: WebSocket | null = null;
  5. let reconnectTimer: NodeJS.Timeout | null = null;
  6. let heartbeatTimer: NodeJS.Timeout | null = null;
  7. let reconnectAttempts = 0;
  8. const maxReconnectAttempts = 5;
  9. const reconnectDelay = 3000; // 3秒
  10. const heartbeatInterval = 30000; // 30秒心跳
  11. /**
  12. * 连接 WebSocket
  13. * @param url WebSocket 地址
  14. * @param params 连接参数(会作为查询参数)
  15. * @param onMessage 消息回调
  16. * @param onError 错误回调
  17. */
  18. export const connectWebsocket = (
  19. url: string,
  20. params: Record<string, any> = {},
  21. onMessage: MessageCallback,
  22. onError?: ErrorCallback
  23. ) => {
  24. // 如果已经连接,先关闭
  25. if (ws && ws.readyState === WebSocket.OPEN) {
  26. closeWebsocket();
  27. }
  28. // 构建带参数的 URL
  29. const paramString = new URLSearchParams(params).toString();
  30. const fullUrl = paramString ? `${url}?${paramString}` : url;
  31. try {
  32. ws = new WebSocket(fullUrl);
  33. ws.onopen = () => {
  34. console.log('WebSocket 连接成功');
  35. reconnectAttempts = 0;
  36. // 启动心跳
  37. startHeartbeat();
  38. };
  39. ws.onmessage = (event) => {
  40. const message = event.data;
  41. // 处理心跳消息
  42. if (message === 'heartbeat' || message === 'ping') {
  43. // 响应心跳
  44. if (ws && ws.readyState === WebSocket.OPEN) {
  45. ws.send('pong');
  46. }
  47. return;
  48. }
  49. // 调用消息回调
  50. onMessage(message);
  51. };
  52. ws.onerror = (error) => {
  53. console.error('WebSocket 错误:', error);
  54. if (onError) {
  55. onError(error);
  56. }
  57. };
  58. ws.onclose = () => {
  59. console.log('WebSocket 连接关闭');
  60. stopHeartbeat();
  61. // 尝试重连
  62. if (reconnectAttempts < maxReconnectAttempts) {
  63. reconnectAttempts++;
  64. console.log(`尝试重连 (${reconnectAttempts}/${maxReconnectAttempts})...`);
  65. reconnectTimer = setTimeout(() => {
  66. connectWebsocket(url, params, onMessage, onError);
  67. }, reconnectDelay);
  68. } else {
  69. console.error('WebSocket 重连次数已达上限,停止重连');
  70. }
  71. };
  72. } catch (error) {
  73. console.error('WebSocket 连接失败:', error);
  74. if (onError) {
  75. onError(error as Event);
  76. }
  77. }
  78. };
  79. /**
  80. * 关闭 WebSocket 连接
  81. */
  82. export const closeWebsocket = () => {
  83. if (reconnectTimer) {
  84. clearTimeout(reconnectTimer);
  85. reconnectTimer = null;
  86. }
  87. stopHeartbeat();
  88. if (ws) {
  89. ws.onopen = null;
  90. ws.onmessage = null;
  91. ws.onerror = null;
  92. ws.onclose = null;
  93. if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
  94. ws.close();
  95. }
  96. ws = null;
  97. }
  98. reconnectAttempts = 0;
  99. console.log('WebSocket 已关闭');
  100. };
  101. /**
  102. * 启动心跳
  103. */
  104. const startHeartbeat = () => {
  105. stopHeartbeat();
  106. heartbeatTimer = setInterval(() => {
  107. if (ws && ws.readyState === WebSocket.OPEN) {
  108. ws.send('heartbeat');
  109. }
  110. }, heartbeatInterval);
  111. };
  112. /**
  113. * 停止心跳
  114. */
  115. const stopHeartbeat = () => {
  116. if (heartbeatTimer) {
  117. clearInterval(heartbeatTimer);
  118. heartbeatTimer = null;
  119. }
  120. };
  121. /**
  122. * 检查 WebSocket 连接状态
  123. */
  124. export const isWebSocketConnected = (): boolean => {
  125. return ws !== null && ws.readyState === WebSocket.OPEN;
  126. };