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

Socket.io服务端断连问题:闲置用户退出时socket未定义报错

根因分析
  • Socket.IO 内置心跳保活机制,默认配置下如果客户端连续pingInterval + pingTimeout(默认25秒+20秒=45秒)没有返回心跳响应,服务端会判定连接失活,主动销毁对应socket实例释放内存。
  • 你当前代码的disconnect事件回调通过闭包引用外部socket对象,当闲置连接被服务端主动回收时,回调触发时机晚于socket实例销毁操作,此时访问socket属性就会触发未定义异常。闲置场景才会触发是因为正常操作下连接不会触发心跳超时销毁逻辑,socket实例一直有效。
  • 额外隐患:当前代码只在用户登录时往users数组push数据,没有在用户退出时清理对应条目,会出现内存泄漏和在线用户列表不准的问题。
修复方案
  • 提前存储不可变的连接标识,不要在disconnect回调中访问可能被销毁的socket对象
  • 增加空值防护逻辑,避免访问已销毁对象的属性导致崩溃
  • 补充users数组的清理逻辑,避免内存泄漏

修复后代码参考:

var users = []; // 在线用户数组
// 可选:如果需要调整闲置超时阈值可以修改io初始化配置,拉长超时时间减少闲置连接被销毁的概率
// const io = new Server(httpServer, {
//   pingInterval: 25000, // 心跳发送间隔,单位ms
//   pingTimeout: 60000, // 心跳响应超时时间,单位ms
// });

io.on('connect', (socket) => {
  // 连接建立时就存储当前socket的id,后续disconnect直接用这个局部变量,不会被销毁
  const currentSocketId = socket.id;
  let loginUser = null;

  socket.on('user_login',(user)=>{
    // 存储用户和当前socket的绑定关系,方便后续退出时清理
    loginUser = { ...user, socketId: currentSocketId };
    users.push(loginUser);
  })

  socket.on('disconnect',()=>{
    // 空值防护,避免异常
    if (!currentSocketId) return;
    // 清理在线用户列表
    if (loginUser) {
      users = users.filter(u => u.socketId !== currentSocketId);
    }
    const message = `${currentSocketId} logs out`;
    io.emit('user_logout', message);
  })
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.14 18:06:04