iT邦幫忙

DAY 26
3

且戰且走HTML5系列 第 26

且戰且走HTML5(26) 使用ws.io完成資源共享

依照昨天的構想,把傳送binary資料的功能也實作出來了,就把它用在資源分享上。
分成兩個部分來傳送資料,基本上問題不大,但是要實作,需要在使用方法上制定一些規則,才能讓傳送一般資訊與binary資料使用同樣的方法。

這些規則及限制主要是因為內部需要傳送額外的資訊,才可能將binary資料與其他資訊一同傳送。為此,先定義一個內部使用的事件名稱,叫做blob,另外,在傳送的資料中,也必須記錄原本的事件,所以也不能在傳送的資料物件中,使用'name'做為屬性的名稱。還有一個限制,不過這是偷懶的緣故XD,就是程式中會做判斷哪個屬性存放了Blob或是ArrayBuffer物件,但是我只檢查一層的屬性,如果屬性中還有其他物件,物件中有Blob,就不管他了。為了讓從放Blob/ArrayBuffer的屬性名稱一致,還佔用了一個欄位,叫做blobfield。所以目前的限制是:

  1. 不可使用blob做為自定事件名稱
  2. 傳遞Blob/ArrayBuffer時,資料中不可有'name'以及'blobfield'這兩個屬性
  3. Blob/ArrayBuffer必須放在物件第一層屬性中,否則會找不到
  4. 每次只能傳送一個binary資料

只要符合這個規則,就可以像平常使用Socket.IO的方式來傳送binary資料。其實做更多判斷、調整設計的話,還是可以拿掉這些規則限制,不過目前先這樣吧。

接下來看目前的程式碼,既然功能差不多了,我就把比較完整的程式放上來。

首先是測試的程式,伺服器端(test844.js):

 var fs = require('fs'),
 url = require('url'),
 app = require('http').createServer(function(req, res) {
 	var filename = '',
 	resource = url.parse(req.url).pathname;
 	switch(resource) {
 		case '/ws.io/ws.io.js':
 			console.log(resource);
 			filename = __dirname + resource;
 			res.setHeader('Content-Type', 'text/javascript');
 			break;
 		default:
 			filename = __dirname + '/test844.html';
 			res.setHeader('Content-Type', 'text/html');
 			break;
 	}
 	fs.readFile(filename, function(err, data) {
 		if(err) {
 			res.writeHead(500);
 			return res.end('Error reading resource.');
 		} else {
 			res.writeHead(200);
 			res.end(data);
 		}
 	});
 }),
 io = require('./ws.io').listen(app);
 
 io.sockets.on('connection', function(socket) {
 	socket.on('share', function(data) {
 		socket.broadcast.emit('share', data);
 	});
 });
 
 app.listen(8443);

網頁(test844.html):

 <meta charset='utf-8'>
 
 <style>
 #panel {
 	border: solid 1px #336699;
 	width: 240px;
 	line-height: 20px;
 	vertical-align: middle;
 	padding: 5px;
 	border-radius: 5px;
 }
 </style>
 <script src='/ws.io/ws.io.js'></script>
 
 
 <input type='file' id='files'><br>
 <div id='panel'>
 	<ul id='list'>
 	</ul>
 </div>
 
 
 <script>
 var files = document.getElementById('files');
 var socket = io.connect('ws://localhost:8443');
 function getUrl() {
 	if(!!window.URL) {
 		return window.URL;
 	}
 	if(!!window.webkitURL) {
 		return window.webkitURL;
 	}
 }
 
 files.addEventListener('change', function(e) {
 	var URL = getUrl();
 	if(files.files.length>0) {
 		var file = files.files[0];
 		var src = URL.createObjectURL(file);
 		var a = document.createElement('a');
 		a.href = src;
 		a.innerHTML = file.name;
 		a.target = '_blank';
 		var li = document.createElement('li');
 		li.appendChild(a);
 		document.getElementById('list').appendChild(li);
 		socket.emit('share', {filename: file.name, type: file.type, file:file});
 	}
 });
 var fileinfo;
 socket.on('share', function(data) {
 	var URL = getUrl();
 	var a = document.createElement('a');
 	var file = new Blob([data.file], {type:data.type});
 	a.href = URL.createObjectURL(file);
 	a.innerHTML = data.filename;
 	a.target = '_blank';
 	var li = document.createElement('li');
 	li.appendChild(a);
 	document.getElementById('list').appendChild(li);
 });
 </script>

