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

Socket.io无法将用户加入指定房间问题求助

多聊天多用户在线状态同步问题修复方案

当前实现的多聊天在线状态功能存在核心问题:用户创建聊天并添加其他用户后,任意一方刷新页面时,仅一方会被加入对应聊天房间,双方无法同时出现在同一房间的在线列表中。


问题根源分析

  1. 全局在线用户存储错误:服务端用全局sockets Map存储所有在线用户,未按聊天房间维度区分,导致发送在线状态时,把所有在线用户推送给所有房间,完全违背「仅展示同一聊天内在线状态」的需求。
  2. 新用户上线同步不完整:新用户加入房间时,仅向房间内推送自己的ID,但未向新用户同步该房间已有的在线用户,导致新用户看不到房间里已在线的人。
  3. 客户端在线状态累加逻辑错误:客户端通过[...onlineUsers, ...ids]累加在线用户,没有去重也没有按房间区分,导致状态混乱。
  4. 断开连接时的状态推送错误:断开连接时推送全局所有在线用户,而非当前用户所在房间的在线用户。

修复方案

服务端修改

改用按房间维护在线用户的结构,替换全局sockets Map,确保每个房间的在线状态独立:

// 按房间存储在线用户,key: 房间名,value: 用户ID的Set
const roomOnlineUsers = new Map<string, Set<number>>();

io.on('connection', (socket) => {
  // 存储当前socket对应的用户ID,用于断开连接时清理
  let currentUserId: number | undefined;

  socket.on('newUserJoined', async (data: { chatIds: number[], user: number }) => {
    if(!data.chatIds || !data.user ) return;
    currentUserId = data.user;
    const rooms = data.chatIds.map(t => `room${t}`);
    
    // 加入所有对应的房间
    socket.join(rooms);

    // 处理每个房间的在线用户同步
    for (const room of rooms) {
      // 初始化房间的在线用户Set(如果不存在)
      if (!roomOnlineUsers.has(room)) {
        roomOnlineUsers.set(room, new Set());
      }
      const usersInRoom = roomOnlineUsers.get(room)!;
      usersInRoom.add(data.user);

      // 向房间内其他用户推送当前用户上线的消息
      socket.to(room).emit('userOnline', { room, userId: data.user });
      // 向当前socket推送该房间已有的在线用户(除了自己)
      socket.emit('roomOnlineUsers', {
        room,
        userIds: Array.from(usersInRoom).filter(id => id !== data.user)
      });
    }
    console.log('Connecting socket', { id: socket.id, user: data.user, chatIds: data.chatIds });
  });

  socket.on('disconnect', () => {
    if (!currentUserId) return;
    // 获取当前socket所在的所有房间(排除socket自身的默认房间)
    const rooms = Array.from(socket.rooms).filter(r => r !== socket.id);
    
    for (const room of rooms) {
      const usersInRoom = roomOnlineUsers.get(room);
      if (usersInRoom) {
        usersInRoom.delete(currentUserId);
        // 如果房间没有用户了,清理该房间的存储
        if (usersInRoom.size === 0) {
          roomOnlineUsers.delete(room);
        }
        // 向房间推送更新后的在线用户列表
        io.to(room).emit('roomOnlineUsers', {
          room,
          userIds: Array.from(usersInRoom)
        });
      }
      socket.leave(room);
    }
    console.log('Disconnecting socket', socket.id);
  });

  socket.on('sendMessage', (message: { text: string, chatId: number, senderId: number }) => {
    io.to(`room${message.chatId}`).emit('getMessage', message);
  });
});

客户端修改

调整在线状态存储结构,按房间维护在线用户,并正确处理服务端推送的状态:

// 初始化按房间存储在线用户,格式示例:{ room1: [1,2], room2: [3] }
const [roomOnlineUsers, setRoomOnlineUsers] = useState<Record<string, number[]>>({});

useEffect(() => {
  if(!socket || !user?.id || !chatResponse || chatResponse.data.length == 0) return;
  
  const chatIds = chatResponse.data.map((c: ChatFull) => c.id);
  socket.emit("newUserJoined", { chatIds, user: user.id });

  // 接收房间初始在线用户列表
  const handleRoomOnlineUsers = ({ room, userIds }: { room: string, userIds: number[] }) => {
    setRoomOnlineUsers(prev => ({
      ...prev,
      [room]: [...new Set(userIds)] // 去重确保唯一性
    }));
  };

  // 接收单个用户上线通知
  const handleUserOnline = ({ room, userId }: { room: string, userId: number }) => {
    setRoomOnlineUsers(prev => ({
      ...prev,
      [room]: [...new Set([...(prev[room] || []), userId])]
    }));
  };

  socket.on('roomOnlineUsers', handleRoomOnlineUsers);
  socket.on('userOnline', handleUserOnline);

  return () => {
    socket.off('roomOnlineUsers', handleRoomOnlineUsers);
    socket.off('userOnline', handleUserOnline);
  };
}, [chatResponse, socket, user?.id]);

关键修复点说明

  1. 房间维度的在线用户存储:服务端用roomOnlineUsers Map分别记录每个房间的在线用户,确保不同房间的状态完全隔离。
  2. 双向同步:新用户加入时,既向房间内其他用户推送上线消息,也向新用户同步房间已有在线用户,解决了刷新后状态不同步的问题。
  3. 精准的状态推送:断开连接时,仅向用户所在的房间推送更新后的在线列表,避免干扰其他房间。
  4. 客户端按房间存储:客户端用对象存储每个房间的在线用户,避免全局累加导致的状态混乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 04:33:24