iT邦幫忙

DAY 22
2

且戰且走HTML5系列 第 22

且戰且走HTML5(22) 調整ws模組

  • 分享至 

  • xImage
  •  

為了讓ws操作起來像Socket.IO,在這裡需要用幾個類別來包裝。
先來分析一下需求,才知道到底要包裝到什麼程度。從之前寫的Chat程式,大致上有幾樣操作:

  1. io = require('socket.io').listen(app) (io就是Manager物件的實例,通常每個伺服器程式只會有一個,app是一個http伺服器)
  2. io.sockets.on('connection', function(socket){}) (相當於處理/路徑的WebSocket的on connection事件)
  3. io.of(namespace).on('connection', function(socket){})(相當於處理/namespace路徑的WebSocket的on connection事件)
  4. socket.manager (io,就是Manager物件的instance,透過Socket的實例化過程傳給了Socket物件)
  5. socket.on (handle clint emit) (透過client事件觸發)
  6. socket.emit (emit client event) (觸發client事件)
  7. socket.set(name, value, callback) (存取附屬於Manager的儲存體)
  8. socket.get(name, callback(err, data))(存取附屬於Manager的儲存體)
  9. socket.has(room, callback(err, data))(存取附屬於Manager的儲存體)
  10. socket.join(room) (加入room)
  11. socket.leave(room) (離開room)
  12. socket.in(room)(針對room全體操作)
  13. socket.in(room).emit(針對room中的連線,觸發client事件)
  14. socket.broadcast (送給我之外的所有人)
  15. socket.broadcast.in(room).emit…(送給room中我之外的所有人)

如果追蹤過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好了。

先做一些基本配置:

  1. package.json:定義套件的資訊
  2. index.js:是套件主檔,不過他實際上只是回傳lib/ws.io.js
  3. lib/ws.io.js:是模組主檔,也是真正的進入點
  4. lib/Manager.js:定義Manager物件的模組
  5. lib/Socket.js:定義Socket物件的模組

在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);
 		})
 	}
 }

就是樣,先拉一閜簡單的骨架,然後再把完整的功能實作出來。這部份就留到明天吧。目標還是使用最小幅度的包裝,讓之前的程式碼可以盡量不修改就能使用。不過最好還是一步一步來,明天先把目前實作的部份做基本的測試。


上一篇
且戰且走HTML5(21) 應用的主軸:FileAPI、URL
下一篇
且戰且走HTML5(23) 加入類似Socket.IO的on與emit方法
系列文
且戰且走HTML530
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

我要留言

立即登入留言