WebSocket房间管理中如何正确处理用户断开连接?

FSD-子聪 阅读 2

我在用 WebSocket 做一个多人实时聊天室,用户加入房间后,如果突然刷新页面或关闭标签页,服务器那边收不到 disconnect 事件,导致房间人数统计一直不准。试过监听 onclose,但有时候根本没触发,咋办?

前端样式这块倒是没问题,比如下面这段 CSS 是用来高亮当前房间的:

.room-item.active {
  border-left: 4px solid #007bff;
  background-color: #f8f9fa;
  font-weight: bold;
}
我来解答 赞 1 收藏
二维码
手机扫码查看
1 条解答
小盼云
小盼云 Lv1
这个问题其实挺常见的,WebSocket 的 onclose 事件在浏览器直接关闭或刷新时,服务端确实不一定能立刻收到。浏览器关闭太快,关闭帧可能根本没发出去就断了。

最靠谱的方案是心跳检测加服务端超时,别指望 onclose 能百分百可靠。

先说服务端,假设你用的是 Node.js 加 ws 库或者 socket.io,核心思路就是定期发心跳包,超时就踢掉:

const clients = new Map();

// 每30秒检查一次所有连接
setInterval(() => {
clients.forEach((client, ws) => {
if (!client.isAlive) {
ws.terminate();
clients.delete(ws);
updateRoomCount(client.roomId);
return;
}
client.isAlive = false;
ws.ping();
});
}, 30000);

wss.on('connection', (ws) => {
const client = { isAlive: true, roomId: null };
clients.set(ws, client);

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

ws.on('close', () => {
clients.delete(ws);
updateRoomCount(client.roomId);
});
});


前端这边配合一下,收到 ping 自动回复 pong,另外可以在 beforeunload 时主动断开:

let ws = null;

function connect() {
ws = new WebSocket('ws://your-server');

ws.onmessage = (e) => {
if (e.data === 'ping') {
ws.send('pong');
}
};
}

window.addEventListener('beforeunload', () => {
if (ws) {
ws.close(1000, 'page unload');
}
});


不过 beforeunload 也不是百分百可靠,比如浏览器崩溃、断电这些极端情况。所以核心还是服务端的心跳超时机制,这才是兜底方案。

还有一点,房间人数统计建议用 Redis 或者内存里的 Map 单独维护,别每次都去遍历连接数,这样更清晰也更高效。

你提到那段 CSS 是用来高亮房间的,样式本身没问题,但如果你在清理用户连接时没正确更新 DOM,高亮状态可能也会残留。记得在清理用户连接时,同步更新前端的房间列表状态。
点赞
2026-03-02 19:37