【问题标题】:Socket.IO - how do I get a list of connected sockets/clients?Socket.IO - 如何获取已连接套接字/客户端的列表?
【发布时间】:2011-09-27 16:17:01
【问题描述】:

我正在尝试获取当前连接的所有套接字/客户端的列表。

io.sockets 不返回数组,不幸的是。

我知道我可以使用数组来保留自己的列表,但我认为这不是最佳解决方案,原因有两个:

  1. 冗余。 Socket.IO 已经保存了这个列表的副本。

  2. Socket.IO 提供了为客户端设置任意字段值的方法(即:socket.set('nickname', 'superman')),因此如果我要维护自己的列表,我需要跟上这些更改。

帮助?

【问题讨论】:

  • 你不能把 io.sockets 转换成一个数组吗?
  • 在 0.7.6 io.sockets.sockets 是一个简单的哈希对象 id => socket
  • 我注意到了,但它表现得很奇怪(看起来不像标准的套接字对象)。例如我试过:pastebin.com/fxuzVckS 但它说套接字对象不包含“get”方法。
  • socket 是 id,io.sockets.sockets[socket] 是套接字。
  • 第一个参数是err,如果你使用内存存储,它总是null.get('nickname', function(err, nickname) { ... })

标签: node.js socket.io


【解决方案1】:

在 Socket.IO 0.7 中,您在命名空间上有一个 clients 方法,这将返回一个包含所有已连接套接字的数组。

无命名空间的 API:

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

对于命名空间

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

希望这对将来的某人有所帮助

注意:此解决方案适用于 1.0 之前的版本


2020 年 3 月 6 日更新

1.x及以上版本请参考此链接:getting how many people are in a chat room in socket.io

【讨论】:

  • 我认为他们已经在 1.0 中删除了 clients 方法......现在只是检查一下......
  • 他们几乎放弃了 1.0 中的所有内容,因为它完全重写了。但由于 1.0 多年来一直未发布。在 1.0 中,您必须使用命名空间的 connected 对象。
  • @3rdEden,使用命名空间的连接对象的语法是什么? var clients = io.sockets.connected(); 显然不起作用。谢谢
  • TypeError: fn.bind 不是函数
【解决方案2】:

我相信您可以从套接字的 manager 属性访问它?

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

【讨论】:

  • 3rdEden 的回答对我不起作用,因为阵列返回断开连接的客户端存在问题,这会导致计数失败。 Object.keys(io.manager.open).length 被证明是我经验中最可靠的计数。 io.of('/namespace').manager.open 如果你正在使用命名空间。
【解决方案3】:

对于任何只想要 COUNT 个已连接客户端的人,我相信这会做到:

io.sockets.manager.server.connections

【讨论】:

  • 不是已连接的客户端计数,经过测试。 (多一点)
  • 为了获得准确的计数,请使用io.sockets.clients().length
  • 目前io.sockets.manager.server.connectionsio.sockets.clients().length 都不起作用。这对我有用-仅用于计数-io.engine.clientsCount 和客户列表-io.engine.clientsCount。因为它一直保持这种状态超过 3 年。似乎是获取连接信息的可靠方法。 (基于 Joseph Dykstra 的回答)
【解决方案4】:

我认为我们可以从服务器访问socket对象,你可以分配昵称,并指向它的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");
    });    
});

disconnect 上,请从io.sockets.sockets 中删除昵称。

【讨论】:

  • 'client.on' 的目的是什么?你能像这样使用你的代码吗(只接受来自 1 个套接字的数据)?:io.sockets.sockets[sock_id].on('newmessage', function (data) { console.log(data) });
【解决方案5】:

在socket.io 1.0之后我们不能使用

io.sockets.clients(); 
or
io.sockets.clients('room'); 

了。相反,您可以使用以下

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
}

【讨论】:

  • 我改成var clients_in_the_room = io.sockets.adapter.rooms[roomId].sockets
【解决方案6】:

Socket.io 1.4

Object.keys(io.sockets.sockets); 为您提供所有连接的套接字。

