阿里Java面试被问:WebSocket的心跳检测和自动重连实现

一、核心概念与必要性

为什么需要心跳检测和重连?

  • 网络不稳定:移动网络、Wi-Fi切换、代理服务器可能导致连接断开

  • 服务器限制:Nginx/负载均衡器默认30-60秒超时

  • 浏览器限制:部分浏览器标签页休眠时暂停WebSocket

  • 资源清理:服务器需要清理僵尸连接

二、完整实现方案

1. WebSocket管理器类(TypeScript实现)

typescript

复制

下载

复制代码
interface WebSocketConfig {
  url: string;
  protocols?: string | string[];
  reconnectInterval?: number;    // 重连间隔(ms)
  maxReconnectAttempts?: number; // 最大重连次数
  heartbeatInterval?: number;    // 心跳间隔(ms)
  heartbeatTimeout?: number;     // 心跳超时(ms)
}

enum WebSocketState {
  CONNECTING = 0,
  OPEN = 1,
  CLOSING = 2,
  CLOSED = 3
}

class WebSocketManager {
  private ws: WebSocket | null = null;
  private config: Required<WebSocketConfig>;
  private reconnectAttempts = 0;
  private heartbeatTimer: NodeJS.Timeout | null = null;
  private heartbeatTimeoutTimer: NodeJS.Timeout | null = null;
  private reconnectTimer: NodeJS.Timeout | null = null;
  private isManualClose = false; // 是否手动关闭
  
  // 事件监听器
  private listeners = {
    open: [] as ((event: Event) => void)[],
    message: [] as ((data: any) => void)[],
    close: [] as ((event: CloseEvent) => void)[],
    error: [] as ((event: Event) => void)[],
    reconnect: [] as ((attempt: number) => void)[],
  };

  constructor(config: WebSocketConfig) {
    this.config = {
      url: config.url,
      protocols: config.protocols || [],
      reconnectInterval: config.reconnectInterval || 3000,
      maxReconnectAttempts: config.maxReconnectAttempts || 5,
      heartbeatInterval: config.heartbeatInterval || 30000,
      heartbeatTimeout: config.heartbeatTimeout || 10000,
    };
  }

  /**
   * 连接WebSocket
   */
  public connect(): void {
    if (this.ws?.readyState === WebSocketState.OPEN || 
        this.ws?.readyState === WebSocketState.CONNECTING) {
      console.warn('WebSocket is already connecting or connected');
      return;
    }

    this.isManualClose = false;
    
    try {
      this.ws = new WebSocket(this.config.url, this.config.protocols);
      this.setupEventListeners();
    } catch (error) {
      console.error('WebSocket creation error:', error);
      this.handleReconnect();
    }
  }

  /**
   * 设置事件监听器
   */
  private setupEventListeners(): void {
    if (!this.ws) return;

    this.ws.onopen = (event: Event) => {
      console.log('WebSocket connected');
      this.reconnectAttempts = 0;
      this.startHeartbeat();
      this.emit('open', event);
    };

    this.ws.onmessage = (event: MessageEvent) => {
      try {
        const data = JSON.parse(event.data);
        
        // 处理心跳响应
        if (data.type === 'pong' || data.type === 'heartbeat') {
          this.resetHeartbeat();
          return;
        }
        
        this.emit('message', data);
      } catch (error) {
        // 非JSON数据直接传递
        this.emit('message', event.data);
      }
    };

    this.ws.onclose = (event: CloseEvent) => {
      console.log(`WebSocket closed: ${event.code} ${event.reason}`);
      this.cleanupHeartbeat();
      
      // 非手动关闭才重连
      if (!this.isManualClose) {
        this.handleReconnect();
      }
      
      this.emit('close', event);
    };

    this.ws.onerror = (event: Event) => {
      console.error('WebSocket error:', event);
      this.emit('error', event);
      
      // 错误时也尝试重连
      if (!this.isManualClose) {
        this.handleReconnect();
      }
    };
  }

  /**
   * 开始心跳检测
   */
  private startHeartbeat(): void {
    this.cleanupHeartbeat();
    
    this.heartbeatTimer = setInterval(() => {
      if (this.ws?.readyState === WebSocketState.OPEN) {
        this.sendHeartbeat();
        this.startHeartbeatTimeout();
      }
    }, this.config.heartbeatInterval);
  }

  /**
   * 发送心跳包
   */
  private sendHeartbeat(): void {
    if (this.ws?.readyState === WebSocketState.OPEN) {
      const heartbeatMsg = JSON.stringify({
        type: 'ping',
        timestamp: Date.now(),
      });
      this.ws.send(heartbeatMsg);
    }
  }

