Socket.IO v4加入房间时如何获取房间内已存在的在线Socket用户列表
Socket.IO v4 获取房间在线连接列表的正确实现
为什么你之前的代码会失效
Socket.IO v4 对内部适配器的存储结构做了调整,同时废弃了多个旧版便捷方法:
- 旧版中
io.sockets.adapter.rooms是普通对象结构,可以直接通过方括号按房间ID取值;但v4中这个属性被替换为原生Map实例,用方括号取值的方式本身就无法获取Map中的值 - 就算通过Map方法拿到了对应的房间实体,v4里的房间实体是直接以socketId为键的Map结构,不存在旧版嵌套的
.sockets属性,链式访问sockets属性自然会返回undefined - 旧版的
io.sockets.clients(roomName)便捷查询接口从v3版本开始就被彻底移除,在v4中完全不存在
v4 官方提供的标准获取房间连接的方法
v4 专门开放了io.in(roomName).fetchSockets()异步方法,可以直接返回对应房间下所有的服务端Socket连接实例数组,这个方法是适配当前版本的正式API,没有版本兼容问题。
结合你现有业务逻辑的改造代码
把你原来的join chat事件逻辑替换成下面的实现:
socket.on('join chat', async ({ chat, userId }) => { // 先执行加入房间的操作 socket.join(chat.id); // 异步获取当前房间内所有的Socket连接实例 const roomSockets = await io.in(chat.id).fetchSockets(); // 从连接实例中提取所有已加入房间的用户ID列表(需要提前在连接时绑定userId到socket对象) const onlineUserIds = roomSockets.map(socket => socket.data.userId); // 原有逻辑:给会话内其他常驻用户推送新用户加入的通知 if (!chat.users) return console.log('users not defined'); chat.users.forEach(user => { if (user._id === userId) return; socket.in(user._id).emit('user joined', userId); }); // 新增逻辑:把当前房间的在线用户列表发给刚加入的新用户 socket.emit('room online users', onlineUserIds); console.log(`User with id ${userId} joined chat with id ${chat.id}`); });
配套注意事项
- 在客户端建立Socket连接的阶段,需要先把用户ID绑定到socket的data属性上,不然从socket实例上取userId会取不到:
// 服务端连接建立阶段的绑定代码 io.on('connection', (socket) => { const userId = socket.handshake.auth.userId; socket.data.userId = userId; });
- 如果需要同步获取而非等待Promise返回,可以直接调用内部适配器的Map结构取值,但这种写法不推荐用于生产环境:
// 同步获取的写法(直接访问内部结构,版本升级可能存在兼容变动) const room = io.sockets.adapter.rooms.get(chat.id); // 提取房间内所有的socketId,再结合本地存储的socketId和userId映射关系得到在线用户列表 const connectedSocketIds = room ? [...room.keys()] : [];
注意:直接访问内部adapter结构的写法没有官方兼容承诺,生产环境优先用官方开放的
fetchSockets()方法,避免后续版本升级出现兼容问题。
内容的提问来源于stack exchange,提问作者regexAgainstTheMachine
相关产品推荐
相关产品推荐

