在线状态同步不及时,WebSocket 心跳怎么处理才靠谱?

Good“馨然 阅读 26

我在做一个聊天应用,用 WebSocket 实现用户在线状态。目前的做法是用户连接时设为 online,断开就设 offline。但实际测试发现,如果用户直接关掉浏览器或者网络突然中断,服务端收不到 disconnect 事件,状态就一直卡在 online,其他用户看到的还是“在线”——这显然不对。

我尝试加了心跳机制,前端每隔 30 秒发个 ping,后端没收到就标记离线。但代码写得不太稳,有时候还是会漏判。下面是我 React 组件里的心跳逻辑,是不是哪里有问题?

useEffect(() => {
  const ws = new WebSocket('wss://example.com/chat');
  ws.onopen = () => {
    const heartbeat = setInterval(() => {
      if (ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify({ type: 'ping' }));
      }
    }, 30000);
  };
  return () => {
    clearInterval(heartbeat); // 这里其实会报错,heartbeat 作用域不对
    ws.close();
  };
}, []);
我来解答 赞 10 收藏
二维码
手机扫码查看
1 条解答
打工人彦杰
心跳机制确实能解决这个问题,但你现在的实现有几个问题需要改进。

首先说说前端这块,heartbeat变量的作用域不对,应该在useEffect函数内定义。另外,建议用一个标志位来跟踪连接状态,避免重复设置定时器。这是修正后的代码:

useEffect(() => {
let isMounted = true;
const ws = new WebSocket('wss://example.com/chat');

ws.onopen = () => {
if (!isMounted) return;
let heartbeat = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping' }));
}
}, 30000);

ws.onclose = () => clearInterval(heartbeat);
};

return () => {
isMounted = false;
ws.close();
};
}, []);


后端这边也需要配合处理。光靠前端发心跳不够稳妥,服务端要主动监控连接状态。建议设置个超时时间,比如60秒收不到任何消息就断开连接。可以用Node.js的setTimeout来做:

let clients = {};

ws.on('connection', (socket) => {
socket.isAlive = true;

socket.on('pong', () => {
socket.isAlive = true;
});

// 每60秒检查一次
setTimeout(() => {
if (!socket.isAlive) {
socket.terminate();
} else {
socket.isAlive = false;
socket.ping();
}
}, 60000);
});


记得把超时时间和心跳间隔错开一点,这样更稳当。折腾过几次才找到这个比较靠谱的方案,希望能帮到你。
点赞
2026-03-27 06:01