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

浏览器刷新时如何追踪Socket.IO的新socket id?

问题核心原因

浏览器刷新会触发Socket连接断开重连,新连接会生成全新的socketId,但你的代码存在两个关键问题:

  1. 用户重连时,服务端仅添加新用户而非更新已有用户的socketId,导致onlineUsers数组中留存旧的无效ID;
  2. 用户断开连接时,未从onlineUsers中移除对应记录,数组会堆积无效数据,进一步导致通知无法发送到新连接。

解决方案:更新用户SocketID管理逻辑

服务端代码修改

核心优化点:

  • 新增upsertUser函数,实现用户SocketID的「更新/新增」逻辑;
  • 断开连接时根据socketId移除用户记录;
  • 增加接收方离线的判断逻辑,方便调试。

修改后的server.js代码:

const io = new Server(server, {
  cors: {
    origin: "http://localhost:3000",
  },
});

let onlineUsers: any = [];

// 新增:更新或添加用户的socketId
const upsertUser = (username: any, socketId: any) => {
  const existingUserIndex = onlineUsers.findIndex(user => user.username === username);
  if (existingUserIndex !== -1) {
    // 用户已存在,替换为新的socketId
    onlineUsers[existingUserIndex].socketId = socketId;
  } else {
    // 新用户,添加到数组
    onlineUsers.push({ username, socketId });
  }
  console.log("当前在线用户:", onlineUsers);
};

const getUser = (username: any) => {
  return onlineUsers.find((user: any) => user.username === username);
};

// 新增:根据socketId移除离线用户
const removeUser = (socketId: any) => {
  onlineUsers = onlineUsers.filter(user => user.socketId !== socketId);
};

io.on("connection", (socket) => {
  console.log("用户连接,socketId:", socket.id);

  socket.on("newUser", (username: any) => {
    console.log("用户上线:", username);
    upsertUser(username, socket.id);
  });

  socket.on("sendNotification", ({ senderName, receiverName, action }) => {
    const receiver = getUser(receiverName);
    if (receiver) {
      io.to(receiver.socketId).emit("getNotification", {
        senderName,
        action,
      });
    } else {
      console.log(`接收方${receiverName}已离线`);
    }
  });

  socket.on("disconnect", () => {
    console.log("用户断开连接,socketId:", socket.id);
    removeUser(socket.id);
  });
});

客户端代码优化

核心优化点:

  • 初始化Socket时直接发送用户信息,避免依赖异步状态更新;
  • 添加监听清理逻辑,防止重复绑定导致通知多次触发。

修改后的客户端代码:

// 初始化Socket并发送用户信息
useEffect(() => {
  const newSocket = io("http://localhost:5000");
  setSocket(newSocket);

  // 连接成功后立即发送用户信息,无需等待state更新
  const userData = localStorage.getItem("user");
  if (userData) {
    const user = JSON.parse(userData);
    newSocket.emit("newUser", user.name);
  }
}, []);

// Navbar组件的通知监听逻辑
React.useEffect(() => {
  if (!socket) return;

  const handleNotification = (data: any) => {
    console.log("收到通知:", data);
    setNotifications(prev => [...prev, data]);
  };

  socket.on("getNotification", handleNotification);

  // 组件卸载时移除监听,防止内存泄漏
  return () => {
    socket.off("getNotification", handleNotification);
  };
}, [socket]);

流程验证
  1. 页面刷新时:旧Socket触发disconnect事件,服务端移除旧socketId的用户记录;
  2. 新页面加载:创建新Socket连接,立即发送newUser事件,服务端upsertUser更新该用户的socketId为新值;
  3. 后续发送通知:getUser拿到最新的有效socketId,通知能正常推送到新连接。

额外建议
  • 如果支持用户多设备登录,可将onlineUsers中用户的socketId改为数组,存储同一用户的所有在线连接,实现多设备同步通知;
  • 生产环境建议用Redis等数据库存储在线用户信息,避免服务器重启后数据丢失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 12:40:37