23

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
sveti petar
  • 3,637
  • 13
  • 67
  • 144

9 Answers9

20

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)
George Marques
  • 820
  • 7
  • 21
  • Note that when using redis to sync multiple nodes the API is different (async): https://github.com/socketio/socket.io-redis#redisadapterclientsroomsarray-fnfunction – Fabiano Soriani Mar 30 '17 at 17:19
  • 3
    this is outdated. at time of writing, `socket.rooms` is now an object with both keys and values set equally, so the test should be `room in socket.rooms` or `Object.keys(socket.rooms).includes(room)` – y_nk May 20 '20 at 07:25
18

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'
}
Jimmy Adaro
  • 1,325
  • 15
  • 26
16

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

This returns you a object with sId e.g.
{"1sEAEIZeuMgdhF35AAAA":true}


Updates specific to versions: 3.0+:
io.sockets.adapter.get('roomId')

1.4:

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

1.3.x:

io.sockets.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, i.e. 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 ;)

P.S. 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!

Community
  • 1
  • 1
Amol M Kulkarni
  • 21,143
  • 34
  • 120
  • 164
2

For current socket.io (1.0+ I suppose) structure of io object was changed, therefore you can now find out is there a user with given socketid in the room with given socket roomid by:

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

Max Yari
  • 3,617
  • 5
  • 32
  • 56
  • 2
    This has changed again, you now require ```if(io.sockets.adapter.rooms[data.groupId].sockets[socket.id])``` – Ryan Apr 01 '16 at 12:56
2

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
  }
});
Parker
  • 83
  • 1
  • 6
  • Seems room names are converted to **PxtiIs22S7GhWPTSAAAA**. Lets' say a client joined a room name **Session-Started**. It will be stored as { PxtiIs22S7GhWPTSAAAA: 'PxtiIs22S7GhWPTSAAAA'}. So there is no way to get plain room names as well. Correct me if I'm wrong. – gwthm.in Feb 20 '17 at 06:55
2

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

deHaar
  • 17,687
  • 10
  • 38
  • 51
2

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);
Saifallak
  • 1,234
  • 2
  • 13
  • 28
1

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);
  }
1

"socket.io": "^4.4.1"

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

Mohit Sharma
  • 622
  • 6
  • 11