开发者

How do I get a list of connected sockets/clients with Socket.IO?

I'm trying to get a list of all the sockets/clients that are currently connected.

io.sockets does not return an array, unfortunately.

I know I could keep my own list using an array, but I don't think this is an optimal solution for two reasons:

  1. Redundancy. Socket.IO already keeps a copy of this list.

  2. Socket.IO provides method to set arbitrary field values fo开发者_Python百科r clients (i.e: socket.set('nickname', 'superman')), so I'd need to keep up with these changes if I were to maintain my own list.

What should I do?


In Socket.IO 0.7 you have a clients method on the namespaces. This returns an array of all connected sockets.

API for no namespace:

var clients = io.sockets.clients();
var clients = io.sockets.clients('room'); // all users from room `room`

For a namespace

var clients = io.of('/chat').clients();
var clients = io.of('/chat').clients('room'); // all users from room `room`

Note: This solution only works with a version prior to 1.0

From 1.x and above, please refer to getting how many people are in a chat room in socket.io.


Socket.IO 1.4

Object.keys(io.sockets.sockets); gives you all the connected sockets.

Socket.IO 1.0

As of Socket.IO 1.0, the actual accepted answer isn't valid any more.

So I made a small function that I use as a temporary fix:

function findClientsSocket(roomId, namespace) {
    var res = []
    // The default namespace is "/"
    , ns = io.of(namespace ||"/");

    if (ns) {
        for (var id in ns.connected) {
            if(roomId) {
                var index = ns.connected[id].rooms.indexOf(roomId);
                if(index !== -1) {
                    res.push(ns.connected[id]);
                }
            } else {
                res.push(ns.connected[id]);
            }
        }
    }
    return res;
}

The API for no namespace becomes:

// var clients = io.sockets.clients();
// becomes:
var clients = findClientsSocket();

// var clients = io.sockets.clients('room');
// all users from room `room`
// becomes
var clients = findClientsSocket('room');

The API for a namespace becomes:

// var clients = io.of('/chat').clients();
// becomes
var clients = findClientsSocket(null, '/chat');

// var clients = io.of('/chat').clients('room');
// all users from room `room`
// becomes
var clients = findClientsSocket('room', '/chat');

Also see this related question, in which I give a function that returns the sockets for a given room.

function findClientsSocketByRoomId(roomId) {
    var res = []
    , room = io.sockets.adapter.rooms[roomId];
    if (room) {
        for (var id in room) {
        res.push(io.sockets.adapter.nsp.connected[id]);
        }
    }
    return res;
}

Socket.IO 0.7

The API for no namespace:

var clients = io.sockets.clients();
var clients = io.sockets.clients('room'); // All users from room `room`

For a namespace

var clients = io.of('/chat').clients();
var clients = io.of('/chat').clients('room'); // All users from room `room`

Note: Since it seems the Socket.IO API is prone to breaking, and some solution rely on implementation details, it could be a matter of tracking the clients yourself:

var clients = [];

io.sockets.on('connect', function(client) {
    clients.push(client);

    client.on('disconnect', function() {
        clients.splice(clients.indexOf(client), 1);
    });
});


After Socket.IO 1.0, we cannot use

io.sockets.clients();

or

io.sockets.clients('room');

any more.

Instead you can use the following:

var clients_in_the_room = io.sockets.adapter.rooms[roomId];
for (var clientId in clients_in_the_room ) {
  console.log('client: %s', clientId); // Seeing is believing
  var client_socket = io.sockets.connected[clientId]; // Do whatever you want with this
}


Using Socket.IO 1.x:

Get an array of the connected clients:

io.engine === io.eio // => true
Object.keys(io.engine.clients) // => [ 'US8AxrUrrDF_G7ZUAAAA', 'Ov2Ca24Olkhf2NHbAAAB' ]
Object.keys(io.eio.clients)    // => [ 'US8AxrUrrDF_G7ZUAAAA', 'Ov2Ca24Olkhf2NHbAAAB' ]

Get the number of connected clients:

io.engine.clientsCount // => 2
io.eio.clientsCount    // => 2


It is very simple in Socket.IO 1.3:

io.sockets.sockets - is an array containing the connected socket objects.

