WebSocket房间管理怎么避免用户重复加入同一个房间?

Designer°会娟 阅读 11

我用WebSocket做了一个多人聊天室,但发现用户刷新页面后会重复加入同一个房间,导致消息重复接收。我已经在服务端用Set存用户ID了,但好像没起作用?

下面是我服务端处理加入房间的逻辑:

const rooms = new Map();

function joinRoom(userId, roomId) {
  if (!rooms.has(roomId)) {
    rooms.set(roomId, new Set());
  }
  rooms.get(roomId).add(userId); // 这里是不是有问题?
}
我来解答 赞 6 收藏
二维码
手机扫码查看
1 条解答
宇文文斌
你的代码逻辑其实没问题,Set 本身就能去重。问题在于用户刷新页面时,旧的WebSocket连接并没有正常断开,服务端不知道用户已经离线了。

看一下 WebSocket 的生命周期,连接断开时需要显式处理。ws库的话,监听 close 事件:

ws.on('close', () => {
// 用户断开时从所有房间移除
for (const [roomId, users] of rooms) {
if (users.has(userId)) {
users.delete(userId);
// 如果房间空了可以清理掉
if (users.size === 0) {
rooms.delete(roomId);
}
}
}
});


另外还有个问题:刷新页面时新旧连接会短暂共存,这期间可能收到重复消息。可以在 joinRoom 里先检查一下:

function joinRoom(userId, roomId) {
if (!rooms.has(roomId)) {
rooms.set(roomId, new Set());
}
const room = rooms.get(roomId);
// 先移除旧连接,避免重复
if (room.has(userId)) {
room.delete(userId);
}
room.add(userId);
}


不过更规范的做法是用 WebSocket 连接本身的 ID(或者 session)来区分不同连接,而不是 userId。因为一个用户可能多设备登录,或者刷新页面时新旧连接并存的情况。

官方文档里关于 ws 的 Connection handling 章节有提到,建议在 connection 事件里就做好这个映射关系,把每个 socket 对应的 userId 存起来,断开时才能准确清理。
点赞
2026-03-18 06:02