  /**
   * 开始心跳超时检测
   */
  private startHeartbeatTimeout(): void {
    this.cleanupHeartbeatTimeout();
    
    this.heartbeatTimeoutTimer = setTimeout(() => {
      console.warn('Heartbeat timeout, reconnecting...');
      this.close();
      this.handleReconnect();
    }, this.config.heartbeatTimeout);
  }

  /**
   * 重置心跳(收到响应时调用)
   */
  private resetHeartbeat(): void {
    this.cleanupHeartbeatTimeout();
  }

  /**
   * 清理心跳定时器
   */
  private cleanupHeartbeat(): void {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
      this.heartbeatTimer = null;
    }
    this.cleanupHeartbeatTimeout();
  }

  /**
   * 清理心跳超时定时器
   */
  private cleanupHeartbeatTimeout(): void {
    if (this.heartbeatTimeoutTimer) {
      clearTimeout(this.heartbeatTimeoutTimer);
      this.heartbeatTimeoutTimer = null;
    }
  }

  /**
   * 处理重连逻辑
   */
  private handleReconnect(): void {
    this.cleanupReconnectTimer();
    
    // 达到最大重连次数
    if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
      console.error('Max reconnection attempts reached');
      return;
    }

    this.reconnectAttempts++;
    console.log(`Reconnecting... Attempt ${this.reconnectAttempts}/${this.config.maxReconnectAttempts}`);
    
    this.emit('reconnect', this.reconnectAttempts);

    // 指数退避算法:重连间隔逐渐增加
    const delay = Math.min(
      this.config.reconnectInterval * Math.pow(1.5, this.reconnectAttempts - 1),
      30000 // 最大30秒
    );

    this.reconnectTimer = setTimeout(() => {
      this.connect();
    }, delay);
  }

  /**
   * 清理重连定时器
   */
  private cleanupReconnectTimer(): void {
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
      this.reconnectTimer = null;
    }
  }

  /**
   * 发送消息
   */
  public send(data: any): boolean {
    if (this.ws?.readyState !== WebSocketState.OPEN) {
      console.error('WebSocket is not connected');
      return false;
    }

    try {
      const message = typeof data === 'string' ? data : JSON.stringify(data);
      this.ws.send(message);
      return true;
    } catch (error) {
      console.error('Send message error:', error);
      return false;
    }
  }

  /**
   * 手动关闭连接
   */
  public close(code?: number, reason?: string): void {
    this.isManualClose = true;
    this.cleanupHeartbeat();
    this.cleanupReconnectTimer();
    
    if (this.ws) {
      this.ws.close(code || 1000, reason);
      this.ws = null;
    }
  }

  /**
   * 获取连接状态
   */
  public getState(): WebSocketState {
    return this.ws?.readyState || WebSocketState.CLOSED;
  }

  /**
   * 添加事件监听
   */
  public on<T extends keyof typeof this.listeners>(
    event: T,
    callback: typeof this.listeners[T][number]
  ): void {
    this.listeners[event].push(callback as any);
  }

  /**
   * 移除事件监听
   */
  public off<T extends keyof typeof this.listeners>(
    event: T,
    callback: typeof this.listeners[T][number]
  ): void {
    const index = this.listeners[event].indexOf(callback as any);
    if (index > -1) {
      this.listeners[event].splice(index, 1);
    }
  }

  /**
   * 触发事件
   */
  private emit<T extends keyof typeof this.listeners>(
    event: T,
    ...args: Parameters<typeof this.listeners[T][number]>
  ): void {
    this.listeners[event].forEach(callback => {
      try {
        (callback as any)(...args);
      } catch (error) {
        console.error(`Error in ${event} event handler:`, error);
      }
    });
  }

  /**
   * 销毁实例
   */
  public destroy(): void {
    this.close();
    this.listeners = {
      open: [],
      message: [],
      close: [],
      error: [],
      reconnect: [],
    };
  }
}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

2. React Hook封装(React + TypeScript)

typescript

复制

下载

复制代码
import { useEffect, useRef, useCallback, useState } from 'react';

interface UseWebSocketOptions {
  onMessage?: (data: any) => void;
  onOpen?: (event: Event) => void;
  onClose?: (event: CloseEvent) => void;
  onError?: (event: Event) => void;
  onReconnect?: (attempt: number) => void;
  heartbeatInterval?: number;
  reconnectInterval?: number;
}

