iT邦幫忙

DAY 4
11

node.js伺服器實戰系列 第 4

node.js伺服器實戰(4) - 內建模組與http伺服器開發的必備知識

就簡單介紹一下node.js內建有哪些模組可以使用。另外,先稍微看一下怎麼使用http模組。
內建模組簡介

node.js有一些模組是預編譯在執行檔裡面的,不過除了Buffer、Process、Console、TypedArray及Timer相關函數是內建在Global裡面,其他則需要利用require來載入。

關於各個模組的詳細說明,可以直接參考官方的API文件:http://nodejs.org/docs/v0.5.9/api/ (這是node-v0.5.9的文件,每一個版本的目錄都不一樣),我不會做詳細的介紹,以下是根據最新的開發版(v0.5.9)做的一個概略的說明:

* STDIO(console): 就是global中的console物件,可以透過他輸出資訊到標準輸出或標準錯誤,也可以輸出物件資訊、計算執行時間等
* Timers:也就是global中的setTimeout/setInterval等函數
* Process(process):就是global中的process物件,提供行程資訊以及行程相關的標準輸出/入的stream物件,有一個特別的nextTick函數,可以把函數排入queue的下一個來執行
* Utilities(require('utils')):一些工具的集合,包含處理格式化字串、輸出debug資訊、把ReadableStream轉向到WriteableStream、用mixin方式繼承其他物件等工具函數
* Events(require('events').EventEmitter):用來建立事件機制的物件(EventEmitter),如果希望自訂物件可以支援node.js的事件機制,那需要利用util.inhertis()函數來繼承他
* Buffers:這是node.js用來處理binary資料的物件,I/O的資料都是靠他來傳遞
* Stream:這是把I/O動作抽象化的物件,在node.js中有定義了Readable以及Writeable兩種stream,用來讀取/寫入資料。通常不需要自己產生stream,而是在一些操作中,stream會當作參數傳給callback函數
* Crypto:用來處理各種編碼/加密的物件
* TLS/SSL(require('TLS')):用來處理TLS/SSL加密傳輸的物件
* FileSystem(require('fs')):處理檔案系統及檔案I/O的物件,裡面的I/O方法會提供非同步與同步兩種版本,不過使用非同步效率才好
* Path(require('path')):用來剖析路徑的物件
* Net(require('net')):用來做TCP通訊的物件,可以用他來建立各種伺服器或客戶端
* UDP/Datagram:用來做UDP通訊的物件
* DNS(require('DNS')):用來處理DNS資訊的物件
* HTTP(require('http')):用來處理HTTP通訊協定的物件,可以用他來建立HTTP伺服器,也可以建立HTTP的客戶端
* HTTPS(require('https')):同上,但是支援TLS/SSL的安全傳輸方式
* URL(require('url')):用來協助做URL剖析的物件
* QueryString(require('query')):用來協助做URL中QueryString剖析的物件
* ReadLine(require('readline')):可以用一行一行讀娶得方式來處理input stream的物件
* REPL(require('repl')): Read-Eval-Print-Loop,用來進行逐行執行的物件,可以做出用node.js當作shell來執行的介面
* VM(require('vm')):編譯執行Javascript程式碼的物件,可以讓程式在指定的Context中運行
* Child_Process(require('child_process')):提供幾種方式,來建立新的行程(也就是執行其他程式)
* Assertion(require('assert')):用來協助做測試的物件
* TTY(require('tty')):用來模擬終端機輸入的物件
* ZLIB(require('zlib')):提供zip、gzip壓縮/解壓縮功能的物件,同時也能讓http支援壓縮的資料傳輸
* OS(require('os')):提供作業系統相關資訊的物件

上面只是提供一些「知道從何找起」的簡單資訊,詳細說明還是要看API文件。

node.js的http模組

http的使用非常簡單,他只要三個動作就能運作:

 var http = require('http');//載入http模組
 var server = http.createServer(function(request, response) {//利用request事件handler來處理每個request
 ...
 });
 server.listen(80, 'localhost');//叫伺服器監聽某網址及埠,提供服務

從這裡可以看出幾件事情:用node.js撰寫HTTP伺服器時,並沒有依賴任何伺服器。單純只是在RequestListener處理客戶端送來的request,然後寫入response。要開始運行伺服器,就利用listen函數叫伺服器在某個網址監聽某個port...一切就是那麼簡單。

接下來就用這樣的方式,寫一個簡單的伺服器,叫他印出撰寫HTTP伺服器會用到的幾個相關的物件資訊。首先把html介面做好後,做一個簡單的template(temp.js):

https://github.com/fillano/node-probe/blob/master/temp.js

接下來透過這個template,利用for...in以及簡單的物件型別偵測,來印出傳入物件的詳細資訊,把這個程式取名叫index.js:

https://github.com/fillano/node-probe/blob/master/index.js

接下來寫一個簡單http伺服器(probeHTTP.js)。在這之前,先把前面兩個檔案做成"probe"模組。方法是:在測試http的程式probeHTTP.js目錄中,建立名為node_modules的目錄,然後在其中建立名為probe的目錄,把上面兩個js檔案放進去。接下來,在程式中就可以引用這個模組:

 var probe = require('probe');//這樣就能使用index.js輸出的物件
 var http = require('http');
 var net = require('net');
 var EventEmitter = require('events').EventEmitter;
 var server = http.createServer(function(req, res) {
     res.writeHead(200, {"Content-Type": "text/html","charset":"utf-8"});
     res.write(probe.start());//html檔頭
     res.write(probe.body(new EventEmitter(), 'events.EventEmitter', true));//偵測EventEmitter
     res.write(probe.body(net.createServer(function(c){}), 'net.Server', true));//偵測net.Server
     res.write(probe.body(http, 'http', true));//偵測http
     res.write(probe.body(server, 'http.Server', true));//偵測http.Server
     res.write(probe.body(req, 'http.ServerRequest', true));//偵測http.ServerRequest
     res.write(probe.body(res, 'http.ServerResponse', true));//偵測http.ServerResponse
     res.write(probe.end());//html結束
     res.end();
 });
 server.listen(8443, 'localhost');