使用'node test844'執行後,會監聽8443埠。另外,ws.io模組放在ws.io子目錄中,裡面有幾個檔案。首先是模組的定義(package.json):

 {
     "name": "ws.io",
     "version": "0.0.1",
     "author": "fillano <fillano.feng@gmail.com>",
     "contributors": [
         {
             "name": "Fillano Feng",
             "email": "fillano.feng@gmail.com"
         }
     ],
     "main": "./lib/ws.io",
     "description": "a simple wrap for ws to make it acts more like Socket.IO",
     "keywords": [
         "WebSocket",
         "ws",
         "Socket.IO"
     ],
     "dependencies": [
         {"ws": "0.4.21"}
     ],
     "license": "MIT",
     "engines": [
         {"node": ">=0.8"}
     ]
 }

以及模組的進入點(index.js):

 module.exports = require('./lib/ws.io');

需要在網頁中載入的靜態js檔也在這個目錄中(ws.io.js),這裡定義了client的基本操作方法,包含io.connect()、socket.on()、socket.emit():

 (function(window, undefined) {
 
 var io = window.io = {
 	connect: function(addr) {
 		if(addr.indexOf('http')==0) addr = addr.replace('http','ws');
 		return new Socket(new WebSocket(addr));
 	}
 };
 
 function Socket(ws) {
 	var handlers = {};
 	var blobinfo = null;
 	this.on = function(name, handler) {
 		if(!!handlers[name]) {
 			handlers[name].push(handler);
 		} else {
 			handlers[name] = [];
 			handlers[name].push(handler);
 		}
 	};
 	this.emit = function(name, data) {
 		var isblob = false;
 		for(var i in data) {
 			if(checkBinary(data[i])) {
 				isblob=true;
 				var blob = data[i];
 				delete data[i];
 				data['name'] = name;
 				data['blobfield'] = i;
 				break;
 			}
 		}
 		if(isblob) {
 			var tosent = {name:'blob',data:data};
 			ws.send(JSON.stringify(tosent));
 			ws.send(blob);
 		} else {
 			ws.send(JSON.stringify({name:name,data:data}));
 		}
 	};
 	ws.onmessage = function(msg) {
 		switch(typeof msg.data) {
 			case 'object':
 				if(!!blobinfo) {
 					if(!!handlers[blobinfo.name]) {
 						var name = blobinfo.name;
 						delete blobinfo.name;
 						blobinfo[blobinfo.blobfield] = msg.data;
 						delete blobinfo.blobfield;
 						handlers[name].forEach(function(fn) {
 							fn.call(this, blobinfo);
 						});
 					}
 					blobinfo = null;
 				}
 				break;
 			case 'string':
 				if(!!msg.data) {
 					var data = JSON.parse(msg.data);
 					switch(data.name) {
 						case 'blob':
 							blobinfo = data.data;
 							break;
 						default:
 							if(!!handlers[data.name]) {
 								handlers[data.name].forEach(function(fn) {
 									fn.call(this, data.data);
 								});
 							}
 							break;
 					}
 				}
 				break;
 		}
 	}
 	function checkBinary(obj) {
 		return (checkBlob(obj) || checkArrayBuffer(obj));
 		function checkBlob(obj) {
 			return (typeof obj==='object' && 
 				!!obj.slice && typeof obj.slice==='function');
 		}
 		function checkArrayBuffer(obj) {
 			return (typeof obj==='object' && !!obj.byteLength && obj.byteLength>0);
 		}
 	}
 }
 
 })(window);

