為了讓ws操作起來像Socket.IO,在這裡需要用幾個類別來包裝。
先來分析一下需求,才知道到底要包裝到什麼程度。從之前寫的Chat程式,大致上有幾樣操作:
如果追蹤過Socket.IO就可以知道透過上述一,會取得Manager物件的實體。由於他是每個伺服器只有一個的物件,所以一些全域的操作,都要經過他。io.of,是定義namespace的操作,他會回傳一個Socket物件的實體,每個連線就會有一個Socket物件做對應。所以針對個別連線的操作,就會放在Socket物件中。至於io.sockets,其實就是使用預設的namespace。
Socket.on可以定義一個處理經由client觸發事件的處理函數,Socket.emit則會觸發client的事件。所以WebSocket的send與onmessage事件,會包裝在這裡。另外,在每個Socket物件產生時。都會給他一個session id (sid),一些透過Socket的操作,是經由sid做為key,再透過操作名稱來查找的方式來運作。
透過broadcast會觸發一個旗標,讓群發的動作可以排除自己,否則透過in(room)發送資料的動作,就會發送給room內的所有人。完全沒指定的話,資料只會回傳給自己。
基本邵這需要好幾個模組一起協作,所以最好寫成套件,就姑且稱作ws.io好了。
先做一些基本配置:
在package.json定義好依賴性後,就可以透過npm rebuild來把依賴的模組也裝進來。然後就是幾個檔案的初步內容。首先是index.js
module.exports = require('./lib/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"}
]
}
然後是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);
}
};
lib/Manager.js
var Socket = require('./Socket');
var Manager = module.exports = function(server) {
this.server = server;
this.namespaces = {};
this.storage = {};
this.rooms = {};
};
Manager.prototype.of = function(ns) {
var sid = Math.random()*1000000 + '' + new Date().getTime();
var socket = new Socket(this.server, this, ns, sid);
if(!!this.namespaces[ns]) {
this.namespaces[ns].push(socket);
} else {
this.namespaces[ns] = [];
this.namespaces[ns].push(socket);
}
return socket;
};
Manager.prototype.__defineGetter__('sockets', function() {
return this.of('/');
});
lib/Socket.js
var WebsocketServer = require('ws').Server;
var handlers = {};
var Socket = module.exports = function(server, manager, namespace) {
this.server = server;
this.manager = manager;
this.websocket = new WebsocketServer({this.server});
this.namespace = namespace;
};
Socket.prototype.on = function(name, handler) {
switch(name) {
case 'connection':
this.websocket.on('connection', handler);
break;
default:
if(!!handlers[name]) {
handlers[name].push(handler);
} else {
handlers[name] = [];
handlers[name].push(handler);
}
}
};
Socket.prototype.emit = function() {
var name = arguments[0];
var args = [];
for(var i=1; i<arguments.length; i++) {
args.push(arguments[i]);
}
if(!!handlers[name]) {
handlers[name].forEach(function(fn) {
fn.apply(this, args);
})
}
}
就是樣,先拉一閜簡單的骨架,然後再把完整的功能實作出來。這部份就留到明天吧。目標還是使用最小幅度的包裝,讓之前的程式碼可以盡量不修改就能使用。不過最好還是一步一步來,明天先把目前實作的部份做基本的測試。