websocket + node.js

websocket的原理和應用

在繼續(xù)本文之前,讓我們了解下websocket的原理:

websocket通信協(xié)議實現(xiàn)的是基于瀏覽器的原生socket,這樣原先只有在c/s模式下的大量開發(fā)模式都可以搬到web上來了,基本就是通過瀏覽器的支持在web上實現(xiàn)了與服務器端的socket通信。

WebSocket沒有試圖在HTTP之上模擬server推送,而是直接在TCP之上定義了幀協(xié)議,因此WebSocket能夠支持雙向的通信。

首先來介紹下websocket客戶端與服務端建立連接的過程:

先用js創(chuàng)建一個WebSocket實例,使用ws協(xié)議建立服務器連接,ws://www.cnodejs.org:8088

ws開頭是普通的websocket連接,wss是安全的websocket連接,類似于https。

客戶端與服務端建立握手,發(fā)送如下信息:

GET /echo HTTP/1.1

Upgrade: WebSocket

Connection: Upgrade

Host:http://www.cnodejs.org:8088

Origin:http://www.cnodejs.com

服務端會發(fā)回如下:

HTTP/1.1 101 Web Socket Protocol Handshake

Upgrade: WebSocket

Connection: Upgrade

WebSocket-Origin:http://www.cnodejs.org

WebSocket-Location: ws://www.cnodejs.org:8088/echo

具體的ws協(xié)議,可以參考:http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76

我們在開發(fā)過程中不需要考慮協(xié)議的細節(jié),因為websocket API已經(jīng)幫我們封裝好了。

需要注意的是所有的通信數(shù)據(jù)都是以”\x00″開頭以”\xFF”結尾的,并且都是UTF-8編碼的。

這個過程類似于http的建立連接過程,不同的是,建立連接之后,接下來客戶端和服務端的任何交互都需要再有這個動作??蛻舳送ㄟ^websocket API提供的如下4個事件進行編程:

onopen 建立連接后觸發(fā)

onmessage 收到消息后觸發(fā)

onerror 發(fā)生錯誤時觸發(fā)

onclose 關閉連接時觸發(fā)

讓我們?nèi)媪私庖幌聎ebsocket API,他其實非常簡單,下面是所有的API:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23[Constructor(in DOMString url, in optional DOMString protocols)]

[Constructor(in DOMString url, in optional DOMString[] protocols)]

interface WebSocket {

readonly attribute DOMString url;

// ready state

const unsigned short CONNECTING = 0;

const unsigned short OPEN = 1;

const unsigned short CLOSING = 2;

const unsigned short CLOSED = 3;

readonly attribute unsigned short readyState;

readonly attribute unsigned long bufferedAmount;

// networking

attribute Function onopen;

attribute Function onmessage;

attribute Function onerror;

attribute Function onclose;

readonly attribute DOMString protocol;

void send(in DOMString data);

void close();

};

WebSocket implements EventTarget;

詳細的websocket API,可以參考此文:http://dev.w3.org/html5/websockets/

node.js與websocket的結合

終于講到了正題了,node.js如何與websocket結合,websocket API是基于事件的,他是對于客戶端而言,而對于服務端來說,如何來處理呢?其實可以簡單的理解為實現(xiàn)websocket協(xié)議的socket server開發(fā)。

node.js天生就是一個高效的服務端語言,可以直接使用 javascript直接來處理來自客戶端的請求,這樣如果服務端這邊需要大量的業(yè)務邏輯開發(fā),則可以直接使用node開發(fā)。通過node和 websocket的結合可以開發(fā)出很多實時性要求很高的web應用,如游戲、直播、股票、監(jiān)控、IM等等。

而node.js如何實現(xiàn)websocket的支持,已經(jīng)有一個比較成熟的開源系統(tǒng)node-websocket-server:https://github.com/miksago/node-websocket-server,讓我們來探究一二:

其實原理也是很簡單就是用node實現(xiàn)了websocket draft-76的協(xié)議,同時他對外提供了api,可以方便其他應用程序簡化編程。

它繼承了node的http.Server的事件和方法,這樣它簡化了服務端的編程,同時可以處理http的請求。

為了實現(xiàn)連接之間的通信和消息的廣播,它實現(xiàn)了一個manager類,給每一個連接創(chuàng)建一個id,然后在內(nèi)存中維護一個連接鏈表,并提供了上線和下線的自動管理。

它還提供對以下幾個事件的接口:

