Keep track of connected sockets with socket.io - javascript

How do I keep track of all the connected clients in socket.io?
I have tried this on the server:
let numSockets = 0;
io.on('connection', (socket) => {
io.sockets.emit('numSockets', ++numSockets);
socket.on('disconnect', () => {
io.sockets.emit('numSockets', --numSockets);
});
});
and this on the client:
const socket = io();
socket.on('numSockets', function (numSockets) {
console.log(numSockets);
});
It does print a number, but the number, however, if I open 2 windows, it shows 4 connected sockets.
Is this the correct way to do it?
What I want to achieve is to print a list of the connected sockets' ids in a sidebar on my website, and let the user set a username (instead of the automatically generated id) if they want to.
But before moving on to this, I will make sure I can keep track of the sockets in a correct way.

I don't think that io.sockets.emit(io.of('/').connected) is a good idea because it will send a hash of socket objects which is a lot of data :-)
You can try the following function:
function findUsersConnected(room, namespace) {
var names = [];
var ns = io.of(namespace || "/");
if (ns) {
for (var id in ns.connected) {
if(room) {
var roomKeys = Object.keys(ns.connected[id].rooms);
for(var i in roomKeys) {
if(roomKeys[i] == room) {
if (ns.connected[id].username) {
names.push(ns.connected[id].username);
} else {
names.push(ns.connected[id].id);
}
}
}
} else {
if (ns.connected[id].username) {
names.push(ns.connected[id].username);
} else {
names.push(ns.connected[id].id);
}
}
}
}
return names.sort();
}
which returns an array of users connected to a room in a namespace. If a socket has not socket.username property then socket.id is used instead.
For instance:
var usersConnected = findUsersConnected();
var usersConnected = findUsersConnected('myRoom');
var usersConnected = findUsersConnected('myRoom', '/myNamespace');

There's the Namespace#connected object that contains all sockets (keyed by their id) that are connected to a particular namespace.
To retrieve the socket id's of the default namespace (/):
let clientIds = Object.keys( io.of('/').connected );
(where io is the server instance)

As of today, socket.io implemented a function called fetchSockets() on server side to retrieve all sockets that are currently connected on the server-side. (Source : https://socket.io/docs/v4/server-instance/#fetchSockets)
You can then use it like this :
const io = require("socket.io")
async function retrieveSockets() {
const connectedSockets = await io.fetchSockets()
}
As far as i tested, you can even execute action on sockets thanks to that, like emitting events, joinings rooms...etc

const count = io.engine.clientsCount;
This seems like a more inclusive approach and may be the same count of Socket instances in the main namespace as below. But depending on namespaces and usage they could be different.
const count2 = io.of("/").sockets.size;
https://socket.io/docs/v4/server-api/#engineclientscount

I believe it is Object.keys(io.sockets.connected).length. Check server api http://socket.io/docs/server-api/

Related

JavaScript issue with import/export modules and Globally defined variables

(note, I'm using Node.js to create a telnet server and handle user input)
I'm having issues using variables/arrays in javascript inside functions in other scripts.
Let me set a simpler example:
var connections = []
is an array in my main.js
Broadcast.js is a function I put in a separate module and attempt to use the broadcast() function in my main.js.
The error I get is stating that connections is undefined. How do get Broadcast.js able to see the connections array in the main.js
~~
For example in my main.js I set an array to handle clients connecting to a server
//point to Telnet library
const Telnet = require('ranvier-telnet');
const logger = require('./logger.js')
var outspeak = []
var connections = []
var clients = []
let server = new Telnet.TelnetServer(rawSocket => {
let telnetSocket = new Telnet.TelnetSocket();
//attaching socket
telnetSocket.attach(rawSocket);
//setting telnet options
telnetSocket.telnetCommand(Telnet.Sequences.WILL, Telnet.Options.OPT_EOR);
//giving clients a name
telnetSocket.name = rawSocket.remoteAddress + ":" + rawSocket.remotePort
//pushing client names to array
clients.push(telnetSocket.name);
//pushing client connections to an array
connections.push(rawSocket);
console.log(`${telnetSocket.name} has connected`)
logger(`${telnetSocket.name} has connected`)
broadcast(telnetSocket.name + " connected.")
telnetSocket.on('data', function (data) {
//broadcast (telnetSocket.name + ">" + data, telnetSocket);
}
function broadcast (message, sender) {
connections.forEach(function (connection) {
//don't want to send it to sender
if (connection === sender) return;
connection.write(`${message} \n`);
});
}
Now inside my main script, I could called that array/push to that array, read from that array, as long as I type out the function inside the main.js file.
And it can easily use the broadcast function.
Now I want to make it more advance and make reduce my lines on my main.js
but once I separate the broadcast function into it's own module.
use strict'
//broadcast function
function broadcast (message, sender) {
connections.forEach(function (connection) {
//don't want to send it to sender
if (connection === sender) return;
connection.write(`${message} \n`);
});
}
module.exports = broadcast
I get a connection undefined error any time I try to invoke that broadcast function. It's like my global variable/array can't be seen by broadcast.js function.
this is how I'm invoking it
// handle input
telnetSocket.on('data', function (data) {
broadcast (telnetSocket.name + ">" + data, telnetSocket);
});
And yes, const broadcast = require('./broadcast.js'); as been added to the file at the top.
Here's the broken code complete:
'use strict'
//point to Telnet library
const Telnet = require('ranvier-telnet');
const logger = require('./logger.js');
const broadcast = require('./broadcast.js');
var connections = []
var clients = []
//had to call message as global variable
//Asan's timestamp functionm
//telnetstuff
console.log("Starting...");
let server = new Telnet.TelnetServer(rawSocket => {
let telnetSocket = new Telnet.TelnetSocket();
//attaching socket
telnetSocket.attach(rawSocket);
//setting telnet options
telnetSocket.telnetCommand(Telnet.Sequences.WILL, Telnet.Options.OPT_EOR);
//giving clients a name
telnetSocket.name = rawSocket.remoteAddress + ":" + rawSocket.remotePort
//pushing client names to array
clients.push(telnetSocket.name);
//pushing client connections to an array
connections.push(rawSocket);
console.log(`${telnetSocket.name} has connected`)
logger(`${telnetSocket.name} has connected`)
broadcast(telnetSocket.name + " connected.")
// handle input
telnetSocket.on('data', function (data) {
broadcast (telnetSocket.name + ">" + data, telnetSocket);
});
//removing client/connection from array
rawSocket.on('end', function () {
clients.splice(clients.indexOf(telnetSocket), 1);
connections.splice(connections.indexOf(rawSocket), 1);
broadcast(telnetSocket.name + " has left.\n");
logger(telnetSocket.name + " has left.");
console.log(telnetSocket.name + " has left.");
});
}).netServer
server.listen(4000);
console.log('ServerRunning...');
logger('>Server started.');
What I'm missing here? Also I apologize in advance this is my first question ever asked and I've gone through as much I could today to even figure out how to ask my question, maybe I'm not using correct lingo/terms? any help is appreciative.
refactor\broadcast.js:5
connections.forEach(function (connection) {
^
ReferenceError: connections is not defined
In nodejs, when you declare a variable not inside any function definitions, it is scoped to the file only. (This is different from browser javascript.) If you want something to be accessible from outside, you need to export it:
module.exports.connections = connections;
Then import it into the other file:
const connections = require(myFile);
This will work as long as you don't try to set the value of the variable in either file, but if you do that they'll end up pointing to separate objects. But mutating it, calling methods on it, etc should work fine.

Working with multiple tabs with Socket.io

I've the following code working in my server-side, it's all ok. But, I want to keep the same connection between n tabs, because when I open a new tab, looks like I've disconnected from the first tab... So, how can I keep the same connection?
client.js
socket.emit("connected", {user: inputUser.val()};
app.js
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
users[socket.user] = socket;
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("typing", function(data) {
var userMsg = data.user;
if(userMsg in users) {
users[userMsg].emit("typing", {user: socket.user});
}
});
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
delete users[socket.user];
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
Update:
The typing event above works fine... So I tried the typing event according to the answer:
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
// add this socket to the Set of sockets for this user
if (!users[socket.user]) {
users[socket.user] = new Set();
}
users[socket.user].add(socket);
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("typing", function(data) {
var userMsg = data.user;
if(userMsg in users) {
users[userMsg].emit("typing", {user: socket.user});
}
});
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
// remove socket for this user
// and remove user if socket count hits zero
if (users[socket.user]) {
users[socket.user].delete(socket);
if (users[socket.user].size === 0) {
delete users[socket.user];
}
}
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
But it is giving the following error:
users[userMsg].emit("typing", {user: socket.user});
^
TypeError: users[userMsg].emit is not a function
Update²:
To fix the typing event error, I just changed to:
socket.on("typing", function(data) {
var userMsg = data.user;
if(userMsg in users) {
for(let userSet of users[userMsg]) {
userSet.emit("typing", {user: socket.user});
}
}
});
There is no simple way to share a single socket.io connection among multiple tabs in the same browser. The usual model for multiple tabs would be that each tab just has its own socket.io connection.
The opening of a new tab and a new socket.io connection should not, on its own, cause your server to think anything was disconnected. If your code is doing that, then that is a fault in your code and it is probably easier to fix that particular fault.
In fact, if you want to explicitly support multiple tabs and be able to recognize that multiple tabs may all be used by the same user, then you may want to change your server side code so that it can keep track of multiple sockets for a single user, rather than how it is currently coded to only keep track of one socket per user.
If your server code is really just trying to keep track of which users online, then there's probably an easier way to do that by referencing counting each user. I will post a code example in a bit.
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
// increment reference count for this user
if (!users[socket.user]) {
users[socket.user] = 0;
}
++users[socket.user];
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
// decrement reference count for this user
// and remove user if reference count hits zero
if (users.hasOwnProperty(socket.user)) {
--users[socket.user];
if (users[socket.user] === 0) {
delete users[socket.user];
}
}
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
If you need the users object to have the socket object in it, then you can change what is stored in the users object to be a Set of sockets like this:
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
// add this socket to the Set of sockets for this user
if (!users[socket.user]) {
users[socket.user] = new Set();
}
users[socket.user].add(socket);
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
// remove socket for this user
// and remove user if socket count hits zero
if (users[socket.user]) {
users[socket.user].delete(socket);
if (users[socket.user].size === 0) {
delete users[socket.user];
}
}
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
For anyone still having this issue. here is how i fixed it.
let me explain.
once the page refreshes or a new tab is opened, socket dosen't really care so it opens a new connection every time . this is more of a advantage than disadvantage. the best way to tackle the issue is on the server side, once a user logs in with his or her user name , you can send that name along with the query options on the client so it can be used as a unique identifier. in my case i used a token
this.socket = io.connect(`${environment.domain}` , {
query: {token: this.authservice.authToken}
});
then on the server side you can create an empty array to a key and an array of values. the username of the user will be used as a key and the corresponding array of socket as the value. in my own case like i said i used a token
const users = [ ]
socket.nickname = (decoded token username);
users[socket.nickname] = [socket];
then you can perform a simple logic to check if a user already exists in an array, if it does, push the new socket to the array of the user
if ( user.username in users) {
console.log('already exists')
users[user.username].push(socket);
}
if it dosent, just create a new key and add the socket as the key.(make sure its an array because a user can always refresh or open a new tab with the same account and you dont want the chat message to deliver in one tab and not deliver in another)
else {
socket.nickname = username;
users[socket.nickname] = [socket];
}
then to emit a message you simply loop through the array and emit the message accordingly. this way each tab gets the message
socket.on('chat', (data) => {
if (data.to in users) {
for(let i = 0; i < users[data.to].length; i++) {
users[data.to][i].emit('chat', data)
}
for(let i = 0; i < users[data.user].length; i++) {
users[data.user][i].emit('chat', data)
}
}
})
you can add a disconnect logic to remove the socket from the users array too to save memory, so only currently open tabs acre active and closed tabs are removed. i hope it solved your problem
My solution is joining socket to a room with specific user Id.
io.on('connection', async (socket) => {
socket.join('user:' + socket.handshake.headers.uid) // The right way is getting `uid` from cookie/token and verifying user
})
One advantage is sending data to specific user (sending to all tabs)
io.to('user:' + uid).emit('hello');
Hope it's helpful!
I belive the best way is create a channel for the user and unique it by their ID, so, when you need to receive or send something you use the channel and every socket connected to it will receive.
Another solution is to save the flag to localStorage and use eventListener to change localStorage.
Do not connect when another connection exists.
and save message in local storage for send with master tab.

node.js eventListener not listen

i'm a noob of node.js and i'm following the examples on "Node.js in action".
I've a question about one example :
The following code implements a simple chat server via telnet. When i write a message, the script should send message to all connected client.
var events = require('events');
var net = require('net');
var channel = new events.EventEmitter();
channel.clients = {};
channel.subscriptions = {};
channel.on('join',function(id,client){
this.clients[id] = client;
this.subscriptions[id] = function(senderId,message){
if(id != senderId){
this.clients[id].write(message);
}
};
this.on('broadcast',this.subscriptions);
});
var server = net.createServer(function(client){
var id = client.remoteAddress+':'+client.remotePort;
client.on('connect',function(){
channel.emit('join',id,client);
});
client.on('data',function(data){
data = data.toString();
channel.emit('broadcast',id,data);
});
});
server.listen(8888);
But when i try to connect via telnet and send a message it doesn't work.
Thanks
A couple issues I noticed. See the comments in the code.
var events = require('events');
var net = require('net');
var channel = new events.EventEmitter();
channel.clients = {};
channel.subscriptions = {};
channel.on('join',function(id, client) {
this.clients[id] = client;
this.subscriptions[id] = function(senderId,message) {
if(id != senderId)
this.clients[id].write(message);
};
//added [id] to "this.subscriptions"
//Before you were passing in the object this.subscriptions
//which is not a function. So that would have actually thrown an exception.
this.on('broadcast',this.subscriptions[id]);
});
var server = net.createServer(function(client) {
//This function is called whenever a client connects.
//So there is no "connect" event on the client object.
var id = client.remoteAddress+':'+client.remotePort;
channel.emit('join', id, client);
client.on('data',function(data) {
data = data.toString();
channel.emit('broadcast',id,data);
});
});
server.listen(8888);
Also note: If a client disconnects and another client sends a message then this.clients[id].write(message); will throw an exception. This is because, as of now, you're not listening for the disconnect event and removing clients which are no longer connected. So you'll attempt to write to a client which is no longer connected which will throw an exception. I assume you just haven't gotten there yet, but I wanted to mention it.

Socket IO Rooms: Get list of clients in specific room

I'm trying to display a list of clients in a specific room. I just want to show their username, and not their socket id.
This where I'm at:
socket.set('nickname', "Earl");
socket.join('chatroom1');
console.log('User joined chat room 1);
var roster = io.sockets.clients('chatroom1');
for ( i in roster )
{
console.log('Username: ' + roster[i]);
}
Haven't had any luck getting it to list Socket IDs or anything. Would like it to return the nicknames however.
In socket.IO 3.x
New to version 3.x is that connected is renamed to sockets and is now an ES6 Map on namespaces. On rooms sockets is an ES6 Set of client ids.
//this is an ES6 Set of all client ids in the room
const clients = io.sockets.adapter.rooms.get('Room Name');
//to get the number of clients in this room
const numClients = clients ? clients.size : 0;
//to just emit the same event to all members of a room
io.to('Room Name').emit('new event', 'Updates');
for (const clientId of clients ) {
//this is the socket of each client in the room.
const clientSocket = io.sockets.sockets.get(clientId);
//you can do whatever you need with this
clientSocket.leave('Other Room')
}
In socket.IO 1.x through 2.x
Please refer the following answer:
Get list of all clients in specific room. Replicated below with some modifications:
const clients = io.sockets.adapter.rooms['Room Name'].sockets;
//to get the number of clients in this room
const numClients = clients ? Object.keys(clients).length : 0;
//to just emit the same event to all members of a room
io.to('Room Name').emit('new event', 'Updates');
for (const clientId in clients ) {
//this is the socket of each client in the room.
const clientSocket = io.sockets.connected[clientId];
//you can do whatever you need with this
clientSocket.leave('Other Room')
}
Instead of going deep in socket/io object , You can use simple and standard way :
io.in(room_name).clients((err , clients) => {
// clients will be array of socket ids , currently available in given room
});
For more detail DO READ
Just a few things.
when you have the socket you can then set the properties like: socket.nickname = 'Earl'; later to use the save property for example in a console log:
console.log(socket.nickname);
you where missing a closing quote (') in your:
console.log('User joined chat room 1);
Im not entirely sure about your loop.
Below is the amended code should help you out a bit, also be aware the loop i am using below is asynchronous and this may effect how you handle data transfers.
socket.nickname = 'Earl';
socket.join('chatroom1');
console.log('User joined chat room 1');
var roster = io.sockets.clients('chatroom1');
roster.forEach(function(client) {
console.log('Username: ' + client.nickname);
});
to help you out more i would need to see all your code as this does not give me context.
For v4 I used this method fetchSockets()
Example :
let roomUsers=await io.in(`room-id`).fetchSockets()
see documentation here :
https://socket.io/docs/v3/migrating-from-3-x-to-4-0/#Additional-utility-methods
All the answers above and the one here socket.io get rooms which socket is currently in or here Socket.IO - how do I get a list of connected sockets/clients? were either incorrect or incomplete if you use 2.0.
In 2.0, io.sockets.manager and io.sockets.clients don't exist anymore.
Without using namespace, the following 3 parameters can all get sockets in a specific room.
socket.adapter.rooms;
io.sockets.adapter.rooms;
io.sockets.adapter.sids; // the socket.id array
With namespace (I used "cs" here), io.sockets.adapter.rooms will give a quite confusing result and the result socket.adapter.rooms gives is correct:
/* socket.adapter.rooms give: */
{
"/cs#v561bgPlss6ELZIZAAAB": {
"sockets": {
"/cs#v561bgPlss6ELZIZAAAB": true
},
"length": 1
},
"a room xxx": {"sockets": {
"/cs#v561bgPlss6ELZIZAAAB": true
},
"length": 1
}
}
/* io.sockets.adapter.rooms give: a sid without namespace*/
{
"v561bgPlss6ELZIZAAAB": {
"sockets": {
"v561bgPlss6ELZIZAAAB": true
}, "length": 1
}
}
Note: the default room is this: "Each Socket in Socket.IO is identified by a random, unguessable, unique identifier Socket#id. For your convenience, each socket automatically joins a room identified by this id."
I only tried memory adapter so far, have not tried redis-adapter.
For socket.IO v3 there's a breaking change here:
Namespace.clients() is renamed to Namespace.allSockets() and now returns a Promise.
BEFORE:
// all sockets in the "chat" namespace and in the "general" room
io.of("/chat").in("general").clients((error, clients) => {
console.log(clients); // => [Anw2LatarvGVVXEIAAAD]
});
Now (v3):
// all sockets in the "chat" namespace and in the "general" room
const ids = await io.of("/chat").in("general").allSockets();
Source
In case you're not so familiar with socket.IO, it might be good to know that instead of io.of("/chat") you can write io to use the default namespace.
For Socket v.4 correct syntax would be:
const sockets = await io.in("room1").fetchSockets();
https://socket.io/docs/v4/server-api/#namespacefetchsockets
socket.io ^ 2.0
function getRoomClients(room) {
return new Promise((resolve, reject) => {
io.of('/').in(room).clients((error, clients) => {
resolve(clients);
});
});
}
...
const clients = await getRoomClients('hello-world');
console.log(clients);
Output
[ '9L47TWua75nkL_0qAAAA',
'tVDBzLjhPRNdgkZdAAAB',
'fHjm2kxKWjh0wUAKAAAC' ]
From Socket.io v3, rooms is now a protected property of Adapter so you won't be able to access it via io.sockets.adapter.rooms.
Instead use:
const clientsInRoom = await io.in(roomName).allSockets()
OR for multiple rooms
const clientsInRooms = await io.sockets.adapter.sockets(new Set([roomName, roomName2]))
For Socket.io greater than v1.0 and node v6.0+ use the following code:
function getSockets(room) { // will return all sockets with room name
return Object.entries(io.sockets.adapter.rooms[room] === undefined ?
{} : io.sockets.adapter.rooms[room].sockets )
.filter(([id, status]) => status) // get only status = true sockets
.map(([id]) => io.sockets.connected[id])
}
If you want to emit something to them , use this :
getSockets('room name').forEach(socket => socket.emit('event name', data))
This solution is for
socket.io : "3.0.4"
socket.io-redis : "6.0.1"
import these first
const redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
socket.on('create or join', function(room) {
log('Received request to create or join room ' + room);
//var clientsInRoom = io.sockets.adapter.rooms[room];
mapObject = io.sockets.adapter.rooms // return Map Js Object
clientsInRoom = new Set(mapObject.get(room))
var numClients = clientsInRoom ? clientsInRoom.size : 0;
log('Room ' + room + ' now has ' + numClients + ' client(s)');
https://socket.io/docs/v3/using-multiple-nodes/#The-Redis-adapter
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get
In socket.IO 4.x
const getConnectedUserIdList = async () => {
let connectedUsers = [];
let roomUsers = await io.in(`members`).fetchSockets();
roomUsers.forEach((obj) => {
connectedUsers.push(obj.request.user.id);
});
return connectedUsers;
};
I just logged all sockets in a room to the console, you can do whatever you like with them...
const socketsInRoom = io.adapter.rooms[room_name];
/*Collect all participants in room*/
for(let participant in socketsInRoom){
for(let socketId in socketsInRoom[participant]){
console.log(socketId)
}
}
you can use the adapter method on io object like
io.sockets.adapter.rooms.get("chatroom1")
this will return the list of connected clients in the particular room.
io.sockets.adapter.rooms this is a map off all clients connected to rooms with room name as keys and the connected clients are the values of the room key. map functions are applicable .
socket.io ^2.2.0
const socket = io(url)
socket.on('connection', client => {
socket.of('/').in("some_room_name").clients((err, clients) => {
console.log(clients) // an array of socket ids
})
})
Since I have found very little on how to get the rooms inside a specific namespace, here it is in case anyone is wondering :
io.of(namespaceName).adapter.rooms;
let sockets = await io
.of("/namespace")
.in(ROOM_NAME)
.allSockets();
you can get length of connected clients via
console.log(receiver.size);
You can create an array object of user collection as
var users = {};
then on server side you can add it as new user when you connect
socket.on('new-user', function (username) {
users[username] = username;
});
while displaying the users, you can loop the "users" object
On Client side
var socket = io.connect();
socket.on('connect', function () {
socket.emit('new-user', 'username');
});

getting how many people are in a chat room in socket.io [duplicate]

This question already has answers here:
How do I get a list of connected sockets/clients with Socket.IO?
(35 answers)
Closed 3 years ago.
I have this code right now that sets the nick and room:
io.sockets.on('connection', function(client){
var Room = "";
client.on("setNickAndRoom", function(nick, fn){
client.join(nick.room);
Room = nick.room;
client.broadcast.to(Room).emit('count', "Connected:" + " " + count);
fn({msg :"Connected:" + " " + count});
});
I wanted to know how I could get how many people are connected to a specific chatroom...like Room.length
client side :
function Chat(){
this.socket = null;
this.Nickname = "";
this.Room = "";
var synched = $('#syncUp');
this.Connect = function(nick, room){
socket = io.connect('http://vybeing.com:8080');
Nickname = nick;
Room = room;
//conectarse
socket.on('connect',function (data) {
socket.emit('setNickAndRoom', {nick: nick, room: room}, function(response){
$("#connection").html("<p>" + response.msg + "</p>");
});
});
}
I found this, but it gives undefined:
count = io.rooms[Room].length;
For socket.io versions >= 1.0:
Note that rooms became actual types with a .length property in 1.4, so the 1.4.x method should be stable from now on. Barring breaking changes to that type's API, of course.
To count all clients connected to 'my_room':
1.4+:
var room = io.sockets.adapter.rooms['my_room'];
room.length;
1.3.x:
var room = io.sockets.adapter.rooms['my_room'];
Object.keys(room).length;
1.0.x to 1.2.x:
var room = io.adapter.rooms['my_room'];
Object.keys(room).length;
This is assuming you're running with the default room adapter on a single node (as opposed to a cluster). Things are more complicated if you're in a cluster.
Other related examples:
Count all clients connected to server:
var srvSockets = io.sockets.sockets;
Object.keys(srvSockets).length;
Count all clients connected to namespace '/chat':
var nspSockets = io.of('/chat').sockets;
Object.keys(nspSockets).length
If you're using version < 1,
var clients = io.sockets.clients(nick.room); // all users from room
For socket.io 1.4.6, what worked for me is specifying the namespace in addition to the room. When using the default namespace, you can just specify it as ['/']. For example, to get the number of clients connected to the room 'kitchen' in the default namespace (nsps), you would write:
var io = require('socket.io')();
io.nsps['/'].adapter.rooms['kitchen'].length
Heads up: If no one has joined a room, it hasn't been created yet, therefore io.nsps['/'].adapter.rooms['kitchen'] will return undefined. If you try to call .length on the undefined kitchen your app will crash.
In version 1.4.5
var clientNumber = io.sockets.adapter.rooms[room].length;
I wanted to get a list of users in a room.
This ended up being my solution.
I added a username property to my socket, but for completeness I changed that to "id" which is the id of the socket.
var sockets = io.in("room_name")
Object.keys(sockets.sockets).forEach((item) => {
console.log("TODO: Item:", sockets.sockets[item].id)
})
Socket.io v2.0.3
For socket.io v2.0.3, I ended up running a redis server and use socket.io-redis plugin. Then you can do:
io.of('/').adapter.clients(['room1', 'room2'], (err, clients) => {
console.log(clients); // an array containing socket ids in 'room1' and/or 'room2'
});
code is from https://github.com/socketio/socket.io-redis#redisadapterclientsroomsarray-fnfunction
kevzettler pointed me to socket.io-redis
The other answer for socket.io v2.0.3 from The Lazy Coder didn't work for me, it gave me a list of all connected clients, regardless of the room.
I'm using 1.4.6 and this did the trick:
Object.keys(io.sockets.connected).length
put this in a function and it will give you failsafe to prevent crashing:
var roomCount = io.nsps['/'].adapter.rooms[roomName];
if (!roomCount) return null;
return roomCount.length;

Categories