Socket.io 1.0 从 socket.io 1.0 开始,实际接受的答案不再有效。 所以我做了一个小功能,用作临时修复:

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;
}
No namespace

Api 变为

// 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');
命名空间

API变成:

// 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');

另见related question,我在其中给出了一个返回给定房间的套接字的函数。

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

无命名空间的API:

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

对于一个命名空间

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

注意:由于 socket.io API 似乎很容易崩溃,并且某些解决方案依赖于实现细节,因此可能需要自己跟踪客户端:

var clients = [];

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

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

【讨论】:

  • @TylerScott 我也是,特别是考虑到他们的迁移指南中没有涵盖这一点(除非我遗漏了什么)。 socket.io/docs/migrating-from-0-9
  • @nha,这对我不起作用,但返回一个整数数组,如 0、1 和 2 等,而不是套接字数组。有什么想法吗?
  • 多节点怎么样?因为我在 redis 存储中使用了多个节点
  • @nha 您的回答完全正确,但它不适用于具有 redis 存储服务器的多个节点。从 1 个节点进程中,您无法使用 ns.connected[id] 来检查套接字是否连接
【解决方案7】:

使用 Socket.IO 1.x:

获取已连接客户端的数组:

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

获取连接的客户端数量:

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

【讨论】:

    【解决方案8】:

    这是在 socket.io 1.3 中访问它的最佳方式

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

    【讨论】:

    • 请让我分享这个例子,var room = io.sockets.adapter.rooms[room_id];var connections = _.map(Object.keys(room), function(socketId){ return (io.sockets.connected[socketId].connected == true) });
    【解决方案9】:

    在 socket.io 1.3 中非常简单:

    io.sockets.sockets - 是一个包含已连接套接字对象的数组。 如果您将用户名存储在每个套接字中,您可以这样做:

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

    轰隆隆。您拥有所有已连接用户的姓名。

    【讨论】:

    • 谢谢! +1 为简单起见。您知道这种方法与约瑟夫·戴克斯特拉的回答之间是否有任何区别? (Object.keys(io.engine.clients)
    • 这不适用于 v1.4.5。仍在研究解决方案
    • TypeError: io.sockets.sockets.map 不是函数
    【解决方案10】:

    我在这里看到了很多很好的答案,还有很多非常有用但不是我所需要的。我将套接字用于 pubsub 功能,感兴趣的客户端可以在其中监听给定记录中的任何更改。

    我的具体问题是同一个套接字多次加入同一个房间。解决这个问题的方法是检查套接字是否已经在其 rooms 属性中拥有房间。

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

    希望这对某人有所帮助。

    【讨论】:

    • 我认为如果您让客户端在套接字连接时加入房间,则可以避免使用“if”语句。
    • myObj 从何而来?您是如何得出这个值的?
    • @MikeDraper myObject 是数据库中充当通道的记录。这解决了这样一种情况:您希望在数据库发生更改时更新所有客户端,例如用户更新其名称并且所有连接的客户端都得到更新。因此,我为该实例 ID 创建了一个房间,因为它是唯一的 (BSON)。对于我的用例来说,这是一个非常具体的值,所以如果我造成任何混淆,我很抱歉。如果您需要帮助,请告诉我:)
    • @JMR 由于我的应用程序的性质,一个套接字被多次订阅,这就是我必须添加该检查的原因。
    【解决方案11】:

    在 socket.io 1.3 上,我已经用 2 行完成了这项工作

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

    【讨论】:

      【解决方案12】:

      我今天经历了这种痛苦。如果 Socket.io 能够为他们的 API 制作适当的文档,那会更好。

      无论如何,我尝试查看 io.sockets 并找到了一些我们可以使用的选项:

      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
      

      另外,请注意,当使用带有命名空间的 socket.io 时,上述方法将中断,因为 io.sockets 变成了一个数组而不是一个对象。要解决,只需将 io.sockets 替换为 io(即 io.sockets.connected 变为 io.connected,io.sockets.adapter.rooms 变为 io.adapter.rooms ...)

      在 socket.io 1.3.5 上测试

      【讨论】:

      • @Зелёный 如果您不使用命名空间,它将起作用。如果您使用命名空间,请更改为 io.server.eio.clientsCount
      【解决方案13】:

      这是一种使用 ES6 生成器将连接的套接字的哈希从命名空间转换为数组的快速方法(适用于 socket.io >= v1.0.0):

      io.on('connection', function(socket) {
        var hash = io.of('/').connected
        var list = null
      
        hash[Symbol.iterator] = function*() {
          // for..of loop to invoke Object.keys() default iterator
          // to get the array values instead of the keys
          for(var id of Object.keys(hash)) yield hash[id]
        }
      
        list = [...hash]
        console.log(Array.isArray(list)) // true
      })
      

      【讨论】:

        【解决方案14】:

        如果您不使用命名空间或房间,这是 Socket.IO 1.0+ 中最简单的方法。

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

        这会查看默认命名空间并确定套接字数组的长度,无需使用Object.keys()

        【讨论】:

          【解决方案15】:

          Socket.io 1.4.4

          为您提供示例代码。

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

          我认为这会帮助这个代码块的人。

          【讨论】:

            【解决方案16】:

            从 socket.io 1.5 开始,请注意 indexOf 的变化,它似乎已贬值,并被 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;
            }
            

            对于 socket.io 2.0.3 版,以下代码有效:

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

            【讨论】:

            • 但是socket.io版本还不是1.5!现在是1.4.8
            【解决方案17】:

            我不知道这是否还在继续。但我最终使用的是这样的东西(我在每个连接的套接字上保留一个 session 对象,该对象又包含用户名和其他信息:

            var connectedUsers = Object.keys(io.sockets.connected).map(function(socketId) {
                return io.sockets.connected[socketId].session.username;
            });
            

            【讨论】:

              【解决方案18】:

              在 Socket.IO 1.4 中

              获取所有已连接用户的数组:

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

              获取所有客户端的计数:

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

              【讨论】:

              • 我确认这在 socket.io 的 1.5.1 版本中有效
              【解决方案19】:

              从 1.5.1 版开始,我可以通过以下方式访问命名空间中的所有套接字:

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

              出于某种原因,他们使用普通对象而不是数组来存储套接字 ID。

              【讨论】:

              • 这个有效。 io.of('') 中的空字符串可以在没有命名空间的情况下使用。
              • @KirályIstván 我已经有一段时间没有研究这个问题了,但我认为在这种情况下你可以完全删除 of() 链式调用。
              【解决方案20】:

              Socket.io 1.7.3(+):

              函数获取连接列表() { 让列表 = [] for(让客户端进入 io.sockets.connected ) { list.push(客户端) } 返回列表 } 控制台日志(getConnectedList()) // 返回 [ 'yIfhb2tw7mxgrnF6AAAA', 'qABFaNDSYknCysbgAAAB' ]

              【讨论】:

                【解决方案21】:

                如果项目有 socket.io 集群,则表示正在使用 socket.io-redis 适配器。

                如果是上述情况,获取所有连接的sockets id进程必须通过socket.io-redis适配器应用。下面的例子可以用于此;

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

                请访问socket.io-redis github page了解更多详情。

                【讨论】:

                  【解决方案22】:

                  版本 +2.0

                  在 +2.0 版本中,您指定要查询的命名空间/房间/节点。

                  与广播一样,默认是来自默认命名空间 ('/') 的所有客户端:

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

                  获取连接到特定命名空间的客户端 ID 列表(如果适用,跨所有节点)。

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

                  获取命名空间房间内所有客户端的示例:

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

                  这是来自官方文档:Socket.IO Server-API

                  【讨论】:

                  • 有没有办法查看所有套接字对象?例如,如果我在连接时向每个套接字添加 socket.username 属性,有没有办法可以查看所有这些套接字对象(包括我添加到每个套接字的自定义属性)? (作为参考,我目前正在使用全局对象变量来存储客户端连接时的其他套接字信息 - 所以不询问如何设置它 - 只是想知道是否有办法通过“默认”查看所有套接字对象) .
                  • hmm,per this answer,我认为您可以看到所有使用console.log(io.sockets.connected) 连接的套接字 - 它似乎是一个对象,其中每个属性值都是一个“套接字对象”,其中包含这些属性nspserveradaptorid(字符串),clientconnroomsacksconnecteddisconnectedfns38@3, 987654340@、_rooms_events_eventsCount 最后是username,这是我在连接时添加到每个套接字的自定义属性。
                  【解决方案23】:

                  对于 2.3 版,它可以工作,而且它也会为您提供套接字,在我看来,socketIo 的变化太快了,而且在使用了一段时间后,可读文档很少。

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

                  这仍然感觉不对,因为我总是以某种方式对套接字 Io 有这种感觉

                  【讨论】:

                    【解决方案24】:

                    集群模式,使用redis-adaptor

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

                    由于每个插座本身就是一个房间,因此可以使用相同的插座来查找插座是否存在。

                    【讨论】:

                      【解决方案25】:

                      socket.io@1.7.3

                      我使用 Object.Keys 来连接套接字数组。然后在同一个数组中用 map 函数迭代构建一个新的对象数组

                      var connectedUsers = Object.keys(io.sockets.connected).map(function(socketId) {
                          return { socket_id : socketId, socket_username: io.sockets.connected[socketId].username };
                      });
                      
                      // test
                      console.log(connectedUsers);
                      

                      也许这个答案可以帮助获取套接字 id/用户名数组。

                      【讨论】:

                      • 嗨 - 你能用英语解释一下这是如何解决问题的吗?这样,它就不仅仅是一个代码答案。谢谢!
                      【解决方案26】:

                      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.
                      

                      【讨论】:

                      • 如果您可以添加一些上下文为什么“这是最好的方法”以及您的答案如何为上述所有答案添加任何内容,那就太好了。代码是东西,但答案也应该有一些解释。如您所见,这个问题还有许多其他答案。你的答案必须给主题带来更多的东西,否则就是多余的。
                      • 使用socket.io 2.1.1 ,我得到TypeError: clients.map is not a function,虽然它看起来很有希望,但我不完全确定上面的例子会实现什么——我希望它会自动创建一个客户端对象数组包括用户名:)。
                      【解决方案27】:

                      socket.io@3.0

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

                      room1 中的所有套接字实例

                      【讨论】:

                        【解决方案28】:

                        namespace.allSockets()

                        返回承诺

                        io.allSockets() 主命名空间中所有连接的套接字 id

                        io.in('room').allSockets() // '房间'中所有连接的id

                        io.of('/namespace').allSockets() // '/namespace' 中所有连接的 ids(你也可以将其与房间结合使用)

                        【讨论】:

                        • 这适用于 Socket.IO v3.x 和 v4.x。如果您想从从allSockets() 接收的 ID 集中获取实际的 Socket 对象,您可以使用 io.of('/namespace').sockets 获取连接到命名空间的所有 Socket 实例的 Map,以便 io.of('/namespace').sockets.get(socketId) 获取与 ID 匹配的 Socket 实例。
                        【解决方案29】:

                        io.sockets.sockets.keys()

                        这对我有帮助。

                        【讨论】:

                          【解决方案30】:

                          2021 更新 V4.0+

                          没有一个答案对我有用。我会免除你的痛苦。从 1.0 开始,他们的 API 和文档发生了很大变化。

                          Server Api所有可用选项

                          但你需要深入挖掘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;
                          
                          });
                          

                          【讨论】:

                          • const sockets = Array.from(io.sockets.sockets).map(socket => socket[0]); console.log(sockets);
                          猜你喜欢
                          • 2017-11-29
                          • 1970-01-01
                          • 1970-01-01
                          • 2013-10-16
                          • 2014-10-01
                          • 2013-10-22
                          • 1970-01-01
                          • 2011-05-04
                          • 1970-01-01
                          相关资源
                          最近更新 更多