export const useWebSocket = (
  url: string,
  options: UseWebSocketOptions = {}
) => {
  const wsManager = useRef<WebSocketManager | null>(null);
  const [isConnected, setIsConnected] = useState(false);
  const [reconnectCount, setReconnectCount] = useState(0);

  const connect = useCallback(() => {
    if (wsManager.current) {
      wsManager.current.destroy();
    }

    wsManager.current = new WebSocketManager({
      url,
      heartbeatInterval: options.heartbeatInterval,
      reconnectInterval: options.reconnectInterval,
    });

    wsManager.current.on('open', (event) => {
      setIsConnected(true);
      setReconnectCount(0);
      options.onOpen?.(event);
    });

    wsManager.current.on('message', (data) => {
      options.onMessage?.(data);
    });

    wsManager.current.on('close', (event) => {
      setIsConnected(false);
      options.onClose?.(event);
    });

    wsManager.current.on('error', (event) => {
      options.onError?.(event);
    });

    wsManager.current.on('reconnect', (attempt) => {
      setReconnectCount(attempt);
      options.onReconnect?.(attempt);
    });

    wsManager.current.connect();
  }, [url, options]);

  const disconnect = useCallback(() => {
    wsManager.current?.close();
    setIsConnected(false);
  }, []);

  const send = useCallback((data: any) => {
    return wsManager.current?.send(data) || false;
  }, []);

  // 自动连接和清理
  useEffect(() => {
    connect();

    return () => {
      wsManager.current?.destroy();
    };
  }, [connect]);

  // 网络状态变化监听
  useEffect(() => {
    const handleOnline = () => {
      console.log('Network online, reconnecting...');
      if (!isConnected) {
        connect();
      }
    };

    const handleOffline = () => {
      console.log('Network offline');
      disconnect();
    };

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, [connect, disconnect, isConnected]);

  return {
    isConnected,
    reconnectCount,
    send,
    connect,
    disconnect,
  };
};

// 使用示例
export const ChatComponent: React.FC = () => {
  const [messages, setMessages] = useState<string[]>([]);
  
  const { isConnected, send } = useWebSocket('wss://api.example.com/ws', {
    onMessage: (data) => {
      setMessages(prev => [...prev, data.content]);
    },
    onReconnect: (attempt) => {
      console.log(`Reconnection attempt ${attempt}`);
    },
  });

  const handleSend = () => {
    send({ type: 'chat', content: 'Hello' });
  };

  return (
    <div>
      <div>Status: {isConnected ? 'Connected' : 'Disconnected'}</div>
      <button onClick={handleSend} disabled={!isConnected}>
        Send Message
      </button>
      <div>
        {messages.map((msg, index) => (
          <div key={index}>{msg}</div>
        ))}
      </div>
    </div>
  );
};

3. 服务器端心跳处理(Node.js)

javascript

复制

下载

复制代码
const WebSocket = require('ws');

class WebSocketServerWithHeartbeat {
  constructor(server) {
    this.wss = new WebSocket.Server({ server });
    this.clients = new Map(); // clientId -> {ws, lastPing, isAlive}
    this.heartbeatInterval = 30000;
    this.maxMissedPings = 3;
    
    this.setup();
  }

  setup() {
    this.wss.on('connection', (ws, request) => {
      const clientId = this.generateClientId();
      
      console.log(`New connection: ${clientId}`);
      
      this.clients.set(clientId, {
        ws,
        lastPing: Date.now(),
        isAlive: true,
      });

      // 发送欢迎消息
      ws.send(JSON.stringify({
        type: 'welcome',
        clientId,
        timestamp: Date.now(),
      }));

      // 消息处理
      ws.on('message', (data) => {
        try {
          const message = JSON.parse(data);
          this.handleMessage(clientId, message);
        } catch (error) {
          console.error('Message parse error:', error);
        }
      });

      // 心跳响应
      ws.on('pong', () => {
        const client = this.clients.get(clientId);
        if (client) {
          client.isAlive = true;
          client.lastPing = Date.now();
        }
      });

      // 连接关闭
      ws.on('close', () => {
        console.log(`Connection closed: ${clientId}`);
        this.clients.delete(clientId);
      });

      // 错误处理
      ws.on('error', (error) => {
        console.error(`WebSocket error for ${clientId}:`, error);
        this.clients.delete(clientId);
      });
    });

    // 定期心跳检测
    setInterval(() => {
      this.checkHeartbeats();
    }, this.heartbeatInterval);
  }

  handleMessage(clientId, message) {
    const client = this.clients.get(clientId);
    if (!client) return;

    switch (message.type) {
      case 'ping':
        // 心跳响应
        client.ws.send(JSON.stringify({
          type: 'pong',
          timestamp: Date.now(),
        }));
        client.isAlive = true;
        client.lastPing = Date.now();
        break;

      case 'chat':
        // 广播消息
        this.broadcast({
          type: 'chat',
          clientId,
          content: message.content,
          timestamp: Date.now(),
        });
        break;

      default:
        console.log(`Unknown message type: ${message.type}`);
    }
  }

  checkHeartbeats() {
    const now = Date.now();
    
    for (const [clientId, client] of this.clients.entries()) {
      const timeSinceLastPing = now - client.lastPing;
      
      // 超过最大时间没有心跳
      if (timeSinceLastPing > this.heartbeatInterval * this.maxMissedPings) {
        console.log(`Client ${clientId} heartbeat timeout, closing connection`);
        client.ws.terminate();
        this.clients.delete(clientId);
        continue;
      }

      // 发送心跳检测
      if (!client.isAlive) {
        console.log(`Client ${clientId} not responding to pings`);
        client.ws.terminate();
        this.clients.delete(clientId);
        continue;
      }

      // 标记为需要检测,发送ping
      client.isAlive = false;
      try {
        client.ws.ping();
      } catch (error) {
        console.error(`Ping error for ${clientId}:`, error);
        this.clients.delete(clientId);
      }
    }
  }

  broadcast(message) {
    const data = JSON.stringify(message);
    
    this.wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(data);
      }
    });
  }

  sendToClient(clientId, message) {
    const client = this.clients.get(clientId);
    if (client && client.ws.readyState === WebSocket.OPEN) {
      client.ws.send(JSON.stringify(message));
    }
  }

  generateClientId() {
    return `client_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  getConnectedCount() {
    return this.clients.size;
  }
}

// 使用示例
const http = require('http');
const server = http.createServer();
const wssWithHeartbeat = new WebSocketServerWithHeartbeat(server);

server.listen(8080, () => {
  console.log('WebSocket server with heartbeat listening on port 8080');
});

4. 高级特性:连接质量监控

typescript

复制

下载

复制代码
interface ConnectionMetrics {
  latency: number;        // 延迟(ms)
  jitter: number;         // 抖动(ms)
  packetLoss: number;     // 丢包率(%)
  bandwidth: number;      // 带宽(kbps)
  uptime: number;         // 连接时长(ms)
}

class ConnectionMonitor {
  private metrics: ConnectionMetrics = {
    latency: 0,
    jitter: 0,
    packetLoss: 0,
    bandwidth: 0,
    uptime: 0,
  };
  
  private latencySamples: number[] = [];
  private startTime: number = Date.now();
  private sentPackets = 0;
  private receivedPackets = 0;

  recordLatency(latency: number) {
    this.latencySamples.push(latency);
    
    // 保留最近100个样本
    if (this.latencySamples.length > 100) {
      this.latencySamples.shift();
    }

    // 计算平均延迟
    this.metrics.latency = this.latencySamples.reduce((a, b) => a + b, 0) / 
                          this.latencySamples.length;
    
    // 计算抖动(延迟的标准差)
    const variance = this.latencySamples.reduce((sum, sample) => {
      return sum + Math.pow(sample - this.metrics.latency, 2);
    }, 0) / this.latencySamples.length;
    
    this.metrics.jitter = Math.sqrt(variance);
  }

  recordPacket(sent: boolean) {
    if (sent) {
      this.sentPackets++;
    } else {
      this.receivedPackets++;
    }
    
    if (this.sentPackets > 0) {
      this.metrics.packetLoss = ((this.sentPackets - this.receivedPackets) / 
                                this.sentPackets) * 100;
    }
  }

  updateUptime() {
    this.metrics.uptime = Date.now() - this.startTime;
  }

  getMetrics(): ConnectionMetrics {
    this.updateUptime();
    return { ...this.metrics };
  }

  reset() {
    this.startTime = Date.now();
    this.sentPackets = 0;
    this.receivedPackets = 0;
    this.latencySamples = [];
    this.metrics = {
      latency: 0,
      jitter: 0,
      packetLoss: 0,
      bandwidth: 0,
      uptime: 0,
    };
  }
}

// 集成到WebSocketManager
class EnhancedWebSocketManager extends WebSocketManager {
  private connectionMonitor = new ConnectionMonitor();
  private metricsTimer: NodeJS.Timeout | null = null;

  // 重写发送心跳方法
  protected sendHeartbeat(): void {
    if (this.ws?.readyState === WebSocketState.OPEN) {
      const sendTime = Date.now();
      const heartbeatMsg = JSON.stringify({
        type: 'ping',
        timestamp: sendTime,
      });
      
      this.connectionMonitor.recordPacket(true);
      
      this.ws.send(heartbeatMsg);
      this.startHeartbeatTimeout();
    }
  }

  // 处理心跳响应时记录延迟
  protected resetHeartbeat(): void {
    super.resetHeartbeat();
    
    // 假设在消息处理中记录了接收时间
    const receiveTime = Date.now();
    // 这里需要存储发送时间来计算延迟
  }

  // 开始监控
  startMetricsCollection() {
    this.metricsTimer = setInterval(() => {
      const metrics = this.connectionMonitor.getMetrics();
      console.log('Connection metrics:', metrics);
      
      // 根据指标调整策略
      if (metrics.packetLoss > 20) {
        console.warn('High packet loss, consider reducing message frequency');
      }
      
      if (metrics.latency > 1000) {
        console.warn('High latency, adjusting heartbeat interval');
      }
    }, 5000);
  }

  stopMetricsCollection() {
    if (this.metricsTimer) {
      clearInterval(this.metricsTimer);
      this.metricsTimer = null;
    }
  }
}

三、最佳实践与优化建议

1. 心跳参数配置建议

javascript

复制

下载

复制代码
// 不同场景下的推荐配置
const configs = {
  // 实时聊天应用
  CHAT: {
    heartbeatInterval: 25000,     // 25秒
    heartbeatTimeout: 10000,      // 10秒
    reconnectInterval: 2000,      // 2秒
    maxReconnectAttempts: 10,     // 最多重连10次
  },
  
  // 实时数据监控
  MONITORING: {
    heartbeatInterval: 15000,     // 15秒
    heartbeatTimeout: 5000,       // 5秒
    reconnectInterval: 1000,      // 1秒
    maxReconnectAttempts: 20,     // 最多重连20次
  },
  
  // 游戏应用
  GAMING: {
    heartbeatInterval: 10000,     // 10秒
    heartbeatTimeout: 3000,       // 3秒
    reconnectInterval: 500,       // 0.5秒
    maxReconnectAttempts: 5,      // 最多重连5次
  },
};

2. 重连策略优化

typescript

复制

下载

复制代码
// 智能重连策略
class SmartReconnectStrategy {
  private failures = 0;
  private lastFailureTime = 0;
  
  shouldReconnect(): boolean {
    const now = Date.now();
    
    // 短时间内连续失败,可能是网络问题
    if (now - this.lastFailureTime < 10000 && this.failures > 3) {
      console.log('Too many failures in short time, waiting...');
      return false;
    }
    
    // 失败次数太多,可能是服务器问题
    if (this.failures > 10) {
      console.log('Too many failures, giving up');
      return false;
    }
    
    return true;
  }
  
  recordFailure() {
    this.failures++;
    this.lastFailureTime = Date.now();
    
    // 逐渐重置失败计数
    setTimeout(() => {
      if (this.failures > 0) {
        this.failures--;
      }
    }, 60000); // 每分钟减少一次失败计数
  }
  
  recordSuccess() {
    this.failures = 0;
  }
}

3. 离线消息队列

typescript

复制

下载

复制代码
class OfflineMessageQueue {
  private queue: Array<{data: any, timestamp: number}> = [];
  private maxQueueSize = 100;
  private maxAge = 5 * 60 * 1000; // 5分钟
  
  addMessage(data: any) {
    // 清理过期消息
    this.cleanup();
    
    // 队列满时移除最旧的消息
    if (this.queue.length >= this.maxQueueSize) {
      this.queue.shift();
    }
    
    this.queue.push({
      data,
      timestamp: Date.now(),
    });
  }
  
  getMessages(): any[] {
    this.cleanup();
    return this.queue.map(item => item.data);
  }
  
  clear() {
    this.queue = [];
  }
  
  private cleanup() {
    const now = Date.now();
    this.queue = this.queue.filter(item => 
      now - item.timestamp <= this.maxAge
    );
  }
}

// 在WebSocketManager中使用
class WebSocketManagerWithQueue extends WebSocketManager {
  private offlineQueue = new OfflineMessageQueue();
  
  send(data: any): boolean {
    const success = super.send(data);
    
    if (!success) {
      // 发送失败,存储到离线队列
      this.offlineQueue.addMessage(data);
      return false;
    }
    
    return true;
  }
  
  protected onOpen(event: Event): void {
    super.onOpen(event);
    
    // 连接成功后发送离线消息
    const offlineMessages = this.offlineQueue.getMessages();
    offlineMessages.forEach(message => {
      super.send(message);
    });
    
    this.offlineQueue.clear();
  }
}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

四、常见问题与解决方案

问题1:心跳包增加了流量开销

解决方案

  • 使用二进制ping/pong帧(1字节)

  • 动态调整心跳频率(根据网络质量)

  • 允许客户端协商心跳间隔

问题2:重连风暴导致服务器压力

解决方案

  • 使用随机延迟避免同时重连

  • 实现退避算法(exponential backoff)

  • 服务器端限制重连频率

问题3:移动端网络切换

解决方案

  • 监听网络状态变化事件

  • 网络恢复后延迟重连

  • 使用短连接心跳确认网络质量

问题4:多标签页重复连接

解决方案

typescript

复制

下载

复制代码
// 使用BroadcastChannel或SharedWorker协调多标签页
class MultiTabWebSocketManager {
  private channel: BroadcastChannel;
  
  constructor() {
    this.channel = new BroadcastChannel('websocket_control');
    
    this.channel.addEventListener('message', (event) => {
      if (event.data.type === 'tab_connected') {
        // 其他标签页已连接,本标签页保持只读
        this.setReadonlyMode();
      }
    });
    
    // 通知其他标签页
    this.channel.postMessage({ type: 'tab_connected' });
  }
}

五、测试方案

typescript

复制

下载

复制代码
// 模拟网络不稳定的测试工具
class NetworkSimulator {
  static async testWebSocket(wsManager: WebSocketManager) {
    // 测试正常连接
    console.log('Test 1: Normal connection');
    wsManager.connect();
    await this.delay(2000);
    
    // 模拟网络中断
    console.log('Test 2: Simulating network loss');
    this.simulateNetworkLoss();
    await this.delay(5000);
    
    // 恢复网络
    console.log('Test 3: Network recovery');
    this.restoreNetwork();
    await this.delay(10000);
    
    // 模拟服务器重启
    console.log('Test 4: Simulating server restart');
    wsManager.close(1001, 'Server restart');
    await this.delay(3000);
    
    // 验证自动重连
    console.log('Test 5: Verifying auto-reconnect');
    await this.delay(15000);
  }
  
  private static delay(ms: number) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
  
  private static simulateNetworkLoss() {
    // 实际项目中可能需要修改网络配置
    console.log('Network loss simulated');
  }
  
  private static restoreNetwork() {
    console.log('Network restored');
  }
}

六、总结

关键要点:

  1. 心跳检测:防止连接被中间设备断开,及时检测连接状态

  2. 自动重连:提供无缝的用户体验,减少手动干预

  3. 智能策略:根据网络质量动态调整参数

  4. 资源管理:及时清理无效连接和定时器

  5. 错误处理:优雅处理各种网络异常情况

推荐库:

  • 客户端:reconnecting-websocket@gamestdio/websocket

  • 服务器端:ws(Node.js)、gorilla/websocket(Go)、Spring WebSocket(Java)

通过以上实现,可以构建一个健壮、可靠的WebSocket连接,能够应对各种网络异常情况,提供良好的用户体验。

相关推荐
冷雨夜中漫步2 小时前
Python入门——__init__.py文件作用
android·java·python
Volunteer Technology2 小时前
Centos7安装python和jupyter
linux·python·jupyter
行秋2 小时前
MATLAB 中的两大电力仿真库:Simscape Electrical(蓝色库) vs SimPowerSystems(黑色库)
开发语言·matlab
涛起云永2 小时前
Qt 源码编译 -- obj文件无法找到
开发语言·qt
deng12042 小时前
【排序算法总结(1)】
java·算法·排序算法
hzb666662 小时前
xd_day28js原生开发-day31 day41asp.net
开发语言·前端·javascript·安全·web安全
小宋10212 小时前
Kafka 自动发送消息 Demo 实战:从配置到发送的完整流程(java)
java·分布式·kafka
Remember_9932 小时前
【数据结构】Java数据结构深度解析:栈(Stack)与队列(Queue)完全指南
java·开发语言·数据结构·算法·spring·leetcode·maven
@zulnger2 小时前
Django 模型
后端·python·django