⚠️ Alpha内测版本警告:此为早期内部构建版本,尚不完整且可能存在错误,欢迎大家提Issue反馈问题或建议
Skip to content

12.4.4 处理连接异常——连接管理:断线重连与状态同步

一句话破题

网络不可靠是常态,优秀的实时应用必须优雅地处理断线、重连和状态同步。

连接状态管理

tsx
'use client';

import { useEffect, useState } from 'react';
import { io, Socket } from 'socket.io-client';

type ConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';

export function useSocket(url: string) {
  const [socket, setSocket] = useState<Socket | null>(null);
  const [status, setStatus] = useState<ConnectionStatus>('connecting');

  useEffect(() => {
    const newSocket = io(url, {
      reconnection: true,
      reconnectionAttempts: 5,
      reconnectionDelay: 1000,
      reconnectionDelayMax: 5000,
    });

    newSocket.on('connect', () => {
      setStatus('connected');
      console.log('已连接');
    });

    newSocket.on('disconnect', (reason) => {
      setStatus('disconnected');
      console.log('断开连接:', reason);
    });

    newSocket.on('reconnect_attempt', (attempt) => {
      setStatus('reconnecting');
      console.log(`重连尝试 ${attempt}`);
    });

    newSocket.on('reconnect', () => {
      setStatus('connected');
      console.log('重连成功');
    });

    newSocket.on('reconnect_failed', () => {
      setStatus('disconnected');
      console.log('重连失败');
    });

    setSocket(newSocket);

    return () => {
      newSocket.close();
    };
  }, [url]);

  return { socket, status };
}

连接状态 UI

tsx
function ConnectionIndicator({ status }: { status: ConnectionStatus }) {
  const config = {
    connecting: { color: 'bg-yellow-500', text: '连接中...' },
    connected: { color: 'bg-green-500', text: '已连接' },
    disconnected: { color: 'bg-red-500', text: '已断开' },
    reconnecting: { color: 'bg-orange-500', text: '重连中...' },
  };

  const { color, text } = config[status];

  return (
    <div className="flex items-center gap-2">
      <span className={`w-2 h-2 rounded-full ${color}`} />
      <span className="text-sm text-gray-600">{text}</span>
    </div>
  );
}

状态同步策略

断线重连后,需要同步期间错过的状态:

typescript
// 服务端:记录消息历史
const messageHistory = new Map<string, Message[]>();

io.on('connection', (socket) => {
  socket.on('joinRoom', (roomId: string) => {
    socket.join(roomId);
    
    // 发送历史消息
    const history = messageHistory.get(roomId) || [];
    socket.emit('messageHistory', history.slice(-50)); // 最近 50 条
  });

  socket.on('sendMessage', ({ roomId, content }) => {
    const message = { id: Date.now().toString(), content, timestamp: Date.now() };
    
    // 保存到历史
    if (!messageHistory.has(roomId)) {
      messageHistory.set(roomId, []);
    }
    messageHistory.get(roomId)!.push(message);
    
    io.to(roomId).emit('newMessage', message);
  });
});

// 客户端:处理历史消息
socket.on('messageHistory', (history: Message[]) => {
  setMessages((prev) => {
    // 去重合并
    const existingIds = new Set(prev.map((m) => m.id));
    const newMessages = history.filter((m) => !existingIds.has(m.id));
    return [...newMessages, ...prev].sort((a, b) => a.timestamp - b.timestamp);
  });
});

心跳机制

Socket.io 内置了心跳机制,但你也可以自定义:

typescript
// 服务端配置
const io = new Server(httpServer, {
  pingTimeout: 60000,    // 等待 pong 响应的超时时间
  pingInterval: 25000,   // 发送 ping 的间隔
});

// 客户端可以监听
socket.on('ping', () => {
  console.log('收到心跳');
});

离线消息队列

网络不好时,先缓存消息,恢复后再发送:

typescript
class MessageQueue {
  private queue: Array<{ event: string; data: unknown }> = [];
  private socket: Socket | null = null;

  setSocket(socket: Socket) {
    this.socket = socket;
    this.flush();
  }

  send(event: string, data: unknown) {
    if (this.socket?.connected) {
      this.socket.emit(event, data);
    } else {
      this.queue.push({ event, data });
    }
  }

  flush() {
    if (this.socket?.connected) {
      while (this.queue.length > 0) {
        const { event, data } = this.queue.shift()!;
        this.socket.emit(event, data);
      }
    }
  }
}

AI 协作指南

  • 核心意图:让 AI 帮你实现健壮的连接管理。
  • 需求定义公式"请帮我实现一个带有连接状态显示、自动重连和离线消息队列的 Socket.io 客户端封装。"
  • 关键术语重连 (reconnection)心跳 (heartbeat)状态同步离线队列

避坑指南

  • 指数退避:重连间隔应该逐渐增加,避免服务器过载。
  • 最大重试次数:不要无限重试,给用户反馈让他们手动刷新。
  • 幂等性:重发的消息不应该产生副作用(如重复扣款)。