Socket.IO - как получить список подключенных сокетов / клиентов?


159

Я пытаюсь получить список всех сокетов / клиентов, которые в данный момент подключены.

io.sockets не возвращает массив, к сожалению.

Я знаю, что могу сохранить свой собственный список, используя массив, но не думаю, что это оптимальное решение по двум причинам:

  1. Избыточность. Socket.IO уже хранит копию этого списка.

  2. Socket.IO предоставляет метод для установки произвольных значений полей для клиентов (то есть:), socket.set('nickname', 'superman')поэтому я должен был бы не отставать от этих изменений, если бы я должен был поддерживать свой собственный список.

Помогите?


Разве вы не можете просто конвертировать io.sockets в массив?
Мак

2
В 0.7.6 io.sockets.sockets представляет собой простой хеш-объект id => socket
mak

1
Я заметил это, но он действует странно (не похоже на стандартные объекты сокетов). Например, я попытался: pastebin.com/fxuzVckS, но там говорится, что объект сокета не содержит метод get.
Энди Хин

3
socketэто id, io.sockets.sockets[socket]это сокет.
Мак

2
Первый аргумент - errи если вы используете хранилище памяти, это всегда null. .get('nickname', function(err, nickname) { ... })
Мак

Ответы:


173

В 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 Мар 06

От 1.x и выше, пожалуйста, перейдите по этой ссылке: узнать, сколько человек в чате в socket.io


3
Я думаю, что они отказались от метода клиентов в 1.0 ... просто проверяя это в данный момент ...
marksyzm

7
Они выпали почти все в 1.0, так как это полностью переписано. Но так как 1.0 не выпускался годами. В 1.0 вы должны использовать connectedобъект пространства имен.
3rdEden

6
@ 3rdEden, каков синтаксис для использования подключенного объекта пространства имен? var clients = io.sockets.connected();очевидно не работает. Спасибо
Кая Тост


Ошибка типа: fn.bind не является функцией
Иман Мараши

101

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

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

