搞死socket.io第六天,io=Manager(source, opts)

原文:http://www.cnblogs.com/xiezhengcai/p/3968067.html 

当我们在使用

var socket = io("ws://103.31.201.154:5555");

的时候,socket.io都做了什么呢?建立socket连接,嗯 不错,但是我们还是得看看它是怎么实现的。

其实在socket.io里,io函数就是Manager函数,而Manager函数返回的就是Manager对象

function Manager(uri, opts){

    //返回Manager对象

  if (!(this instanceof Manager)) return new Manager(uri, opts);

  if (uri && ('object' == typeof uri)) {

    opts = uri;

    uri = undefined;

  }

  opts = opts || {};

  opts.path = opts.path || '/socket.io';

  this.nsps = {};
//所有订阅socket状态的容器
this.subs = []; this.opts = opts; //是否重连 this.reconnection(opts.reconnection !== false); //最大重连次数 this.reconnectionAttempts(opts.reconnectionAttempts || Infinity); //两次重连之间的延迟 this.reconnectionDelay(opts.reconnectionDelay || 1000); //两次重连之间的最大延迟 this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000); //connection超时时间 this.timeout(null == opts.timeout ? 20000 : opts.timeout); //连接状态 this.readyState = 'closed'; this.uri = uri; //重连数 this.connected = 0; //尝试次数 this.attempts = 0; //与engine.io数据交互时数据要编码 this.encoding = false;
// packetBuffer
this.packetBuffer = []; this.encoder = new parser.Encoder(); this.decoder = new parser.Decoder(); //是否自动连接 this.autoConnect = opts.autoConnect !== false; //如果自动连接则开始连接 if (this.autoConnect) this.open(); }

代码中的注释已经很清楚了,但是值得一提的manager是对engine.io的一层封装,从客户端代码来看,在engine.io的基础上实现 自动重连机制。另外值得注意的是encoding、packetBuffer俩变量,当向engine.io传递数据时,我们要对数据进行encode,所以encoding是表示是否在对数据进行encode中,因为encoding是调用engine.io下的.write函数,同时也看出来,engine.io是socket.io的数据传输层。当数据在encode中时,encoding=true,如果当前正在encode中,那么新的数据就会被缓存到packetBuffer里,当encode结束会自动在packetBuffer里遍历进行encode(代码2)。另外subs数组装的是清除所有订阅socket状态的容器,如(代码1):

代码1

//清除socket连接超时计时器   

 this.subs.push({

      destroy: function(){

        clearTimeout(timer);

      }

    });

代码2

Manager.prototype.packet = function(packet){

  debug('writing packet %j', packet);

  var self = this;

  if (!self.encoding) {

    // encode,encoding标识为true

    self.encoding = true;

    this.encoder.encode(packet, function(encodedPackets) {

      for (var i = 0; i < encodedPackets.length; i++) {

        self.engine.write(encodedPackets[i]);

      }

      //encode结束,encoding置为false

      self.encoding = false;

      //查询队列执行packet

      self.processPacketQueue();

    });

  } else { //如果在encode中,push到Buffer里

    self.packetBuffer.push(packet);

  }

};

在Manager的构造函数里,最后的结果是调用了open函数,这是打开socket连接的入口

Manager.prototype.open =

Manager.prototype.connect = function(fn){

  debug('readyState %s', this.readyState);

  //如果已经打开,直接返回

  if (~this.readyState.indexOf('open')) return this;



  debug('opening %s', this.uri);

  //打开socket连接

  this.engine = eio(this.uri, this.opts);

  var socket = this.engine;

  var self = this;

  this.readyState = 'opening';



  // emit `open`

  var openSub = on(socket, 'open', function() {

    self.onopen();

    fn && fn();

  });



  // emit `connect_error`

  var errorSub = on(socket, 'error', function(data){

    debug('connect_error');

    self.cleanup();

    self.readyState = 'closed';

    self.emitAll('connect_error', data);

    if (fn) {

      var err = new Error('Connection error');

      err.data = data;

      fn(err);

    }



    self.maybeReconnectOnOpen();

  });



  if (false !== this._timeout) {

    var timeout = this._timeout;

    debug('connect attempt will timeout after %d', timeout);



    // 设置连接超时

    var timer = setTimeout(function(){

      debug('connect attempt timed out after %d', timeout);

      openSub.destroy();

      socket.close();

      socket.emit('error', 'timeout');

      self.emitAll('connect_timeout', timeout);

    }, timeout);



    this.subs.push({

      destroy: function(){

        clearTimeout(timer);

      }

    });

  }



  this.subs.push(openSub);

  this.subs.push(errorSub);

    //返回Manager实例(已经打开socket连接的)

  return this;

};

相信熟悉之前的代码后,阅读这部分代码轻松也很容易,通过engine.io打开socket连接,同时将readyState置于opening状态,也监听socket连接的状态信息,和刚才讲的一样,将清除监听放在subs容其中,在后续的代码可以看到有cleanup来执行容器里面的所有函数。这就不细讲了。

你可能感兴趣的:(manager)