接下來是ws.io/lib目錄,所有套件實作的程式都放在這裡。首先是模組的主檔(ws.io.js):

 var server = require('http').createServer();
 var Manager = require('./Manager');
 
 exports.listen = function(arg) {
 	if(typeof arg === 'number') {
 		server.listen(arg);
 		return new Manager(server);
 	} else {
 		return new Manager(arg);
 	}
 };

接下來是Manager.js,這裡定義了Manager物件:

 var Namespace = require('./Namespace');
 
 var Manager = module.exports = function(server) {
 	this.server = server;
 	this.namespaces = {};
 	this.store = {};
 	this.rooms = {};
 	this.handlers = {};
 	this.blobinfo = {};
 };
 
 Manager.prototype.of = function(ns) {
 	var namespace = new Namespace(this.server, this, ns);
 	return namespace;
 };
 
 Manager.prototype.__defineGetter__('sockets', function() {
 	return this.of('/');
 });

Namespace.js,ws模組的事件都在這裡處理,然後轉發到使用者定義的事件處理函數:

 var Socket = require('./Socket');
 var wss = require('ws').Server;
 
 var Namespace = module.exports = function(server, manager, ns) {
 	this.namespace = ns;
 	this.socket = Socket;
 	this.wss = new wss({server: server});
 	this.manager = manager;
 	this.server = server;
 	this.manager.handlers[ns] = {};
 	this.manager.namespaces[this.namespace] = {};
 	this.manager.blobinfo[this.namespace] = {};
 }
 
 Namespace.prototype.on = function(conn, handler) {
 	var self = this;
 	if(conn == 'connection') {
 		this.wss.on('connection', function(ws) {
 			var id = Math.random()*10000 + '-' + new Date().getTime();
 			var socket = new Socket(self.server, self.manager, self, id, ws);
 			if(!!self.manager.namespaces[self.namespace]['all']) {
 				self.manager.namespaces[self.namespace]['all'][id] = socket;
 			} else {
 				self.manager.namespaces[self.namespace]['all'] = {};
 				self.manager.namespaces[self.namespace]['all'][id] = socket;
 			}
 			if(!self.manager.handlers[self.namespace]) {
 				self.manager.handlers[self.namespace] = {};
 			}
 			if(!self.manager.handlers[self.namespace][id]) {
 				self.manager.handlers[self.namespace][id] = {};
 			}
 			if(!self.manager.blobinfo[self.namespace][id]) {
 				self.manager.blobinfo[self.namespace][id] = null;
 			}
 			ws.on('message', function(data, flags) {
 				if(typeof data === 'string') {
 					var args = JSON.parse(data);
 					var msg = true;
 					var tmp = ['open', 'close', 'error', 'ping', 'pong'];
 					tmp.forEach(function(n) {
 						if(n===args.name) msg = false;
 					});
 					if(!msg) return;
 
 					if(args.name==='blob') {
 						if(!!self.manager.blobinfo[self.namespace]) {
 							self.manager.blobinfo[self.namespace][id] = args.data;
 						}
 						return;
 					}
 					if(!!self.manager.handlers[self.namespace][id][args.name]) {
 						self.manager.handlers[self.namespace][id][args.name].call(self, args.data);
 						ev = null;
 					} else {
 						console.log('no handler: ');
 					}
 				} else {
 					if(!!self.manager.blobinfo[self.namespace][id]) {
 						var name = self.manager.blobinfo[self.namespace][id].name;
 						if(!!self.manager.handlers[self.namespace][id][name]) {
 							var msg = self.manager.blobinfo[self.namespace][id];
 							msg[msg.blobfield] = data;
 							delete msg.blobfield;
 							self.manager.handlers[self.namespace][id][name].call(self, msg);
 							self.manager.blobinfo[self.namespace][id] = null;
 						}
 					}
 				}
 			});
 			ws.on('open', function() {
 				if(!!self.manager.handlers[self.namespace][id]['open']) {
 					self.manager.handlers[self.namespace][id]['open'].call(self);
 				}
 			});
 			ws.on('close', function(code, message) {
 				if(!!self.manager.handlers[self.namespace][id]['close']) {
 					self.manager.handlers[self.namespace][id]['close'].call(self, code, message);
 				}
 			});
 			ws.on('error', function(error) {
 				if(!!self.manager.handlers[self.namespace][id]['error']) {
 					self.manager.handlers[self.namespace][id]['error'].call(self, error);
 				}
 			});
 			ws.on('ping', function(data, flags) {
 				if(!!self.manager.handlers[self.namespace][id]['ping']) {
 					self.manager.handlers[self.namespace][id]['ping'].call(self, data, flags);
 				}
 			});
 			ws.on('pong', function(data, flags) {
 				if(!!self.manager.handlers[self.namespace][id]['pong']) {
 					self.manager.handlers[self.namespace][id]['pong'].call(self);
 				}
 			});
 			handler.call(socket, socket);
 		});
 	}
 }

