简体   繁体   中英

One-line check if socket is in given room?

I'm using Node.js with socket.io for a multiplayer card game, and there are game rooms which players can join.

For joining a room, I simply use:

io.sockets.on('connection', function (socket) {
    socket.on('joinRoom', function (gid) {
        //gid is game ID - create room name based on this and join the room
        var room = 'game'+gid;
        socket.join(room);
    });
});

My question is, what is the quickest way to check if a socket is connected to a certain room? I know I could get all sockets in that room in an array and then check whether the target socket is in the array, but I'm guessing there should be a more basic syntax for this. What I'm looking for (in pseudo-code) would be

if(socket with ID "z8b7dbf98i" is in room "game10")
    //do something

For the documentation, socket.io doesn't seem to have any simple way to do that. You really need to check if the client is in the room array, or the opposite: if the room is in the client array.

This can be done with an oneliner using indexOf :

if(socket.rooms.indexOf(room) >= 0)

Or the opposite search:

if(io.sockets.manager.rooms['/' + room].indexOf(socket.id) >= 0)

You can simply check like this
io.sockets.adapter.rooms['roomId']

This returns you a object with sId eg
{"1sEAEIZeuMgdhF35AAAA":true}


Updates specific to versions: 3.0+:
io.sockets.adapter.rooms['roomId']

1.4:

io.sockets.adapter.rooms['roomId']; 

1.3.x:

io.adapter.rooms['roomId'];

1.0.x to 1.2.x:

 io.adapter.rooms['roomId'];

**Update:**
However one can check socket Id is in a given room or not with one-line as mentioned above only if server architecture has a single node server/single node process.

If you are using multi-node server , ie separate node process with load balanced.

Point to note here is, that the sockets are only registered on the process that they first connected to. So, you need to use socket.io-redis to connect all your nodes together to sync events, and what you can do to maintain list of socket Ids across multi-node is broadcast an event each time a client connects/disconnects, so that each node updates & maintains the real-time list of all the clients/socket Ids.

Background/Details:
The redis adapter extends the base adapter , but it only overrides/adds the following properties :
clients broadcast add del delAll

With the following code:

 io.sockets.adapter.rooms["roomId"]; //Any of the above examples specific to versions mentioned above

you are querying the rooms property on socket.io-redis adapter. This wasn't overridden by the redis adapter, so you're actually querying the base adapter, which only knows about rooms/clients in the current process.

Why didn't the redis adapter override the rooms property? Might be because it would have to query the redis database instance to construct an object containing all rooms and connections on every access of this property. Not a good idea?

So as of this writing answer, you'll have to add that functionality to the adapter itself with a method like this:

 /** * One-Line code/property to check if the socket id is in given room. * * @param {String} room id * @param {Function} callback (optional) * @api public */ Redis.prototype.isSidExistsInRoom = function(room, fn){ ... }

where you will hit the redis database instance.

This should be part of the base adapter interface for all other adapters to implement. It's a common problem everyone will face one day, when they scale their servers ;)

PS Just a hint on another approach is to use the customRequest/customHook methods in socket.io-redis 3.1.0 .


**Update with ver 5.2.0: (relevant multi node servers)**
Now redis adapter gives you rooms across processes/nodes as of 5.2.0
Source: [RedisAdapter#clients(rooms:Array, fn:Function)][5] > Returns the list of client IDs connected to rooms across all nodes. See [Namespace#clients(fn:Function)][6]
 io.of('/').adapter.clients((err, clients) => { console.log(clients); // an array containing all connected socket ids }); io.of('/').adapter.clients(['room1', 'room2'], (err, clients) => { console.log(clients); // an array containing socket ids in 'room1' and/or 'room2' }); // you can also use io.in('room3').clients((err, clients) => { console.log(clients); // an array containing socket ids in 'room3' });


Happy Coding!

2021 response:

This was such a headache for me, but currently in version 4.0.2 of Socket IO, socket.rooms is a Javascript Set , so you can check if the given socket is in the room using .has() :

if (socket.rooms.has('abc')) {
  // Do something if socket is in room 'abc'
} else {
  // Do something if socket is NOT in room 'abc'
}

If you need to check if the user is not in the room, you can simply use ! :

if (!socket.rooms.has('abc')) {
  // Do something if socket is NOT in room 'abc'
}

对于当前的socket.io (我想是 1.0+), io对象的结构已更改,因此您现在可以通过以下方式找出房间中是否存在具有给定socketid的用户和给定的 socket roomid

if(io.sockets.adapter.rooms[roomid][socketid])

This seems to have changed quite a lot with versions of socket.io, but as of this writing (version 1.7.2), this looks like it's stored in socket.rooms . It's an object that looks like this:

{
  room_name: 'room_name',
  second_room_name: 'second_room_name',
  ...
}

Before your socket has joined any rooms, as documented, you'll see that the socket is already in a room with it's own id, so socket.rooms will look something like:

{ PxtiIs22S7GhWPTSAAAA: 'PxtiIs22S7GhWPTSAAAA'}

That means you can check if a socket is in a room something like this:

io.on('connection', function(socket){
  if(socket.rooms[myRoomName]){
    // in the room
  }else{
    // not in the room
  }
});

now socket.rooms looks like that:

{
    "room1":"room1",
    "room2":"room2",
    ...
    "room100":"room100"
}

way to check if a socket is connected to a certain room: if(socket.rooms[roomID]) return true;

answers from link https://github.com/socketio/socket.io/issues/2890

using "socket.io": "^2.3.0" this worked for me

if (!(io.sockets.adapter.rooms[room] && io.sockets.adapter.rooms[room].sockets[socket.id])) 
    // Join room or do any stuff
    socket.join('product_' + product_id);

If you still need it you can do next:

  socket.room = "someRoom";

and then simply check it:

  if (socket.room !== undefined){
        socket.leave(socket.room);
  }

"socket.io": "^4.4.1"

socket.rooms.has('roomName') worked for me.
return true if exist other wise false

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM