Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 708
sveti petar Avatar asked Apr 13 '14 16:04

sveti petar


People also ask

Can socket join multiple rooms?

Joining and leaving​ In that case, a union is performed: every socket that is at least in one of the rooms will get the event once (even if the socket is in two or more rooms). In that case, every socket in the room excluding the sender will get the event. To leave a channel you call leave in the same fashion as join .

How do you join a room in a socket?

Joining Rooms You can call the join method on the socket to subscribe the socket to a given channel/room. For example, let us create rooms called 'room-<room-number>' and join some clients. As soon as this room is full, create another room and join clients there.

How do you build a socket room?

Rooms in Socket.IO don't need to be created, one is created when a socket joins it. They are joined on the server side, so you would have to instruct the server using the client. socket. on('create', function (room) { socket.


3 Answers

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)
like image 153
George Marques Avatar answered Oct 07 '22 01:10

George Marques


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!

like image 26
Amol M Kulkarni Avatar answered Oct 06 '22 23:10

Amol M Kulkarni


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'
}
like image 36
Jimmy Adaro Avatar answered Oct 06 '22 23:10

Jimmy Adaro