1. 程式人生 > >websocket與node.js完美結合

websocket與node.js完美結合

nds node spl 簡化 下載地址 iostat match 根據 所有

之所以寫下此文,是我覺得越是簡單的技術往往能發揮越重要的作用,隨著各種新的技術的誕生,實時web技術已經走進我們。websocket和node.js使開發實時應用非常簡單,同時性能也非常高。

關於websocket

websocket是html5的重要feature,它直接在瀏覽器上對與socket的支持,這給了web開發無限的想象,雖然以前也有 flash socket+js的實現,不過畢竟不穩定,而且兼容性有很多問題,當然websocket的普及也依賴於支持html5標準的瀏覽器的更新,目前只有 chrome、safari、firefox 4.0等少數瀏覽器可以支持,不過大勢所驅,加上智能移動設備的普及,websocket可以有更大的作為。

他解決了web實時化的問題,相比傳統http有如下好處:

  • 一個WEB客戶端只建立一個TCP連接
  • Websocket服務端可以推送(push)數據到web客戶端.
  • 有更加輕量級的頭,減少數據傳送量

本文來重點來分析下。

websocket的原理和應用

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

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

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

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

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

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

客戶端與服務端建立握手,發送如下信息:
GET /echo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: http://www.cnodejs.org:8088
Origin: http://www.cnodejs.com

服務端會發回如下:
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協議,可以參考: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76

我們在開發過程中不需要考慮協議的細節,因為websocket API已經幫我們封裝好了。

需要註意的是所有的通信數據都是以”\x00″開頭以”\xFF”結尾的,並且都是UTF-8編碼的。

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

  • onopen 建立連接後觸發
  • onmessage 收到消息後觸發
  • onerror 發生錯誤時觸發
  • onclose 關閉連接時觸發

讓我們全面了解一下websocket 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是基於事件的,他是對於客戶端而言,而對於服務端來說,如何來處理呢?其實可以簡單的理解為實現websocket協議的socket server開發。

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

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

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

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

為了實現連接之間的通信和消息的廣播,它實現了一個manager類,給每一個連接創建一個id,然後在內存中維護一個連接鏈表,並提供了上線和下線的自動管理。

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

  • listening 當服務器準備好接受客戶端請求時
  • request 當一個http 請求發生時觸發
  • 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
27
var 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端開發,我們已經不需要考慮 websocket協議的實現,他幾乎有著和客戶端瀏覽器上websocket API一樣的事件,只有對連接、斷開連接、消息、錯誤等事件進行處理,這樣應用的開發就非常的靈活了。

實例:用websocket和node.js搭建實時監控系統

通過websocket打通了瀏覽器和服務端之後,我們就可以嘗試搭建一個實際的應用,這裏以實時監控系統為例。

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

讓我們來看看這個過程:

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

1
2
3
4
5
6
var 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可以根據參數啟動一個進程,同時可以對stdout, stderr, exit code進行捕獲,當這些事件觸發時,可以綁定我們的函數,同時捕獲其輸出。
這裏是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

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

1
2
3
4
5
  mon.stdout.on(‘data‘,function(data) {
    data = format_string(data);
    sys.puts(data);
    conn.send("#mon:"+data+"");
  });

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
w.onmessage = function(e) {
    var msg = e.data;
    if(msg.match(/#mon:/)) {
        var monarr = msg.split(":")[1].split(" ");
        var body = "";
        for(var item in monarr) {
            body += "<td id=‘io_"+item+"‘>"+monarr[item]+"</td>"
        }
        $("#iobody").html(body);
        //log(monarr[0]);

    }
    else
        log(e.data);
}

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

服務端和客戶端總共100多行的代碼,就已經實現了一個實時服務器性能監控系統。
全部代碼下載地址: http://cnodejs.googlecode.com/svn/trunk/monsocket/examples/
(註:本程序僅在mac osx下測試通過)

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

總結

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

  • 使用websocket API可以開發web應用實時
  • websocket api和 node.js可以很完美的配合
  • node-websocket-server 封裝了websocket協議,使服務端進行websocket的開發,非常的簡單
  • node的易用性,使其在服務端略加編程,即可以打造一個完美的後臺服務
  • node的事件驅動的機制保證了系統的高效,可以使用EventEmitter定義自己的事件觸發
  • 對於命令行輸出可以使用spawn來捕獲,通過在web應用中充分利用linux的各種系統工具

參考資料

  • http://nodejs.org/
  • http://dev.w3.org/html5/websockets/
  • http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
  • https://github.com/miksago/node-websocket-server

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

websocket與node.js完美結合