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

Socket.IO v3.03多用户同时刷新时emit推送数据串户问题求助

问题根因与修复方案

核心问题定位

你遇到的串数据问题核心来自两个高频场景:

  • 服务端userid作用域错误:你打印映射日志的逻辑在await getTickets之前,此时userid是正确的,但如果userid被定义为全局/外层作用域变量,await等待数据库查询的过程中,另一个用户的连接请求会覆盖userid的值,等查询返回执行emit时,拿到的已经是后连接用户的userid,自然会推错对象。
  • 客户端多Socket实例残留:你开启了forceNew: true每次刷新都会生成新的Socket实例,旧实例没有断开和移除监听,会额外接收其他连接的推送消息。

服务端修复方案

将userid改为连接回调内的局部变量,优先用当前Socket实例推送数据,避免全局查找的串发风险,同时增加断开连接的映射清理逻辑:

io.on('connection', async (socket) => {
  // 从当前连接的query中取token,解析逻辑放在回调内部,userid为局部变量不会被其他连接覆盖
  const token = socket.handshake.query['x-token'];
  const userid = await parseToken(token); 
  if (!userid) {
    socket.disconnect();
    return;
  }
  // 更新全局映射
  mapSessions[userid] = socket.id;

  // 直接用当前socket实例推送,完全规避全局查询id的错误风险
  socket.emit('ticketList', await getTickets(userid));

  // 断开时清理无效映射
  socket.on('disconnect', () => {
    if (mapSessions[userid] === socket.id) {
      delete mapSessions[userid];
    }
  })
})

客户端修复方案

创建新连接前先断开旧连接、移除旧事件监听,避免多实例同时接收消息:

const connectSocket = useCallback(() => {
  if (token) {
    // 清理旧连接
    if (window.existSocket) {
      window.existSocket.disconnect();
      window.existSocket.off('ticketList');
    }
    const socketTemp = io.connect(serverPath, {
      transports: ['websocket'],
      autoConnect: true,
      reconnection:true,
      forceNew: true,
      query: {
        'x-token': token
      }
    });
    window.existSocket = socketTemp;
    dispatch(setSocket(socketTemp));
    // 事件绑定在当前新实例上
    socketTemp.on('ticketList', (data) => {
      // 建议加一层兜底校验:返回数据的用户标识和本地登录用户id不一致就丢弃
      if (data.userid === localUserid) {
        // 处理待办数据逻辑
      }
    })
  }
}, [token, serverPath, dispatch]);

额外优化建议

Socket.IO 3.0.3存在已知的多连接会话串扰bug,可升级到3.1.2+的同大版本稳定版,无需修改现有业务逻辑即可规避底层框架问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 01:48:03