最後是Socket.js物件,大部分使用者操作在這裡實作:

 var Socket = module.exports = function(server, manager, namespace, id, ws) {
 	this.id = id;
 	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) {
 	this.manager.handlers[this.namespace][this.id][name] = handler;
 };
 
 Socket.prototype.emit = function(name, data, opt) {
 	var room = '';
 	if(!this.flags.inroom) {
 		room = 'all';
 	} else {
 		room = this.flags.inroom;
 	}
 	var targets = [];
 	if(this.flags['allnotme']) {
 		for(var i in this.manager.namespaces[this.namespace][room]) {
 			if(this.flags.allnotme) {
 				if(i.indexOf(this.id)<0) {
 					targets.push(this._findSocket(this.namespace, room, i));
 				}
 			} else {
 				targets.push(this._findSocket(this.namespace, room, i));
 			}
 		}
 	} else {
 		targets.push(this);
 	}
 	var self = this;
 	//data['name'] = name;
 	var isblob = false;
 	for(var i in data) {
 		if(Buffer.isBuffer(data[i])) {
 			var blob = data[i];
 			delete data[i];
 			data['name'] = name;
 			data['blobfield'] = i;
 			isblob = true;
 			break;
 		}
 	}
 	targets.forEach(function(socket) {
 		if(isblob) {
 			socket.ws.send(JSON.stringify({name:'blob',data:data}));
 			socket.ws.send(blob, {binary:true,mask:false});
 		} else {
 			socket.ws.send(JSON.stringify({name:name,data: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, id) {
 	if(!!this.manager) {
 		if(!!this.manager.namespaces[ns]) {
 			if(!!this.manager.namespaces[ns][room]) {
 				if(!!this.manager.namespaces[ns][room][id]) {
 					return this.manager.namespaces[ns][room][id];
 				}
 			}
 		}
 	}
 };
 
 Socket.prototype.join = function(room) {
 	if(!!this.manager.namespaces[this.namespace][room]) {
 		this.manager.namespaces[this.namespace][room][this.id] = this;
 	} else {
 		this.manager.namespaces[this.namespace][room] = {};
 		this.manager.namespaces[this.namespace][room][this.id] = this;
 	}
 };
 
 Socket.prototype.leave = function(room) {
 	if(!!this.manager.namespaces[this.namespace][room]) {
 		if(!!this.manager.namespaces[this.namespace][room][this.id]) {
 			delete this.manager.namespaces[this.namespace][room][this.id];
 		}
 	}
 	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.id]) {
 			check = true;
 		}
 	}
 	if(check) {
 		this.flags['inroom'] = room;
 	}
 	return this;
 }
 
 Socket.prototype.set = function(name, value, cb) {
 	if(!this.manager.store[this.id]) {
 		this.manager.store[this.id] = {};
 	}
 	this.manager.store[this.id][name] = value;
 	cb();
 };
 
 Socket.prototype.get = function(name, cb) {
 	if(!!this.manager.store[this.id]) {
 		if(!!this.manager.store[this.id][name]) {
 			cb(false, this.manager.store[this.id][name]);
 		} else {
 			cb(true);
 		}
 	} else {
 		this.manager.store[this.id] = {};
 		cb(true);
 	}
 };
 
 Socket.prototype.has = function(name, cb) {
 	if(!!this.manager.store[this.id]) {
 		if(!!this.manager.store[this.id][name]) {
 			cb(false, true);
 		} else {
 			cb(true, true);
 		}
 	} else {
 		this.manager.store[this.id] = {};
 		cb(true, false);
 	}
 };
 
 function checkBinary(obj) {
 	return Buffer.isBuffer(obj);
 }

在package.json已經定義好模組依賴性,所以進入ws.io目錄後,執行'npm rebuild'就會把ws安裝在ws.io/node_modules目錄中。

接下來看上述測試test844.html執行的結果:

可以看到在三個網頁中分別選擇三個檔案,這些檔案會出現在所有網頁的列表中,點選以後就可以打開檔案。

OK,就先把資源分享的主題告一段落,明天開始測試WebRTC,並使用他做出視訊會議的功能。

(2012-11-15 22:56補充)
今天中午把ws.io調整了一下,讓namespace能真正運作。這個部分還是繞了幾圈,主要是因為對於ws不夠了解,文件也不太清楚。本來以為他的WebSocket物件的url屬性,就是client請求時的url的pathname(host之後的資源位置),結果這是WebSocket物件當做WebSocket client來使用時才會用到的。實際上在當Server使用時,ws是在configuration中傳給他要handle的pathname,所以一個path可以用一個WebSocket物件來handle,共享一個http伺服器。

了解了之後,其實要調整沒有花很多時間。主要調整的有三支程式首先是Manager.js:

var Namespace = require('./Namespace');

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

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

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

還有Namespace.js:

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


var Namespace = module.exports = function(server, manager, ns) {
	this.manager = manager;
	this.namespace = ns;
	this.manager.handlers[ns] = {};
	this.manager.namespaces[this.namespace] = {};
	this.manager.blobinfo[this.namespace] = {};
	this.rooms = {all:{}};
	this.wss = new wss({
		server: server,
		path: ns
	});
};

Namespace.prototype.on = function(conn, handler) {
	if(conn == 'connection') {
//---------------------
	var self = this;
	this.wss.on('connection', function(ws) {
		if(!!self.manager.namespaces[self.namespace]) {
			var id = Math.random()*10000 + '-' + new Date().getTime();
			var socket = self.createSocket(id, ws);
		} else {
			console.log('namespace object not exists.');
			return;
		}
		ws.on('message', function(data, flags) {
			console.log('message received to '+id);
			console.log(data);
			if(typeof data === 'string') {
				var args = JSON.parse(data);
				var msg = true;
				var tmp = ['open', 'close', 'error', 'ping', 'pong'];
				tmp.forEach(function(n) {
					if(n===args.name) msg = false;
				});
				if(!msg) return;
				if(args.name==='blob') {
					if(!!self.manager.blobinfo[self.namespace]) {
						self.manager.blobinfo[self.namespace][id] = args.data;
					}
					return;
				}
				if(!!self.manager.handlers[self.namespace][id][args.name]) {
					self.manager.handlers[self.namespace][id][args.name].call(self, args.data);
					ev = null;
				} else {
					console.log('no handler: ');
				}
			} else {
				if(!!self.manager.blobinfo[self.namespace][id]) {
					var name = self.manager.blobinfo[self.namespace][id].name;
					if(!!self.manager.handlers[self.namespace][id][name]) {
						var msg = self.manager.blobinfo[self.namespace][id];
						msg[msg.blobfield] = data;
						delete msg.blobfield;
						self.manager.handlers[self.namespace][id][name].call(self, msg);
						self.manager.blobinfo[self.namespace][id] = null;
					}
				}
			}
		});
		ws.on('open', function() {
			if(!!self.manager.handlers[self.namespace][id]['open']) {
				self.manager.handlers[self.namespace][id]['open'].call(self);
			}
		});
		ws.on('close', function(code, message) {
			if(!!self.manager.handlers[self.namespace][id]['close']) {
				self.manager.handlers[self.namespace][id]['close'].call(self, code, message);
			}
			delete self.manager.handlers[self.namespace][id];
			if(!!self.manager.store[id]) {
				delete self.manager.store[id];
			}
			if(!!self.manager.blobinfo[self.namespace][id]) {
				delete self.manager.blobinfo[self.namespace][id];
			}
			self.manager.namespaces[self.namespace].destroySocket(id);
		});
		ws.on('error', function(error) {
			if(!!self.manager.handlers[self.namespace][id]['error']) {
				self.manager.handlers[self.namespace][id]['error'].call(self, error);
			}
		});
		ws.on('ping', function(data, flags) {
			if(!!self.manager.handlers[self.namespace][id]['ping']) {
				self.manager.handlers[self.namespace][id]['ping'].call(self, data, flags);
			}
		});
		ws.on('pong', function(data, flags) {
			if(!!self.manager.handlers[self.namespace][id]['pong']) {
				self.manager.handlers[self.namespace][id]['pong'].call(self);
			}
		});
		handler.call(socket, socket);
	});
//---------------------	
	}
};

Namespace.prototype.createSocket = function(id, ws) {
	var socket = new Socket(this.manager, this, id, ws);
	if(!this.manager.handlers[this.namespace]) {
		this.manager.handlers[this.namespace] = {};
	}
	if(!this.manager.handlers[this.namespace][id]) {
		this.manager.handlers[this.namespace][id] = {};
	}
	if(!this.manager.blobinfo[this.namespace][id]) {
		this.manager.blobinfo[this.namespace][id] = null;
	}
	if(!this.rooms['all']) {
		this.rooms['all'] = {};
	}
	this.rooms['all'][id] = socket;
	return socket;
};

Namespace.prototype.destroySocket = function(id) {
	for(var i in this.rooms) {
		if(!!this.rooms[i][id]) {
			delete this.rooms[i][id];
		}
	}
	if(!!this.manager.handlers[this.namespace]) {
		if(!!this.manager.handlers[this.namespace][id]) {
			delete this.manager.handlers[this.namespace][id];
		}
	}
	if(!!this.manager.blobinfo[this.namespace]) {
		if(!!this.manager.blobinfo[this.namespace][id]) {
			delete this.manager.blobinfo[this.namespace][id];
		}
	}
};

最後是大部分操作所在的Socket.js

var Socket = module.exports = function(manager, namespace, id, ws) {
	this.id = id;
	this.manager = manager;
	this.ws = ws;
	this.namespace = namespace;
	this.flags = {"allnotme":false,"inroom":false,"to":false};
	if(!this.manager.handlers[this.namespace.namespace]) {
		this.manager.handlers[this.namespace.namespace] = {};
		this.manager.handlers[this.namespace.namespace][id] = {};
	}
};

Socket.prototype.on = function(name, handler) {
	this.manager.handlers[this.namespace.namespace][this.id][name] = handler;
};

Socket.prototype.emit = function(name, data, opt) {
	var room = '';
	if(!this.flags.inroom) {
		room = 'all';
	} else {
		room = this.flags.inroom;
	}
	var targets = [];
	if(this.flags['allnotme']) {
		for(var i in this.namespace.rooms[room]) {
			if(this.flags.allnotme) {
				if(i.indexOf(this.id)<0) {
					targets.push(this._findSocket(room, i));
				}
			} else {
				targets.push(this._findSocket(room, i));
			}
		}
	} else {
		targets.push(this);
	}
	if(this.flags['to']) {
		targets = [];
		targets.push(this._findSocket(room, this.flags['to']));
	}
	var self = this;
	//data['name'] = name;
	var isblob = false;
	for(var i in data) {
		if(checkBinary(data[i])) {
			var blob = data[i];
			delete data[i];
			data['name'] = name;
			data['blobfield'] = i;
			isblob = true;
			break;
		}
	}
	targets.forEach(function(socket) {
		console.log('message reply to '+socket.id);
		console.log(data);
		if(isblob) {
			socket.ws.send(JSON.stringify({name:'blob',data:data}));
			socket.ws.send(blob, {binary:true,mask:false});
		} else {
			socket.ws.send(JSON.stringify({name:name,data:data}));
		}
	});
	this.flags['allnotme'] = false;
	this.flags['inroom'] = false;
	this.flags['to'] = false;
};

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

Socket.prototype.to = function(id) {
	this.flags.allnotme = false;
	this.flags.inroom = false;
	this.flags.to = id;
	return this;
}

Socket.prototype._findSocket = function(room, id) {
	if(!!this.namespace.rooms[room]) {
		if(!!this.namespace.rooms[room][id]) {
			return this.namespace.rooms[room][id];
		}
	}
};

Socket.prototype.join = function(room) {
	if(!!this.namespace.rooms[room]) {
		this.namespace.rooms[room][this.id] = this;
	} else {
		this.namespace.rooms[room] = {};
		this.namespace.rooms[room][this.id] = this;
	}
};

Socket.prototype.joined = function(room) {
	return (!!this.namespace.rooms[room] && !!this.namespace.rooms[room][this.id]);
}

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

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

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

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

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

function checkBinary(obj) {
	if(Buffer.isBuffer(obj)) {
		if(!!obj.copy) {
			return true;
		}
	}
	return false;
}

使用方式沒有改變,只是可以利用

io.of(pathname).on('connection', function(socket){
    socket.on('client send event', function(data) {
         socket.emit('server send event', data);
    });
});

的方式,同時服務更多的子目錄。

(2012-11-17 1:45 補充)
把ws.io放到github了:https://github.com/fillano/ws.io


上一篇
且戰且走HTML5(25) 解決ws.io模組的問題
下一篇
且戰且走HTML5(27) 應用的主軸:WebRTC
系列文
且戰且走HTML530
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
0
ted99tw
iT邦高手 1 級 ‧ 2012-11-03 20:52:26

筆記筆記筆記
費公的鐵文就算是在放假時也是要用力看滴!!!
讚讚讚

0
chaocraig
iT邦新手 5 級 ‧ 2012-11-17 23:10:48

有 git 下來了,但是 npm rebuild 有錯誤!!
% npm rebuild
npm ERR! TypeError: Object #<Object> has no method 'trim'
npm ERR! at /usr/local/Cellar/node/0.8.14/lib/node_modules/npm/node_modules/read-package-json/read-json.js:494:39
npm ERR! at Array.forEach (native)
npm ERR! at depObjectify (/usr/local/Cellar/node/0.8.14/lib/node_modules/npm/node_modules/read-package-json/read-json.js:493:22)
npm ERR! at objectifyDep_ (/usr/local/Cellar/node/0.8.14/lib/node_modules/npm/node_modules/read-package-json/read-json.js:484:30)
npm ERR! at /usr/local/Cellar/node/0.8.14/lib/node_modules/npm/node_modules/read-package-json/read-json.js:471:33
npm ERR! at Array.forEach (native)
npm ERR! at objectifyDeps (/usr/local/Cellar/node/0.8.14/lib/node_modules/npm/node_modules/read-package-json/read-json.js:470:26)
npm ERR! at final (/usr/local/Cellar/node/0.8.14/lib/node_modules/npm/node_modules/read-package-json/read-json.js:307:17)
npm ERR! at /usr/local/Cellar/node/

fillano iT邦超人 1 級 ‧ 2012-11-18 01:01:23 檢舉

看起來像是package.json的問題,我測試一下看看。

0
fillano
iT邦超人 1 級 ‧ 2012-11-18 02:08:45

請git pull一次,我改過package.json了。如果之前沒有安裝過ws,可以在目錄中npm intall,之前安裝過,則可npm rebuild。

chaocraig iT邦新手 5 級 ‧ 2012-11-20 10:53:35 檢舉

OK了! 讚! :-)

我要留言

立即登入留言