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
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 .
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.
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.
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 thisio.sockets.adapter.rooms['roomId']
This returns you a object with sId e.g. {"1sEAEIZeuMgdhF35AAAA":true}
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'];
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.
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'
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With