If you stored the username in each socket, you can do:

io.sockets.sockets.map(function(e) {
    return e.username;
})

Boom. You have the names of all connected users.


I've gone through this pain today. Socket.IO would be much better if they could make a proper documentation for their API.

Anyway, I tried to look into io.sockets and found a number of options we can use:

io.sockets.connected //Return {socket_1_id: {}, socket_2_id: {}} . This is the most convenient one, since you can just refer to io.sockets.connected[id] then do common things like emit()
io.sockets.sockets //Returns [{socket_1}, {socket_2}, ....]. Can refer to socket_i.id to distinguish
io.sockets.adapter.sids //Return {socket_1_id: {}, socket_2_id: {}} . Looks similar to the first one but the object is not actually the socket, just the information.

// Not directly helps but still relevant
io.sockets.adapter.rooms // Returns {room_1_id: {}, room_2_id: {}}
io.sockets.server.eio.clients // Return client sockets
io.sockets.server.eio.clientsCount // Return number of connected clients

Also, do note that when using socket.io with namespace, the above methods will break since io.sockets becomes an array instead of an object. To resolve, just replace io.sockets by io (i.e., io.sockets.connected becomes io.connected, io.sockets.adapter.rooms becomes io.adapter.rooms...)

It was tested on Socket.IO 1.3.5.


Version 2.x

In version 2.x you specify the namespace/room/node you are querying against.

As with broadcasting, the default is all clients from the default namespace ('/'):

const io = require('socket.io')();  
io.clients((error, clients) => {
      if (error) throw error;
      console.log(clients); // => [6em3d4TJP8Et9EMNAAAA, G5p55dHhGgUnLUctAAAB]
});

Gets a list of client IDs connected to specific namespace (across all nodes if applicable).

const io = require('socket.io')();
io.of('/chat').clients((error, clients) => {
     if (error) throw error;
     console.log(clients); // => [PZDoMHjiu8PYfRiKAAAF, Anw2LatarvGVVXEIAAAD]
});

An example to get all clients in namespace's room:

const io = require('socket.io')();
io.of('/chat').in('general').clients((error, clients) => {
      if (error) throw error;
      console.log(clients); // => [Anw2LatarvGVVXEIAAAD] 
});

This is from the official documentation: Socket.IO Server-API


Update Socket.IO v4.0+ (last checked Nov 6 2022)

I've tried all of the other answers... None of them worked, except this:

The easiest way to get all the connected sockets is through:

await io.fetchSockets()

It returns an array of all connected sockets

Documentation

// Return all Socket instances
const sockets = await io.fetchSockets();

// Return all Socket instances in the "room1" room of the main namespace
const sockets = await io.in("room1").fetchSockets();

// Return all Socket instances in the "room1" room of the "admin" namespace
const sockets = await io.of("/admin").in("room1").fetchSockets();

// This also works with a single socket ID
const sockets = await io.in(theSocketId).fetchSockets();

Example usages

// With an async function

 const sockets = await io.fetchSockets()
 sockets.forEach(socket => {
    // Do something
 });
// Without an async function

io.fetchSockets()
.then((sockets) => {
  sockets.forEach((socket) => {
    // Do something
  })
})
.catch(console.log)


I think we can access the socket object from the server, and you can assign the nickname, and point its socket id,

io.sockets.on('connection',function(socket){ 
    io.sockets.sockets['nickname'] = socket.id;
    client.on("chat", function(data) {      
        var sock_id = io.sockets.sockets['nickname']
        io.sockets.sockets[sock_id].emit("private", "message");
    });    
});

On disconnect please remove the nickname from io.sockets.sockets.


This is the best way to access it in Socket.IO 1.3:

Object.keys(socket.adapter.rooms[room_id])


Version 4.0 and later (2021)

None of the answers worked for me. I will spare you the pain. Their API and documentation have changed greatly since 1.0 onward.

Server API: all available options

But you need to dig deeper here.

// Return all Socket instances
var clients = io.sockets;

clients.sockets.forEach(function(data, counter) {

    //console.log(data); // Maps

    var socketid = data.id; // Log ids
    var isConnected = data.connected // true, false;

});

UPDATE 2023 - Get All Connected Users

I want to add to this answer by showing you how you can attach the userid to the socketid to create persistence if you need it to use it in a project(ie: private messaging).

client.js (client-side)

/***
*
*
* getConnectedUsers
*
*
*
*/
function getConnectedUsers(){
/**
*
*
* STEP 1
* GET
*
*
*/
//
var userID = localStorage.getItem('userid');//this typically would be the unique id from your database. Set this variable at login()
//set-username
socket.auth = { userID };
//console.log(socket.auth);
//
//get-connected-socket-users
socket.emit('get-connected-socket-users',{
userid:userID
});
}
/**
*
*
* STEP 2
* SET
* use this for instant communication
*
*/
socket.on('connected-socket-users',function(data){

//console.log(data);
var connectedUsers = JSON.stringify(data);

localStorage.setItem('connectedUsers',connectedUsers);
//console.log(localStorage.getItem('connectedUsers'));


});

server.js (server side)

/**
* 
* 
* GET ALL CONNECTED USERS
* 
* 
*/
socket.on('get-connected-socket-users',function(data){
//
//variables
var userid = data.userid;
socket.username = userid;
//console.log(io.sockets);
//console.log(userid);
/**
* 
* GET ALL CONNECTED USERS
* 
*/
const users = [];
var clients = io.sockets;
clients.sockets.forEach(function(data,counter){

users.push({
userSocketID: data.id,
username: data.username,
});

});


//console.log(users);
//var handle = 
setInterval(function(){

socket.emit("connected-socket-users", users);

}, 3000);


// When you want to cancel it:
//clearInterval(handle);
//handle = 0; // I just do this so I know I've cleared the interval


});


For anyone that just wants a count of the connected clients, I believe this will do it:

io.sockets.manager.server.connections


In Socket.IO 1.4

To get the array of all connected users:

var allConnectedClients = Object.keys(io.sockets.connected); // This will return the array of SockeId of all the connected clients

To get the count of all clients:

var clientsCount = io.engine.clientsCount ; // This will return the count of connected clients


Socket.IO 1.4.4

Sample code for you.

function get_clients_by_room(roomId, namespace) {
    io.of(namespace || "/").in(roomId).clients(function (error, clients) {
        if (error) { 
            throw error;
        }

        console.log(clients[0]); // => [Anw2LatarvGVVXEIAAAD]
        console.log(io.sockets.sockets[clients[0]]); // Socket detail
        return clients;
    });
}


As of Socket.IO 1.5, note the change from indexOf which appears to de deprecated, and replaced by valueOf

function findClientsSocket(roomId, namespace) {
    var res = [];
    var ns = io.of(namespace ||"/");    // The default namespace is "/"

    if (ns) {
        for (var id in ns.connected) {
            if (roomId) {
                //var index = ns.connected[id].rooms.indexOf(roomId) ;
                var index = ns.connected[id].rooms.valueOf(roomId) ; //Problem was here

                if(index !== -1) {
                    res.push(ns.connected[id]);
                }
            } else {
                res.push(ns.connected[id]);
            }
        }
    }
    return res.length;
}

For Socket.IO version 2.0.3, the following code works:

function findClientsSocket(io, roomId, namespace) {
    var res = [],
        ns = io.of(namespace ||"/");    // the default namespace is "/"

    if (ns) {
        for (var id in ns.connected) {
            if(roomId) {
                // ns.connected[id].rooms is an object!
                var rooms = Object.values(ns.connected[id].rooms);
                var index = rooms.indexOf(roomId);
                if(index !== -1) {
                    res.push(ns.connected[id]);
                }
            }
            else {
                res.push(ns.connected[id]);
            }
        }
    }
    return res;
}


io.sockets.sockets.keys()

This helps me.


In Socket.IO 1.3, I've accomplished this in two lines:

var usersSocketIds = Object.keys(chat.adapter.rooms['room name']);
var usersAttending = _.map(usersSocketIds, function(socketClientId){ return chat.connected[socketClientId] })


Socket.io 1.7.3 or later:

function getConnectedList ()
{
    let list = []
    
    for (let client in io.sockets.connected)
    {
        list.push(client)
    }
    
    return list
}

console.log(getConnectedList())

// returns [ 'yIfhb2tw7mxgrnF6AAAA', 'qABFaNDSYknCysbgAAAB' ]


For version 2.3, this works and it will get you the socket too. It seems to me that socketIo is changing too fast and to much with to little readable documentation after using it for a while.

ioSite.of('/').in(roomId).clients((error, clients) => {
    if (error) throw error;
    for (var i=0; i<clients.length; i++) {
        clientId = clients[i];
        console.log(clientId);

        // Load the socket of your namespace
        var socket = ioSite.of('/').in(roomId).connected[clientId]
        console.log(socket.constructor.name);
          console.log(socket.id);
    }
});

Still this does not feel right, as I have always this feeling with Socket.IO somehow.


For cluster mode, using Redis adapter:

io.in(<room>).clients(function(err, clients) {

});

As each socket is itself a room, one can find whether a socket exist using the same.


Socket.IO 3.0

io.in('room1').sockets.sockets.forEach((socket, key) => {
    console.log(socket);
})

It gets all socket instances in room1.


namespace.allSockets()

It returns a Promise<Set<SocketId>>.

io.allSockets(): All connected socket ids in the main namespace

io.in('room').allSockets(): All connected ids in the 'room'

io.of('/namespace').allSockets(): All connected ids in '/namespace' (you can also combine this with rooms)


To get a list of socket id's, you can do:

[...io.sockets.sockets].map(s => s[0]);

To get the socket object, do:

[...io.sockets.sockets].map(s => s[1]);


As of version 1.5.1, I'm able to access all the sockets in a namespace with:

var socket_ids = Object.keys(io.of('/namespace').sockets);
socket_ids.forEach(function(socket_id) {
    var socket = io.of('/namespace').sockets[socket_id];
    if (socket.connected) {
        // Do something...
    }
});

For some reason, they're using a plain object instead of an array to store the socket IDs.


I believe you can access this from the socket's manager property:

var handshaken = io.manager.handshaken;
var connected = io.manager.connected;
var open = io.manager.open;
var closed = io.manager.closed;


I saw a lot of good answers here and many were quite useful, but not quite what I needed. I am using sockets for a pubsub feature in which an interested client can listen to any changes in a given record.

My specific issue was that the same socket was joining the same room several times. The solution to this was to check if the socket had the room inside its rooms property already.

var room = myObj.id.toString();
if (socket.rooms.indexOf(room) === -1) {
    socket.join(room);
    socket.emit('subscribed', {to : room});
} else {
    console.log("Already in room");
}


This is the simplest way in Socket.IO 1.0 or later, if you are not using namespaces or rooms.

io.nsps["/"].sockets.length

This looks at the default namespace and determines the length of the sockets array, without needing to use Object.keys().


If the project has a socket.io cluster this means the socket.io-redis adapter is being used.

If the case is like the above, getting the all connected sockets id processes must be applied via the socket.io-redis adapter. The examples below might be used for this;

io.of('/').adapter.clients(function (err, clients) {
  console.log("clients: ", clients); // an array containing all connected socket ids
});


io.of('/').adapter.allRooms(function (err, rooms) {
  console.log("all rooms: ", rooms);
});

Please visit socket.io-redis github page for more details.


Socket.IO 4.x

const socketsOnDefaultNamespace = io.of('/').sockets.size;
console.log("Number of clients connected: ", socketsOnDefaultNamespace);

const socketsInARoomInSomeNamespace = io
  .of('/someNamespace')
  .in('/someRoomname')
  .fetchSockets()
  .then((room) => {
      console.log("clients in this room: ", room.length);
    });

You can read more in the documentation.


v.10

var clients = io.nsps['/'].adapter.rooms['vse'];
/* 
'clients' will return something like:
Room {
sockets: { '3kiMNO8xwKMOtj3zAAAC': true, FUgvilj2VoJWB196AAAD: true },
length: 2 }
*/
var count = clients.length;  // 2
var sockets = clients.map((item)=>{  // all sockets room 'vse'
       return io.sockets.sockets[item];
      });
sample >>>
var handshake  = sockets[i].handshake; 
handshake.address  .time .issued ... etc.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