iT邦幫忙

DAY 24
4

且戰且走HTML5系列 第 24

且戰且走HTML5(24) 完成ws模組的包裝

其實還沒有真的完成,因為這個工作幾乎等於重寫Socket.IO...不過還是來看一下目前進行的狀況
主要的改寫還是在幾個部分,跟昨天的程式比起來,還需要在Manager與Socket之間插入Namespace物件來串接

首先,Manager改成:

var Socket = require('./Socket');
var wss = require('ws').Server;

var Manager = module.exports = function(server) {
	this.server = server;
	this.namespaces = {};
	this.store = {};
	this.rooms = {};
	this.handlers = {};
};

Manager.prototype.of = function(ns) {
	var namespace = new Namespace(this.server, this, ns);
	return namespace;
};

Manager.prototype.__defineGetter__('sockets', function() {
	return this.of('/');
});

function Namespace(server, manager, ns) {
	this.namespace = ns;
	this.socket = Socket;
	this.wss = new wss({server: server});
	this.manager = manager;
	this.server = server;
	this.ws = null;
	this.manager.handlers[ns] = {};
	this.manager.namespaces[this.namespace] = {};
}

Namespace.prototype.on = function(conn, handler) {
	var self = this;
	if(conn == 'connection') {
		this.wss.on('connection', function(ws) {
			self.ws = ws;
			ws.on('message', function(data, flags) {
				var args = JSON.parse(data);
				if(!!self.manager.handlers[self.namespace][args.name]) {
					var ev = args.name;
					delete args.name;
					self.manager.handlers[self.namespace][ev].forEach(function(fn) {
						fn.call(self, args);
					});
					ev = null;
				} else {
					console.log('no handler: ');
				}
			});
			var sid = Math.random()*10000 + '-' + new Date().getTime();
			var socket = new Socket(self.server, self.manager, self, sid, ws);
			if(!!self.manager.namespaces[self.namespace]['all']) {
				self.manager.namespaces[self.namespace]['all'][sid] = socket;
			} else {
				self.manager.namespaces[self.namespace]['all'] = {};
				self.manager.namespaces[self.namespace]['all'][sid] = socket;
			}
			handler.call(socket, socket);
		});
	}
}

需要多一個Namespace做中介,才有辦法為每個連線真正產生一個獨立的Socket物件。接下來是Socket物件的調整:

var WebsocketServer = require('ws').Server;

var handlers = {};

var Socket = module.exports = function(server, manager, namespace, sid, ws) {
	this.sid = sid;
	this.server = server;
	this.manager = manager;
	this.ws = ws;
	this.namespace = namespace.namespace;
	this.flags = {"allnotme":false,"inroom":false};
};

Socket.prototype.on = function(name, handler) {
	var self = this;
	switch(name) {
		default:
			if(!!this.manager.handlers[this.namespace][name]) {
				this.manager.handlers[this.namespace][name].push(handler);
			} else {
				this.manager.handlers[this.namespace][name] = [];
				this.manager.handlers[this.namespace][name].push(handler);
			}
	}
};

Socket.prototype.emit = function(name, data, opt) {
	var room = '';
	if(!this.flags.inroom) {
		room = 'all';
	} else {
		room = this.flags.inroom;
	}
	var targets = [];
	console.log(this.sid);
	for(var i in this.manager.namespaces[this.namespace][room]) {
		if(this.flags.allnotme) {
			if(i!=this.sid) {
				console.log(i);
				targets.push(this._findSocket(this.namespace, room, i));
			}
		} else {
			targets.push(this._findSocket(this.namespace, room, i));
		}
	}
	//console.log(targets);
	if(!!opt) {
		targets.forEach(function(socket) {
			console.log(socket);
			socket.ws.send({name:name, data:data});
		})
	} else {
		data['name'] = name;
		this.ws.send(JSON.stringify(data));
	}
	this.flags['allnotme'] = false;
	this.flags['inroom'] = false;
};

Socket.prototype.__defineGetter__('broadcast', function() {
	this.flags['allnotme'] = true;
	return this;
});

Socket.prototype._findSocket = function(ns, room, sid) {
	if(!!this.manager) {
		if(!!this.manager.namespaces[ns]) {
			if(!!this.manager.namespaces[ns][room]) {
				if(!!this.manager.namespaces[ns][room][sid]) {
					return this.manager.namespaces[ns][room][sid];
				}
			}
		}
	}
};

Socket.prototype.join = function(room) {
	if(!!this.manager.namespaces[this.namespace][room]) {
		this.manager.namespaces[this.namespace][room][this.sid] = this;
	} else {
		this.manager.namespaces[this.ns][room] = {};
		this.manager.namespaces[this.namespace][room][this.sid] = this;
	}
};

Socket.prototype.leave = function(room) {
	if(!!this.manager.namespaces[this.namespace][room]) {
		if(!!this.manager.namespaces[this.namespace][room][this.sid]) {
			delete this.manager.namespaces[this.namespace][room][this.sid];
		}
	}
	var i = 0;
	for(var a in this.manager.namespaces[this.namespace][room]) {
		i++
	}
	if(i===0) {
		delete this.manager.namespaces[this.namespace][room];
	}
}

Socket.prototype.in = function(room) {
	var check = false;
	if(!!this.manager.namespaces[this.namespace][room]) {
		if(!!this.manager.namespaces[this.namespace][room][this.sid]) {
			check = true;
		}
	}
	if(check) {
		this.flags['inroom'] = room;
	}
	return this;
}

Socket.prototype.set = function(name, value, cb) {
	if(!this.manager.store[this.sid]) {
		this.manager.store[this.sid] = {};
	}
	this.manager.store[this.sid][name] = value;
	cb();
};

Socket.prototype.get = function(name) {
	if(!!this.manager.store[this.sid]) {
		if(!!this.manager.store[this.sid][name]) {
			cb(false, this.manager.store[this.sid][name]);
		} else {
			cb(true);
		}
	} else {
		this.manager.store[this.sid] = {};
		cb(true);
	}
};

Socket.prototype.has = function(name, cb) {
	if(!!this.manager.cache[this.sid]) {
		if(!!this.manager.cache[this.sid][name]) {
			cb(false, true);
		} else {
			cb(true, true);
		}
	} else {
		this.manager.store[this.sid] = {};
		cb(true, false);
	}
};

主要的差別還是把事件的handler移到Manager來管理,程式碼則在Namespace物件中實作。也加入了data store以及群播...不過現在問題出在群播,因為他會傳給所有人XD。

只好明天再繼續...果然是且戰且走阿,希望不要把所有Socket.IO的物件架構都複製過來才能實現。Orz


上一篇
且戰且走HTML5(23) 加入類似Socket.IO的on與emit方法
下一篇
且戰且走HTML5(25) 解決ws.io模組的問題
系列文
且戰且走HTML530

1 則留言

0
ted99tw
iT邦高手 1 級 ‧ 2012-11-02 20:24:41

用力猛筆記呀...

讚讚讚

我要留言

立即登入留言