無名

大猫咪与小狮子

导航

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来执行容器里面的所有函数。这就不细讲了。

posted on 2014-09-12 11:34  xiezhengcai  阅读(2761)  评论(0编辑  收藏  举报