socket.io之NameSpace类


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 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值