没有内置的方法可以做到这一点。所以首先让我们看看广播是如何工作的:
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.js 的packet 方法:
https://github.com/Automattic/socket.io/blob/master/lib/client.js
每个直接发出的数据包都将被单独编码,这又是昂贵的。因为我们向所有用户发送完全相同的数据包。
回答你的问题:
要么更改 socket.io 适配器类并修改广播方法,将你自己的方法添加到原型中,要么通过从适配器类继承来滚动你自己的适配器)。 (var io = require('socket.io')(server, { adapter: yourCustomAdapter });)
或者覆盖socket.js的join和leave方法。考虑到这些方法并不经常被调用,而且您没有编辑多个文件的麻烦,这相当方便。
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");
}
};
})();
如果要向A和B中的所有用户广播,然后广播到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')
});