socket.io只广播给在A和B房间的用户

是否有可能将socket.io广播给位于A房间和B房间的命名空间的所有用户,而不是刚好在A房间或B房间的那些用户。

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

我正在与节点中的socket.io 1.0

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

你可以使用(参考如何更新房间内所有客户端的套接字对象(socket.io) )来查找所有的房间用户。

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

所以给了你两个房间名单的两个数组,你可以使用类似于这里的答案来计算交集(参考: 在javascript中最简单的数组交集 )

最后,你可以把这两个房间的用户列表,并发出事件使用(参考: 如何更新套房对象的房间里的所有客户?(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"); 

另一种方式是有隐藏的房间,在那里你保持所有房间的组合,并且把用户添加到幕后的房间,然后你就可以简单地发射到那些隐藏的房间。 但是,这是一个指数增长的问题。

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

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

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

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

现在我们创build了更多的临时对象,并在没有select房间的情况下循环遍历房间中的所有客户端或所有客户端。 循环发生在编码callback。 该方法可以在这里find:

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

但是,如果我们不是通过broadcast发送数据包,而是在循环遍历房间并find房间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适配器类并修改广播方法,将自己的方法添加到原型中,或通过inheritance适配器类来展开自己的适配器)。 ( var io = require('socket.io')(server, { adapter: yourCustomAdapter });

或者覆盖joinsocket.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"); } }; })(); 

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

作为定制适配器来使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') }); 
 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' }); } }); });