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 阅读(2804) 评论(0) 编辑 收藏 举报