zoukankan      html  css  js  c++  java
  • 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来执行容器里面的所有函数。这就不细讲了。

  • 相关阅读:
    Java硬件同步机制Swap指令模拟+记录型信号量模拟
    算法(第四版)练习 1.1.26 ~ 1.1.31
    C++ 电路布线/最短路径问题
    线性代数笔记
    算法导论(第三版)练习 2.2-1 ~ 2.2-4
    条款45: 弄清C++在幕后为你所写、所调用的函数
    条款42: 明智地使用私有继承
    条款41: 区分继承和模板
    【python】字符遍历
    【python】range的用法
  • 原文地址:https://www.cnblogs.com/xiezhengcai/p/3968067.html
Copyright © 2011-2022 走看看