listening 當服務器準備好接受客戶端請求時

request 當一個http 請求發(fā)生時觸發(fā)

stream

close

clientError

error

讓我們看看一個node-websocket-server提供的一個server的例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27var sys = require("sys")

, ws = require('../lib/ws/server');

var server = ws.createServer({debug: true});

// Handle WebSocket Requests

server.addListener("connection", function(conn){

conn.send("Connection: "+conn.id);

conn.addListener("message", function(message){

conn.broadcast("<"+conn.id+"> "+message);

if(message == "error"){

conn.emit("error", "test");

}

});

});

server.addListener("error", function(){

console.log(Array.prototype.join.call(arguments, ", "));

});

server.addListener("disconnected", function(conn){

server.broadcast("<"+conn.id+"> disconnected");

});

server.listen(8000);

這個例子非常的簡單,可以看到對于websocket的server端開發(fā),我們已經(jīng)不需要考慮 websocket協(xié)議的實現(xiàn),他幾乎有著和客戶端瀏覽器上websocket API一樣的事件,只有對連接、斷開連接、消息、錯誤等事件進行處理,這樣應用的開發(fā)就非常的靈活了。

實例:用websocket和node.js搭建實時監(jiān)控系統(tǒng)

通過websocket打通了瀏覽器和服務端之后,我們就可以嘗試搭建一個實際的應用,這里以實時監(jiān)控系統(tǒng)為例。

直接與linux自身監(jiān)控工具的結合,將監(jiān)控結果通過websocket直接更到網(wǎng)頁上,由于建立了socket長連接,綁定iostat的標準輸 出的事件,做到了真正的實時。同時可以支持對監(jiān)控結果的討論,增加了一個簡單的chat,基于事件的通訊中,chat和監(jiān)控同時發(fā)送完全不受影響,所以還 可以把更多的事件加入進來。

讓我們來看看這個過程:

首先是用node.js捕獲iostat的輸出:

1

2

3

4

5

6var sys = require("sys")

, ws = require('../lib/ws/server');

var sys = require('sys');

var spawn = require('child_process').spawn;

var mon = spawn("iostat",["-I","5"]);

spawn可以根據(jù)參數(shù)啟動一個進程,同時可以對stdout, stderr, exit code進行捕獲,當這些事件觸發(fā)時,可以綁定我們的函數(shù),同時捕獲其輸出。

這里是iostat的標準輸出:

disk0 cpu load average

KB/t tps MB/s us sy id 1m 5m 15m

14.64 4 0.06 7 5 88 0.76 0.95 0.90

我們捕獲他的輸出,將其發(fā)送到客戶端去:

1

2

3

4

5mon.stdout.on('data',function(data) {

data = format_string(data);

sys.puts(data);

conn.send("#mon:"+data+"");

});

客戶端也就是瀏覽器,在收到消息后,對其進行簡單的字符串處理,然后就可以直接在網(wǎng)頁中輸出了。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15w.onmessage = function(e) {

var msg = e.data;

if(msg.match(/#mon:/)) {

var monarr = msg.split(":")[1].split(" ");

var body = "";

for(var item in monarr) {

"+monarr[item]+"

}

$("#iobody").html(body);

//log(monarr[0]);

}

else

log(e.data);

}

這里自定義了一個#mon的簡單協(xié)議,這樣可以對更多類型的輸出分開處理。

服務端和客戶端總共100多行的代碼,就已經(jīng)實現(xiàn)了一個實時服務器性能監(jiān)控系統(tǒng)。

全部代碼下載地址:http://cnodejs.googlecode.com/svn/trunk/monsocket/examples/

(注:本程序僅在mac osx下測試通過)

如果加上RGraph(基于html5),則可以打造更加精美的實時展現(xiàn): ?http://www.rgraph.net/docs/dynamic.html

總結

這篇文章適合node.js的初學者或者對于websocket不夠了解的人,總結起來,就是以下幾個點:

使用websocket API可以開發(fā)web應用實時

websocket api和 node.js可以很完美的配合

node-websocket-server 封裝了websocket協(xié)議,使服務端進行websocket的開發(fā),非常的簡單

node的易用性,使其在服務端略加編程,即可以打造一個完美的后臺服務

node的事件驅(qū)動的機制保證了系統(tǒng)的高效,可以使用EventEmitter定義自己的事件觸發(fā)

對于命令行輸出可以使用spawn來捕獲,通過在web應用中充分利用linux的各種系統(tǒng)工具

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容