https://github.com/fillano/node-probe/blob/master/example/probeHTTP.js

執行以後會出現這樣的網頁:


(網頁太長了,抓圖抓不完,有興趣可以自己跑跑看)

仔細觀察各個物件,會發現似乎有這樣的繼承關係:http.Server繼承net.Server,而net.Server繼承EventEmitter。這裡用到的繼承方式並不是利用典型的prototype繼承,而是在子物件中呼叫父物件constructor。這樣的做法跟utils.inhertis函數也有點像。(另外,http.IncomingMessage看起來就是http.ServerRequest或是他的父物件的constructor,而http.OutgoingMessage則看起來像是http.ServerResponse或是他父物件的constructor。這兩個都會繼承Stream,只是在constructor分別指定了Readable及Writeable屬性,讓他們成為ReadableStream及WriteableStream)

另外需要注意一點,就是撰寫node.js的http伺服器,需要先了解更多http的知識。需要知道這些知識,才能從ServerRequest取出該處理的header,並且在ServerResponse中回覆適當的header。

相關文章


上一篇
node.js伺服器實戰(3) - 基礎知識
下一篇
node.js伺服器實戰(5) - 計畫與範圍
系列文
node.js伺服器實戰33
0
chiounan
iT邦研究生 1 級 ‧ 2011-10-15 21:06:24

沙發
cool

0
shazi
iT邦新手 4 級 ‧ 2013-10-18 11:00:22

一口氣看完這係列文件,想一篇一篇來練習,但才第4篇就卡住了!!

不好意思,想請教一下,我clone下來執行,會出現以下錯誤

<pre class="c" name="code">
http.js:854
    throw new TypeError('first argument must be a string or Buffer');
          ^
TypeError: first argument must be a string or Buffer
    at ServerResponse.OutgoingMessage.write (http.js:854:11)
    at Server.<anonymous> (/home/user/NodeJS/fillano/node-prode/example/probeHTTP.js:7:9)
    at Server.EventEmitter.emit (events.js:98:17)
    at HTTPParser.parser.onIncoming (http.js:2076:12)
    at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:120:23)
    at Socket.socket.ondata (http.js:1966:22)
    at TCP.onread (net.js:525:27)

我的版本是 v0.10.20

再請前輩們給予指導!!~

看更多先前的回應...收起先前的回應...
fillano iT邦超人 1 級 ‧ 2013-10-18 13:24:24 檢舉

嗯,我在v0.10.16可以正確執行,我換到v0.10.20看看。不過說實在,這個code base有點舊了XD

fillano iT邦超人 1 級 ‧ 2013-10-18 13:29:23 檢舉

看了一下你的錯誤,可能要先問一下,你怎麼配置(probe模組放哪裡、probeHTTP.js在哪裡),看起來是在ServerResponse.write(probe.start())這幾行執行時發生的問題。這表示probe.start()回傳不是字串或buffer,我懷疑是null或是undefined。

fillano iT邦超人 1 級 ‧ 2013-10-18 16:09:31 檢舉

fillano提到:
接下來寫一個簡單http伺服器(probeHTTP.js)。在這之前,先把前面兩個檔案做成"probe"模組。方法是:在測試http的程式probeHTTP.js目錄中,建立名為node_modules的目錄,然後在其中建立名為probe的目錄,把上面兩個js檔案放進去。接下來,...(恕刪)

所以請確認:在probeHTTP.js目錄中,是否有node_modules目錄,node_modules中是否有probe目錄,probe目錄中,是否有index.js及temp.js兩個檔案。

fillano iT邦超人 1 級 ‧ 2013-10-19 16:56:23 檢舉

建議有空的話看一看:http://nodejs.org/api/modules.html

由於這只是一個自定的模組,沒有註冊到npm的registry,所以需要自己手動來。通常簡單的作法是,在probeHTTP.js目錄中,建立一個node_modules目錄,在這個目錄中建立probe目錄,把index.js與temp.js這兩個檔案複製到probe目錄中,這樣在probeHTTP.js不用修改也可以執行。

另一個方式,是在probeHTTP.js所在的目錄中,建立一個probe目錄,然後把index.js及temp.js複製到這個目錄,但是要把probeHTTP.js的第一行改成:

<pre class="c" name="code">var probe = require('./probe');

如果要在example目錄中直接執行probeHTTP.js,需要把第一行改成
<pre class="c" name="code">var probe = require('..');

至於為什麼需要這樣做,其實都是因為...modules的搜尋規則的關係。要詳細了解的話,還是建議看一下api文件。

0
shazi
iT邦新手 4 級 ‧ 2013-10-21 10:54:35

首先,真的非常感謝 fillano 前輩的指導!!謝謝
可以執行了!!

現在,我要非常慚愧的回文....
原來我沒有 probe 這個套件!!暈

首先我先 npm 安裝了 probe , 但裡面沒有 index.js與temp.js

後來,我照著 fillano 前輩的指導
在 probeHTTP.js 建立 node_modules 再建立 probe 資料夾,再將這兩個檔案放入
就可以執行了!!

真的很慚愧 .... 有點大意了!!

也很感謝 fillano 的指導!!

我再繼續練習下去!!~ 衝刺

我要留言

立即登入留言