Socket.io передается только пользователям, которые находятся в комнатах A и B

Можно ли сделать передачу socket.io всем пользователям пространства имен, которые находятся в комнате A и комнате B, но не те, кто находится только в комнате A или комнате B?

Если нет, как бы я сам это сделал? Есть ли способ получить всех пользователей в пространстве имен, которые находятся в данной комнате?

Я работаю с socket.io 1.0 в node

Изменить: Если нет встроенного метода, как бы я начал создавать свой собственный синтаксис, например: socket.broadcast.in('room1').in('room2').emit(...)?

Ответ 1

Вы можете найти всех пользователей комнаты, используя (ref Как обновить объект сокета для всех клиентов в комнате (socket.io))

var clients = io.sockets.adapter.room["Room Name"]

Итак, учитывая два массива для списка ваших двух комнат, вы можете вычислить пересечение, используя что-то вроде ответа здесь (ref: Простейший код для пересечения массива в javascript)

И, наконец, вы можете взять этот список пользователей в обеих комнатах и ​​испускать события, используя (ref: Как обновить объект сокета для всех клиентов в комнате? (socket.io))

//this is the socket of each client in the room.
var clientSocket = io.sockets.connected[clientId];

//you can do whatever you need with this
clientSocket.emit('new event', "Updates");

Альтернативный курс - иметь скрытые комнаты, где вы поддерживаете сочетание всех комнат и добавляете пользователей в эти комнаты за кулисами, а затем вы можете просто просто выбросить их в скрытые комнаты. Но это страдает от экспоненциальной проблемы роста.

Ответ 2

Нет встроенного способа сделать это. Поэтому сначала посмотрим, как работает трансляция:

https://github.com/Automattic/socket.io/blob/master/lib/namespace.js 206... 221-224... 230

this.adapter.broadcast(packet, {
    rooms: this.rooms,
    flags: this.flags
});

Теперь мы знаем, что каждая трансляция создает кучу объектов temp, lookups indexOf, срезов аргументов... И затем вызывает метод широковещания адаптера. Давайте посмотрим на это:

https://github.com/Automattic/socket.io-adapter/blob/master/index.js 111-151

Теперь мы создаем еще более временные объекты и просматриваем все клиенты в комнатах или во всех клиентах, если не было выбрано ни одного места. Цикл происходит в обратном вызове кодирования. Этот метод можно найти здесь:

https://github.com/socketio/socket.io-parser/blob/master/index.js

Но что, если мы не отправляем наши пакеты через broadcast, а каждому клиенту отдельно после прохода по комнатам и поиска клиентов, которые существуют как в комнате А, так и в комнате B?

socket.emit определяется здесь: https://github.com/Automattic/socket.io/blob/master/lib/socket.js

Что приводит нас к методу packet client.js: https://github.com/Automattic/socket.io/blob/master/lib/client.js

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

Чтобы ответить на ваш вопрос:

Либо измените класс адаптера socket.io, либо измените метод широковещания, добавьте собственные методы в прототип или сверните собственный адаптер, наследуя его от класса адаптера). (var io = require('socket.io')(server, { adapter: yourCustomAdapter });)

Или перезапишите методы join и leave socket.js. Это довольно удобно, учитывая, что эти методы не вызывают очень часто, и у вас нет проблем с редактированием через несколько файлов.

Socket.prototype.join = (function() {
    // the original join method
    var oldJoin = Socket.prototype.join;

    return function(room, fn) {

        // join the room as usual
        oldJoin.call(this, room, fn);

        // if we join A and are alreadymember of B, we can join C
        if(room === "A" && ~this.rooms.indexOf("B")) {
            this.join("C");
        } else if(room === "B" && ~this.rooms.indexOf("A")) {
             this.join("C");
        }  
    };
})();

Socket.prototype.leave = (function() {
    // the original leave method
    var oldLeave = Socket.prototype.leave;

    return function(room, fn) {

        // leave the room as usual
        oldLeave.call(this, room, fn);

        if(room === "A" || room === "B") {
             this.leave("C");
        }  
    };
})();

И затем передайте на C, если вы хотите транслировать все пользователи в A и B. Это всего лишь пример кода, вы могли бы еще больше улучшить это, не жестко кодируя имена комнат, но используя массив или объект вместо этого, чтобы перебрать возможные комбинации комнат.

Как пользовательский адаптер, чтобы сделать работу socket.broadcast.in("A").in("B").emit():

var Adapter = require('socket.io-adapter');

module.exports = CustomAdapter;

function CustomAdapter(nsp) {
  Adapter.call(this, nsp);
};

CustomAdapter.prototype = Object.create(Adapter.prototype);
CustomAdapter.prototype.constructor = CustomAdapter;

CustomAdapter.prototype.broadcast = function(packet, opts){
  var rooms = opts.rooms || [];
  var except = opts.except || [];
  var flags = opts.flags || {};
  var packetOpts = {
    preEncoded: true,
    volatile: flags.volatile,
    compress: flags.compress
  };
  var ids = {};
  var self = this;
  var socket;

  packet.nsp = this.nsp.name;
  this.encoder.encode(packet, function(encodedPackets) {
    if (rooms.length) {
      for (var i = 0; i < rooms.length; i++) {
        var room = self.rooms[rooms[i]];
        if (!room) continue;
        for (var id in room) {
          if (room.hasOwnProperty(id)) {
            if (~except.indexOf(id)) continue;
            socket = self.nsp.connected[id];
            if (socket) {
              ids[id] = ids[id] || 0;
              if(++ids[id] === rooms.length){
                socket.packet(encodedPackets, packetOpts);
              }
            }
          }
        }
      }
    } else {
      for (var id in self.sids) {
        if (self.sids.hasOwnProperty(id)) {
          if (~except.indexOf(id)) continue;
          socket = self.nsp.connected[id];
          if (socket) socket.packet(encodedPackets, packetOpts);
        }
      }
    }
  });
};

И в вашем файле приложения:

var io = require('socket.io')(server, {
  adapter: require('./CustomAdapter')
});

Ответ 3

io.sockets.adapter.room["Room A"].forEach(function(user_a){
  io.sockets.adapter.room["Room B"].forEach(function(user_b){
    if(user_a.id == user_b.id){
      user_a.emit('your event', { your: 'data' });
    }
  });
});