var Socket = require('./socket');
var Emitter = require('events').EventEmitter;
var parser = require('socket.io-parser');
var debug = require('debug')('socket.io:namespace');
module.exports = exports = Namespace;
exports.events = [
'connect', // for symmetry with client
'connection',
'newListener'
];
//一些个标志,在访问Server类上的标志时,根命名空间标志属性对象上的对应标志会被设为true
exports.flags = ['json','volatile','local']
var emit = Emitter.prototype.emit;
//命名空间构造函数,使用服务对象,名称初始化
function Namespace(server, name){
//名字
this.name = name;
//服务对象
this.server = server;
//socket集合,键为id,值为Socket对象
this.sockets = {};
this.connected = {};
//命名空间上中间件函数
this.fns = [];
this.ids = 0;
//房间名数组
this.rooms = [];
//标志
this.flags = {};
//初始化适配器
this.initAdapter();
}
//继承事件发射器
Namespace.prototype.__proto__ = Emitter.prototype;
//访问命名空间上指定标志时,标志属性对象上对应标志会被设为true
exports.flags.forEach(function(flag){
Object.defineProperty(Namespace.prototype, flag, {
get: function() {
this.flags[flag] = true;
return this;
}
});
});
//初始化适配器
Namespace.prototype.initAdapter = function(){
//this.server.adapter()返回了服务对象的_adapter属性,即适配器构造函数
this.adapter = new (this.server.adapter())(this);
};
//添加中间件函数
Namespace.prototype.use = function(fn){
//每添加一个中间件函数,就要先删除服务引擎上的初始化数据包
if (this.server.eio) {
debug('removing initial packet');
delete this.server.eio.initialPacket;
}
//加入数组
this.fns.push(fn);
return this;
};
//执行中间件,中间件第一个参数为Socket对象
Namespace.prototype.run = function(socket, fn){
//复制函数数组
var fns = this.fns.slice(0);
//不存在直接执行回调
if (!fns.length) return fn(null);
//从下标为0开始执行,直到下标超出
function run(i){
fns[i](socket, function(err){
//出现错误,传递给回调
if (err) return fn(err);
//下标超出则停止
if (!fns[i + 1]) return fn(null);
run(i + 1);
});
}
run(0)
}
//加入指定房间名
Namespace.prototype.to =
Namespace.prototype.in = function(name){
if (!~this.rooms.indexOf(name)) this.rooms.push(name);
return this;
};
//添加一个客户端到命名空间的Socket
Namespace.prototype.add = function(client, query, fn){
debug('adding socket to nsp %s', this.name);
//创建顶层Socket对象,一个Socket代表了一个客户端到一个命名空间的连接
var socket = new Socket(this, client, query);
var self = this;
//对socket对象执行所有中间件函数
this.run(socket, function(err){
process.nextTick(function(){
//如果底层socket为open状态
if ('open' == client.conn.readyState) {
if (err) return socket.error(err.data || err.message);
//没有错误,将Socket加入命名空间中
self.sockets[socket.id] = socket;
//调用socket连接函数
socket.onconnect();
//执行回调
if (fn) fn();
//发射连接事件
self.emit('connect', socket);
self.emit('connection', socket);
} else {
debug('next called after client was closed - ignoring socket');
}
});
});
return socket;
};
//从命名空间中移除执行Socket
Namespace.prototype.remove = function(socket){
if (this.sockets.hasOwnProperty(socket.id)) {
delete this.sockets[socket.id];
} else {
debug('ignoring remove for %s', socket.id);
}
};
//发射事件到所有客户端
Namespace.prototype.emit = function(ev){
//如果包含于指定事件
if (~exports.events.indexOf(ev)) {
//直接发射
emit.apply(this, arguments);
return this;
}
//复制参数数组
var args = Array.prototype.slice.call(arguments);
//构造事件类型数据包
var packet = { type: parser.EVENT, data: args };
if ('function' == typeof args[args.length - 1]) {
throw new Error('Callbacks are not supported when broadcasting');
}
//复制房间名数组
var rooms = this.rooms.slice(0);
//标志
var flags = Object.assign({}, this.flags);
//重置
this.rooms = [];
this.flags = {};
//使用适配器对指定房间名内连接广播数据包,指定标志
this.adapter.broadcast(packet, {
rooms: rooms,
flags: flags
});
return this;
};
Namespace.prototype.send =
Namespace.prototype.write = function(){
var args = Array.prototype.slice.call(arguments);
//添加message到参数数组
args.unshift('message');
//发射message事件,即广播事件类型数据包
this.emit.apply(this, args);
return this;
};
//获取客户端列表?
Namespace.prototype.clients = function(fn){
this.adapter.clients(this.rooms, fn);
// reset rooms for scenario:
// .in('room').clients() (GH-1978)
this.rooms = [];
return this;
};
//设置压缩标志,true代表数据包过大需要压缩
Namespace.prototype.compress = function(compress){
this.flags.compress = compress;
return this;
};
可以看到,命名空间对象有以下属性:
1.name:空间名称,根命名空间为/
2.sockets:Socket对象的缓存,即一个命名空间可以被多个来自于不同客户端的Socket连接
3.connected:已连接Socket
4.fns:连接到这个命名空间的Socket都要经过的中间件数组
5.rooms:房间名数组,命名空间下存在的房间
6.adapter:适配器,用于为socket分配房间
命名空间承担的功能:
1.to:添加一个房间名
2.add:连接一个客户端到这个命名空间,生成Socket,执行中间件,存储Socket,调用Socket连接事件,发射本身连接事件
3.remove:移除一个socket,从当前命名空间
4.emit:发射指定事件,如果在事件名数组中,就激发回调,如果不在,就向当前命名空间所有房间内Socket广播一个事件类型数据包
5.send/write:发射一个message事件
内部连接模型:
Namespace:一个socket.io服务(即默认以socket.io标识的上下文路径)下面的不同端点,默认只有根端点/
Client:一条客户端与服务器的通信通道,可能采用不同方式传输数据包(polling或websocket),客户端连接到服务器的任何一个命名空间都经由一个Client对象传输数据包
Socket:一个客户端对一个命名空间端点的连接
注意:底层engine.io包中也有Socket类,那个socket类与Client是一对一的概念
---------------------
作者:crayon-shin-chan
来源:CSDN
原文:https://blog.csdn.net/qq_27868061/article/details/78957173
版权声明:本文为博主原创文章,转载请附上博文链接!