如何配置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
相关产品推荐
相关产品推荐

