You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Socket.io双人聊天房开发:如何让用户刷新后仍加入原房间?

解决Socket.io双人聊天房间重连分配问题

这个问题的核心是用户刷新后丢失了原房间的归属关系,同时系统的房间分配逻辑优先选择空房间,导致用户无法回到原本的房间。下面是一套完整的解决方案,兼顾重连体验和动态房间管理:

核心思路

  1. 让客户端记住自己的「归属房间ID」,刷新后优先尝试重连原房间
  2. 服务器端维护房间的实时状态,判断原房间是否还可以加入
  3. 调整房间分配逻辑:仅当用户无法重连原房间时,才分配空房间或创建新房间

具体实现步骤

1. 客户端本地存储房间ID

当用户首次被分配到房间后,把房间ID存在localStorage里,这样刷新页面后能快速获取到自己之前的房间:

document.addEventListener('DOMContentLoaded', () => {
  const socket = io();
  const savedRoomId = localStorage.getItem('currentChatRoom');

  // 优先尝试重连原房间
  if (savedRoomId) {
    socket.emit('rejoinRoom', savedRoomId);
  } else {
    // 首次连接,请求分配新房间
    socket.emit('requestRoom');
  }

  // 监听服务器返回的分配结果,更新本地存储
  socket.on('assignedRoom', (roomId) => {
    localStorage.setItem('currentChatRoom', roomId);
    // 这里可以加入UI更新逻辑,比如显示房间号、聊天窗口等
  });

  // 重连失败时,走正常的房间分配流程
  socket.on('rejoinFailed', () => {
    socket.emit('requestRoom');
  });
});

2. 服务器端处理重连请求

服务器需要维护每个房间的用户数量,并且在用户重连时判断原房间是否还能加入(比如是否存在、是否有空闲位置):

const io = require('socket.io')(server);
// 用Map存储房间状态:key=房间ID,value=当前用户数
const roomUserCount = new Map();

io.on('connection', (socket) => {
  // 处理用户重连原房间的请求
  socket.on('rejoinRoom', (roomId) => {
    const targetRoom = io.sockets.adapter.rooms.get(roomId);
    // 检查房间存在且用户数小于2(双人房间最大容量)
    if (targetRoom && targetRoom.size < 2) {
      socket.join(roomId);
      // 更新房间用户数
      roomUserCount.set(roomId, targetRoom.size);
      // 通知客户端重连成功
      socket.emit('assignedRoom', roomId);
      // 通知房间内的另一个用户有人重连
      socket.to(roomId).emit('userRejoined');
    } else {
      // 原房间不可用,通知客户端走正常分配
      socket.emit('rejoinFailed');
    }
  });

  // 处理首次房间分配请求
  socket.on('requestRoom', () => {
    let assignedRoom = null;

    // 第一步:优先分配有1个用户的房间(等待配对的房间)
    for (const [roomId, count] of roomUserCount.entries()) {
      if (count === 1) {
        assignedRoom = roomId;
        break;
      }
    }

    // 第二步:如果没有等待配对的房间,分配空房间(用户数为0的)
    if (!assignedRoom) {
      for (const [roomId, count] of roomUserCount.entries()) {
        if (count === 0) {
          assignedRoom = roomId;
          break;
        }
      }
    }

    // 第三步:没有可用房间的话,创建新房间
    if (!assignedRoom) {
      assignedRoom = `room${Date.now()}`;
      roomUserCount.set(assignedRoom, 0);
    }

    // 让用户加入房间并更新状态
    socket.join(assignedRoom);
    const newCount = roomUserCount.get(assignedRoom) + 1;
    roomUserCount.set(assignedRoom, newCount);
    socket.emit('assignedRoom', assignedRoom);
  });

  // 用户断开连接时,更新房间用户数
  socket.on('disconnecting', () => {
    const userRooms = Array.from(socket.rooms).filter(room => room !== socket.id);
    userRooms.forEach(roomId => {
      const currentCount = roomUserCount.get(roomId) || 0;
      const newCount = Math.max(currentCount - 1, 0);
      roomUserCount.set(roomId, newCount);

      // 空房间超时清理:5分钟后如果还是空的,就删除该房间记录
      if (newCount === 0) {
        setTimeout(() => {
          if (roomUserCount.get(roomId) === 0) {
            roomUserCount.delete(roomId);
          }
        }, 5 * 60 * 1000);
      }
    });
  });
});

3. 关键细节说明

  • 重连优先逻辑:确保用户刷新后第一时间尝试回到原房间,只有当原房间满员、不存在或已被清理时,才分配其他房间
  • 空房间超时清理:避免系统中积累大量无人使用的空房间,优化分配效率
  • 房间状态实时同步:通过roomUserCount Map实时跟踪每个房间的用户数量,确保分配逻辑准确

内容的提问来源于stack exchange,提问作者Jos Fernando Valenzuela

火山引擎 最新活动