You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置Socket.IO:允许短断网重连,禁止复用旧Socket ID

Socket.io 重连逻辑问题解决指南

先搞懂两个关键区别

别再混淆这俩行为:

  • 临时断网重连:短时间(<10秒)网络波动时,Socket.io会自动用原Socket ID恢复连接,相当于捡回之前的会话。
  • 完全断开重连:断网超10秒、关窗口/跳页面后,再连会生成新Socket ID,属于全新会话。

对应你的需求改代码

1. 10秒内断网重连+同步错过的消息

  • 先把状态恢复的超时改成10秒,超过这个时间就不让用原ID:
    const io = new Server(server, {
      cors: corsOptions,
      connectionStateRecovery: {
        maxDisconnectionDuration: 10 * 1000, // 调整为10秒
        skipMiddlewares: true,
      },
      // 追加心跳配置,避免浏览器后台误判断开
      pingInterval: 10000,
      pingTimeout: 30000
    });
    
  • 同步消息:服务端给每个用户维护未读消息队列,重连成功时推送:
    // 全局存储用户未读消息
    const userMissedMessages = new Map();
    
    io.on("connection", (socket) => {
      // 如果是恢复连接,推送未读消息
      if (socket.recovered) {
        const messages = userMissedMessages.get(socket.id) || [];
        socket.emit("sync_messages", messages);
        userMissedMessages.delete(socket.id); // 推送后清空队列
      }
    
      // 正常发消息时,同时存入离线用户的队列
      socket.on("send_msg", (msg) => {
        io.to(roomCode).emit("receive_msg", msg);
        // 把消息存到所有离线用户的队列(需自行判断用户在线状态)
        game.getPlayers().forEach(player => {
          if (!player.isOnline()) {
            const queue = userMissedMessages.get(player.socketId) || [];
            queue.push(msg);
            userMissedMessages.set(player.socketId, queue);
          }
        });
      });
    
      // ...其他业务逻辑
    });
    

2. 切标签页/空闲时维持连接不中断

浏览器后台会限制定时器,导致Socket.io心跳失效,客户端也要同步调整配置:

// React客户端代码
import { io } from "socket.io-client";

const socket = io("你的服务器地址", {
  pingInterval: 10000, // 每10秒发送一次心跳
  pingTimeout: 30000, // 30秒未收到响应才判定断开
  reconnectionDelayMax: 10000 // 重连延迟上限设为10秒
});

3. 超10秒断网/关窗口后禁止用原ID

  • 维护失效Socket ID集合,连接时先校验:
    const invalidSocketIds = new Set();
    const disconnectTimers = new Map(); // 存储断网后的延迟定时器
    
    io.on("connection", (socket) => {
      // 若ID已失效,直接强制断开
      if (invalidSocketIds.has(socket.id)) {
        socket.disconnect(true);
        return;
      }
    
      // 重连成功则取消之前的断开定时器
      if (socket.recovered) {
        const timer = disconnectTimers.get(socket.id);
        if (timer) {
          clearTimeout(timer);
          disconnectTimers.delete(socket.id);
          // 发送用户回归通知
          const backMsg = {
            msg: `${game.getPlayerById(socket.id)?.getUsername()} 重新连接了`,
            sender: "PLAYER UPDATE",
            time: ""
          };
          io.to(roomCode).emit("receive_msg", backMsg);
        }
      }
    
      socket.on("disconnect", (reason) => {
        console.log(`用户 ${socket.id} 断开:${reason}`);
        // 设置10秒延迟,超时未重连则标记ID失效并发送离开通知
        const timer = setTimeout(() => {
          invalidSocketIds.add(socket.id);
          const byeMsg = {
            msg: `${game.getPlayerById(socket.id)?.getUsername()} 已断开连接`,
            sender: "PLAYER UPDATE",
            time: ""
          };
          io.to(roomCode).emit("receive_msg", byeMsg);
          disconnectTimers.delete(socket.id);
        }, 10000);
    
        disconnectTimers.set(socket.id, timer);
      });
    });
    

修复当前存在的问题

1. 切标签页/空闲时随机断开、返回后被踢但能连旧ID

  • 用上述心跳配置避免浏览器后台误判断开。
  • 通过延迟定时器,10秒内重连就取消离开通知,不会踢用户,也不会标记旧ID失效。

2. 断网时的相同问题

  • 同样靠延迟定时器区分临时断网和永久断开:10秒内重连恢复会话,超过10秒才标记ID失效并发离开通知。

内容的提问来源于stack exchange,提问作者chung

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 05:00:37