socket.io 只向房间 A 和 B 中的用户广播

Posted

技术标签:

【中文标题】socket.io 只向房间 A 和 B 中的用户广播【英文标题】:socket.io broadcast only to users who are in room A and B 【发布时间】:2015-09-27 20:08:30 【问题描述】:

是否可以将 socket.io 广播给所有在房间 A 和房间 B 中的命名空间用户,而不是只在房间 A 或房间 B 中的用户?

如果没有,我将如何自己实施?有没有办法检索给定房间中的命名空间中的所有用户?

我正在节点中使用 socket.io 1.0

编辑: 如果没有本机方法,我将如何创建自己的语法,例如: socket.broadcast.in('room1').in('room2').emit(...)?

【问题讨论】:

看这个:***.com/a/24145381/3842050 这可能会有所帮助。 您可以在客户端通过发送到两者并仅对同一客户端的第二次到达做出反应... 【参考方案1】:

您可以使用 (ref How to update socket object for all clients in room? (socket.io)) 查找房间的所有用户

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

因此,为您的 2 个房间的花名册列表提供两个数组,您可以使用此处的答案(参考:Simplest code for array intersection in javascript)计算交集

最后,您可以获取两个房间中的用户列表并使用(参考:How to update socket object for all clients in room? (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");

另一种方法是拥有隐藏房间,您可以在其中维护所有房间的组合,并在幕后将用户添加到这些房间,然后您可以简单地向这些隐藏房间发射。但这会遇到指数增长问题。

【讨论】:

谢谢!这是非常有价值的信息。你知道我如何在 socket.io 原型中实现这种行为吗?这是我将在我的代码中使用的东西,所以如果我可以调用socket.broadcast.toAll([...]).emit(...) 并让toAll() 函数获取所有房间的联合并仅向那些房间发出,那真的很简单。跨度> 【参考方案2】:

没有内置的方法可以做到这一点。所以首先让我们看看广播是如何工作的:

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

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

现在我们知道每个广播都会创建一堆临时对象、indexOf 查找、参数切片……然后调用适配器的广播方法。让我们看看那个:

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

现在我们正在创建更多的临时对象,并循环遍历房间中的所有客户端,如果没有选择房间,则遍历所有客户端。循环发生在编码回调中。该方法可以在这里找到:

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

但是,如果我们不是通过broadcast 发送数据包,而是在遍历房间并找到同时存在于房间 A 和房间 B 中的客户端之后分别发送给每个客户端怎么办?

socket.emit 在这里定义:https://github.com/Automattic/socket.io/blob/master/lib/socket.js

这将我们带到client.jspacket 方法: https://github.com/Automattic/socket.io/blob/master/lib/client.js

每个直接发出的数据包都将被单独编码,这又是昂贵的。因为我们向所有用户发送完全相同的数据包。

回答你的问题:

要么更改 socket.io 适配器类并修改广播方法,将你自己的方法添加到原型中,要么通过从适配器类继承来滚动你自己的适配器)。 (var io = require('socket.io')(server, adapter: yourCustomAdapter );)

或者覆盖socket.jsjoinleave方法。考虑到这些方法并不经常被调用,而且您没有编辑多个文件的麻烦,这相当方便。

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

如果要向AB中的所有用户广播,然后广播到C。 这只是一个示例代码,您可以通过不对房间名称进行硬编码而是使用数组或对象来循环可能的房间组合来进一步改进它。

作为自定义适配器制作socket.broadcast.in("A").in("B").emit()work:

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

【讨论】:

这看起来是迄今为止最有前途的方法。我还没来得及测试它,但我会在它到期之前给你赏金,奖励你的工作和努力。 谢谢! :) 您还可以尝试其他方法。例如。遍历A 中的所有用户,查找他们是否也在B 中(通过在他们的room 属性上使用indexOf),然后首先对您的消息进行编码并通过socket.packet 而不是socket.emit 发送它。我也没有尝试过代码,所以如果有什么没有按预期工作,请告诉我。 会的!这周末我会有一些空闲时间。我的主要问题是将我的函数添加到 socket.io 的原型中【参考方案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' );
    
  );
);

【讨论】:

以上是关于socket.io 只向房间 A 和 B 中的用户广播的主要内容,如果未能解决你的问题,请参考以下文章

Socket.io 中的动态房间

如何使用nodejs socket.io向特定房间中的用户发送打字警报和消息

关于 socket.io 中的房间创建

带有 Socket.io 和 Node 的动态房间

socket io 广播、房间和确认功能

socket io 广播、房间和确认功能