Также смотрите этот связанный вопрос , в котором я даю функцию, которая возвращает сокеты для данной комнаты.

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`

Примечание. Поскольку кажется, что API socket.io склонен к взлому, а некоторые решения основаны на деталях реализации, это может быть вопросом отслеживания клиентов самостоятельно:

var clients = [];

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

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

3
Спасибо тебе за это. Я удивлен, что они убрали эту функциональность.
Тайлер Скотт

1
@TylerScott Я тоже, особенно с учетом того, что это не описано в их руководстве по миграции (если я что-то упустил). socket.io/docs/migrating-from-0-9
nha

@nha, это не работает для меня, но возвращает массив целых чисел, таких как 0, 1 и 2 и т. д. вместо массива сокетов. Любые идеи?
ThePixelPony

как насчет нескольких узлов? потому что я использую несколько узлов с магазином Redis
Джон Нгуен

3
@nha вы точно ответили, но он не работает для нескольких узлов с сервером Redis Store. с 1 узла процесс, вы не можете проверить сокет подключен или нет с помощью ns.connected [id]
Джон Нгуен

46

После 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
}

3
я var clients_in_the_room = io.sockets.adapter.rooms[roomId].sockets
перехожу

37

Используя 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

32

Очень просто в socket.io 1.3:

io.sockets.sockets- это массив, содержащий подключенные объекты сокетов. Если вы сохранили имя пользователя в каждом сокете, вы можете сделать:

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

Boom. У вас есть имена всех подключенных пользователей.


2
Спасибо! +1 для простоты. Знаете ли вы, есть ли разница между этим методом и, скажем, ответом Джозефа Дикстры? (Object.keys(io.engine.clients)
cyclingLinguist

4
Это не работает на v1.4.5. Все еще работаем над решением проблемы
Нуно Фуртадо,

ты нашел это? \
ocram

2
Ошибка типа: io.sockets.sockets.map не является функцией
Király István

23

Я прошел через эту боль сегодня. 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


1
@ Зелёный Это сработает, если вы не используете пространство имен. Если вы используете пространство имен, измените на io.server.eio.clientsCount
Thanh Nguyen

2
Да, официальная документация может быть настолько улучшена
CodeAndCats

18

Версия +2.0

В версии +2.0 вы указываете пространство имен / комнату / узел, к которому вы обращаетесь.

Как и в случае широковещания, по умолчанию используются все клиенты из пространства имен по умолчанию ('/'):

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

Получает список идентификаторов клиентов, связанных с конкретным пространством имен (по всем узлам, если применимо).

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


1
Есть ли способ просмотреть все объекты сокета ? например, если я добавляю socket.usernameсвойство к каждому сокету при подключении, есть ли способ просмотреть все эти объекты сокетов (включая настраиваемые свойства, которые я добавляю в каждый сокет)? (для справки, в настоящее время я использую глобальную переменную объекта для хранения дополнительной информации о сокете при подключении клиента - поэтому не спрашиваю, как это настроить - просто интересно узнать, есть ли способ просмотреть все объекты сокета по умолчанию) ,
user1063287

1
хмм, за этот ответ , я думаю , что вы можете видеть все подключенные розетки с console.log(io.sockets.connected)- это , кажется, объект , где каждое значение свойства является «сокет объект» , который содержит эти свойства nsp, server, adaptor, id(строка), client, conn, rooms, acks, connected, disconnected, handshake, fns, flags, _rooms, _events, _eventsCountи , наконец , usernameчто является пользовательским свойством я добавить к каждому разъему подключения.
user1063287

17

Я думаю, что мы можем получить доступ к объекту сокета с сервера, и вы можете назначить псевдоним и указать его идентификатор сокета,

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"? Можете ли вы использовать свой код следующим образом (принимая данные только из одного сокета) ?: io.sockets.sockets [sock_id] .on ('newmessage', function (data) {console.log (data)}));
Ястреб

9

Это лучший способ получить к нему доступ в 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) });
Хани Алсамман

5

Для тех, кто просто хочет COUNT подключенных клиентов, я верю, что это сделает это:

io.sockets.manager.server.connections


Количество подключенных клиентов не проверено. (немного больше)
mrzmyr

5
для точного подсчета используйтеio.sockets.clients().length
Ivo

2
В настоящее время оба io.sockets.manager.server.connectionsи io.sockets.clients().lengthне работают. Это сработало для меня - только для подсчета - io.engine.clientsCountи для списка клиентов - io.engine.clientsCount. Так как он остается таким более 3 лет. Похоже, надежный способ получить информацию о соединении. (Основано на ответе
Джозефа Дикстры

5

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

Думаю, кому-то поможет этот блок кода.


4

В 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

Я подтверждаю, что это работает в версии 1.5.1 socket.io
Себастьян

3

Начиная с 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;
}

Вы не можете себе представить, сколько боли вы спасли меня
nicholaswmin

но версия socket.io еще не 1.5! Сейчас 1.4.8
leszek.hanusz

@MoleIsKing Obrigado amigo!

2

на socket.io 1.3 я выполнил это в 2 строки

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

2

Socket.io 1.7.3 (+):

функция getConnectedList ()
{
    let list = []
    
    для (пусть клиент в io.sockets.connected)
    {
        list.push (клиент)
    }
    
    список возврата
}

console.log (getConnectedList ())

// возвращает ['yIfhb2tw7mxgrnF6AAAA', 'qABFaNDSYknCysbgAAAB']


2

Для версии 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 как-то


1
я начинаю думать, что это библиотека мусора. единственное, что он делает правильно, - это беспрепятственная передача многих типов данных (json, двоичных и т. д.), в противном случае вся архитектура является дерьмом и не масштабируется за пределы приложения чата «1 на 1»
Мухаммед Умер,

1

Я полагаю, вы можете получить доступ к этому из свойства менеджера сокета?

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). Длина оказалась самым надежным подсчетом в моем опыте. io.of ('/ namespace'). manager.open, если вы используете пространства имен.
Уэс Джонсон

1

Здесь я вижу много хороших ответов, и многие из них весьма полезны, но не совсем то, что мне нужно. Я использую сокеты для функции pubsub, в которой заинтересованный клиент может прослушивать любые изменения в данной записи.

Моя конкретная проблема заключалась в том, что одна и та же розетка несколько раз входила в одну и ту же комнату. Решение этой проблемы состояло в том, чтобы проверить, есть ли у сокета комната в собственности комнат.

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

Надеюсь, это кому-нибудь поможет.


Я думаю, что можно избежать этого «если», если вы заставляете клиента входить в комнату при подключении через сокет.
Чаймор

Откуда берется myObj? Как вы пришли к этому значению?
Майкл Дрэйпер

@MikeDraper myObject - это запись в базе данных, которая действует как канал. Это решает тот случай, когда вы хотите, чтобы все ваши клиенты обновлялись всякий раз, когда происходит изменение в базе данных, например, пользователь обновляет свое имя, а все подключенные клиенты получают обновление. Поэтому я создаю комнату для этого экземпляра, учитывая, что он уникален (BSON). Это очень конкретное значение для моего варианта использования, поэтому извините, если я вызвал какую-то путаницу. Дайте мне знать, если вам нужна помощь :)
Гас Ортис

@JMR из-за характера моего приложения случилось так, что сокет был подписан более одного раза, поэтому мне пришлось добавить эту проверку.
Гас Ортис

1

Это самый простой способ в Socket.IO 1.0+, если вы не используете пространства имен или комнаты.

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

Это смотрит на пространство имен по умолчанию и определяет длину массива сокетов, без необходимости использования Object.keys()


1

Начиная с версии 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...
    }
});

По какой-то причине они используют простой объект вместо массива для хранения идентификаторов сокетов.


Этот работает. Пустая строка в io.of ('') работает без пространств имен.
Király István

@ KirályIstván Я уже давно не разбираюсь в этом, но думаю, что of()в этом случае вы можете полностью удалить цепочку вызовов.
rppc

1

Для режима кластера, используя Redis-адаптер

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

});

Поскольку каждый сокет является комнатой, можно найти, существует ли сокет, используя тот же.


0

Вот быстрый способ преобразования хэша подключенных сокетов из пространства имен в массив с использованием генераторов 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
})

0

Я не знаю, если это все еще происходит. Но что-то вроде этого я и использовал (я сохраняю объект сеанса на каждом подключенном сокете, который, в свою очередь, содержит имя пользователя и другую информацию:

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

0

Если в проекте есть кластер socket.io, это означает, что используется адаптер socket.io-redis .

В случае, подобном описанному выше, процесс получения идентификатора всех подключенных сокетов должен выполняться через адаптер 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 для более подробной информации.


0

socket.io@1.7.3

Я использовал Object.Keys, чтобы подключить массив сокетов. Затем в том же массиве итерации с функцией карты, чтобы построить новый массив объектов

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

Возможно, этот ответ поможет получить массив идентификатора сокета / имени пользователя.


1
Привет. Можете ли вы объяснить по-английски, как это решает проблему? Таким образом, это будет не просто ответ только для кода. Спасибо!
Макс фон Хиппель

0

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.

1
Было бы хорошо, если бы вы могли добавить некоторый контекст, почему «Это лучший способ» и как ваш ответ добавляет что-либо ко всем ответам выше. Код это что-то, но ответ также должен иметь некоторое объяснение. Как вы можете видеть, есть много других ответов на этот вопрос. Ваш ответ должен принести что-то еще к предмету, в противном случае он является излишним.
Натан Риперт

С socket.io 2.1.1 , я получаю , TypeError: clients.map is not a functionи , хотя это выглядит многообещающим, я не совсем уверен , что в приведенном выше примере будет достичь - я надеялся , что это будет автоматически создать массив объектов клиента с логинами включены :).
user1063287
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.