一、實現“Hello World!”及相關API介紹
使用 HTTP 服務器和客戶端必須使用 require('http')
Node.js 中的 HTTP 接口設計主要是爲了支持 傳統的很難使用的多特性的協議,尤其是大量的信息。接口沒有緩衝整個接收和相應,用戶可使用流式數據。
HTPP信息的頭部是像一個對象那樣描述的:
{ 'content-length': '123', 'content-type': 'text/plain', 'connection': 'keep-alive', 'host': 'mysite.com', 'accept': '*/*' }
http.createServer([requestListener])
Returns: <http.Server>
返回一個新的 http.Server 的實例
requestListener 是一個自動添加 'request'
的函數
server.listen([port][, hostname][, backlog][, callback])
開始接受指定的端口號(port)和主機名(hostname)之間的連接。
參數類型:
port:Number
hostname:String
backlog:Number
callback:Function
如果省略了 hostname,當IPV6可用時,服務器將連接任意的IPV6地址(::
),或者是任意的IPV4地址 (0.0.0.0
)
忽略 port 參數或者使用 0 作爲端口號,如果想讓操作系統隨機分配一個端口號,可以在 'listening'
事件之後使用 server.address().port 來重新取回
如果想要監聽 unix 套接字,可以用一個文件名來替換 port 和 hostname
backlog 是連接請求隊列的最大長度,實際的長度將由你的 OS 通過系統調用設置來決定,比如 linux 上的 tcp_max_syn_backlog 和 somaxconn 。這個參數的默認值是511(不是512)
callback 作爲 'listening'
事件的一個監聽器, 是一個異步的回調函數
response.writeHead(statusCode[, statusMessage][, headers])
給請求消息發送一個響應頭
statusCode Number 類型, 是一個三位數的 HTTP 狀態碼 比如:404
statusMessage String 類型,是可選的,主要是提高可讀性
headers Object 類型, 是一個響應頭,第二個參數是可選的
例如:
var body = 'hello world';response.writeHead(200, { 'Content-Length': Buffer.byteLength(body), 'Content-Type': 'text/plain' });
此方法在一個消息上只能調用一次,且必須在 response.end()
之前調用
response.end([data][, encoding][, callback])
這個方法是用來告訴服務器,所有的響應頭和響應主體已經發送完畢,服務器就會知道這個消息已經完成了。每一個響應消息都必須調用 response.end()
encoding
<String>callback
<Function>
如果指定了 data ,就相當於在 response.end(callback) 之前調用了 response.write(data, encoding)
如果指定了 callback ,將在響應流結束之後調用
結合以上內容,我們再來看一下經典的 Node.js 入門案例,在頁面打印“Hello World”
基本的骨架:
//第一步: 引入 http 模塊 var http = require('http'); //第二步:創建一個服務器(requestListener 是一個函數,裏面有2個參數,一個請求消息,一個響應消息) var server = http.createServer(function(req, res){ }); //第三步:服務器監聽本地的82端口 server.listen(8082, '127.0.0.1');
設置消息內容:
var server = http.createServer(function(req, res){ //req -> request,表示請求; res -> response,表示響應 //設置響應頭,第一個參數是3位數的HTTP狀態碼,第二個參數可選,第三個參數是一個對象,裏面可存放合法的MIME類型 //MIME類型:文件類型是純文本,字符集編碼方式是 utf8 res.writeHead(200,{'Content-Type': 'text/plain; charset=utf8'}); //響應消息發送結束,如果指定了data參數,就相當於在 res.end() 之前添加了一個 res.end(data, encoding) 方法 //res.end('Hello World!', 'utf8'); res.end('Hello World!'); });
通過命令行工具掛起服務器,打開瀏覽器,在地址欄輸入: 127.0.0.1:8082
同時,也可以打開控制檯查看請求頭部
值得注意的是,如果省略了 res.end(), 頁面是渲染不出來的,因爲服務器會一直處於等待狀態,沒人告訴它響應消息是否結束了
在 Node.js 三大特點詳解 一文中,我們瞭解到傳統的 HTTP服務器一般建立在 Apache、Nginx、IIS等服務器上,而 Node.js 並不需要,由於 Node.js 提供了 http 模塊,自身就可以構建服務器(從上面的例子可以體會到)。
http 模塊 是通過 C++實現的,性能非常可靠,其中,封裝了一個 http 服務器 和一個簡易的 http 客戶端。http.Server 是一個基於事件的 http 服務器,http.request 則是一個 http 客戶端工具,用於向 http 服務器發起請求
前面提到的 http.createServer( requestListener ) 方法中,requestListener 是一個回調函數,函數有2個參數 request 和 response,request 是 http.IncomingMessage 對象的實例,response 是 http.ServerResponse 對象的實例。下面分別來學習 HTTP 服務器 和 HTTP 客戶端。
二、HTTP 服務器
先回顧下“Hello World!”這個例子
//第一步: 引入 http 模塊 var http = require('http'); //第二步:創建一個服務器(requestListener 是一個函數,裏面有2個參數,一個請求消息,一個響應消息) var server = http.createServer(function(req, res){ //req -> request,表示請求; res -> response,表示響應 //設置響應頭,第一個參數是3位數的HTTP狀態碼,第二個參數可選,第三個參數是一個對象,裏面可存放合法的MIME類型 //MIME類型:文件類型是純文本,字符集編碼方式是 utf8 res.writeHead(200,{'Content-Type': 'text/plain; charset=utf8'}); //響應消息發送結束,如果指定了data參數,就相當於在 res.end() 之前添加了一個 res.end(data, encoding) 方法 //res.end('Hello World!', 'utf8'); res.end('Hello World!'); }); //第三步:服務器監聽本地的82端口 server.listen(8082, '127.0.0.1');
基本的結構是直接創建一個 http 對象,然後創建一個 http 對象的實例 server,併爲其監聽82端口
1、http.Server
http.Server 繼承自 net.Server
而 net.Server是一個 EventEmitter
,主要用來創建一個 TCP或者本地服務器。
事實上,Node.js 中大部分模塊都繼承自 EventEmitter,包括fs、net等模塊,這也是爲什麼說 Node.js 基於事件驅動。http.Server 提供的事件如下:
request
客戶端發出請求時觸發,每一次連接可能會發出很多個請求(假如是持久連接的話)。提供兩個參數request 和 response,是最常用的事件
request
<http.IncomingMessage>response
<http.ServerResponse>
connection
當 TCP 建立連接時,觸發此事件,提供一個參數 socket,是 net.socket 的實例
socket
<net.Socket>
close
當服務器關閉時,觸發此事件。注意不是用戶斷開連接時
除此之外還有 checkContinue,upgrade,clientError 等事件,只有在實現複雜的 HTTP 服務器的時候纔會用到。
在這些事件中,最常用的就是 request 事件,所以,http 提供了一個捷徑:http.createServer( requestListener ),如我們所知,功能就是創建一個 HTTP 服務器並將 requestListener 作爲 request 事件的監聽函數,
2、http.IncomingMessage --> request
http.IncomingMessage 是 HTTP 請求的信息,一般由 http.Server 的 request 事件發送,並作爲第一個參數傳遞,http 請求一般包括 請求頭部 和 請求主體。
其提供了三個事件用於控制請求體傳輸:
data: 當請求體數據到來時,觸發該事件。該事件提供一個參數 chunk,表示接收到的數據。如果該事 件沒有被監聽,那麼請求體將會被拋棄,該事件可能會被調用多次
end:當請求體數據傳輸完成時,該事件被觸發,此後將不會再有數據到來
close:用戶當前請求結束時,該事件被觸發。不同於end,如果用戶強制終止傳輸,也還是調用 close
http.IncomingMessage 的屬性如下:
3、http.ServerResponse ---> response
http.ServerResponse 是返回給客戶端的信息,決定了用戶最終能看到的結果。它是由 http.Server 的 request 事件發送的,作爲第二個參數傳遞,一般簡稱爲 response 或 res
http.ServerResponse 有三個重要的成員函數,用於返回響應頭、響應內容以及結束請求(上面已經介紹過,這裏再複習一次)
response.writeHead(statusCode[, statusMessage][, headers]): 向請求的客戶端發送響應頭
statusCode 是 HTTP狀態碼,常用的比如 200(請求成功),404(未找到)
headers 是一個類似關聯數組的對象,表示響應頭的每個屬性
此函數在一個請求內最多隻能調用一次,如果不調用,則會自動生成一個響應頭
response.write(data, [encoding]): 向請求的客戶端發送響應內容
data 是一個 Buffer 或字符串,表示要發送的內容。如果data 是字符串,那麼需要指定 encoding 來說明它的編碼方式,默認是 utf-8。在 response.end 調用之前,response.write 可以被多次調用
response.end([data], [encoding]): 結束響應
告知客戶端所有發送已經完成,當所有要返回的內容都發送完畢的時候,該函數必須被調用一次。
此函數接受兩個可選參數,意義和 response.write 相同,如果不調用此函數,客戶端將用於處於等待狀態(前面的案例提到過)
三、HTTP 客戶端
http 模塊提供了兩個函數 http.request 和 http.get,功能是作爲客戶端向 HTTP 服務器發起請求
1、http.request(options, callback) 發起 HTTP 請求
接受兩個參數,options表示請求的參數,可以是一個對象或字符串,如果是字符串的話,將自動調用 url.parse()
解析;
callback 是請求的回調函數,需要傳遞一個參數
options 常用的參數如下:
host:請求網站的域名或 IP 地址
port:請求網站的端口號,默認 80
method:請求方法,默認是 GET
path:請求的相對於根的路徑,默認是“/”。QueryString 應該包含在其中,例如:/search?query=byvoid
headers:一個關聯數組對象,爲請求頭的內容
http.request() 返回一個 http.ClientRequest
的實例
示例代碼:
var http=require("http"); var options={ hostname:"www.12306.cn", port:80 }; var req=http.request(options,function(res){ res.setEncoding("utf-8"); res.on("data",function(chunk){ console.log(chunk.toString()) }); console.log(res.statusCode); }); req.on("error",function(err){ console.log(err.message); }); req.end();
我們運行這段代碼,就可以在控制檯中看到 12036 的 HTML 代碼了
2、http.get(options, callback)
由於大多數的 GET 請求都沒有主體,所以 Node.js 提供了這個簡便的方法用於處理 GET 請求。
此方法是 http.request 的簡化版,唯一的區別在於 http.get 自動將請求方法設爲了 GET 請求,同時不需要手動調用 req.end()
var http = require('http'); http.get({host: 'www.12306.cn'}, function(res) { res.setEncoding('utf8'); res.on('data', function (data) { console.log(data); }); });
運行這段代碼,一樣可以在控制檯得到 12036 的 HTML 代碼
3、http.ClientRequest
http.ClientRequest 是由 http.request 或 http.get 返回產生的對象,表示一個已經產生而且正在進行中的 HTTP 請求。
它提供了一個 response 事件,即 http.request 或 http.get,第二個參數指定的回調函數的綁定對象。我們可以顯示地綁定這個事件的監聽函數:
var http = require('http'); var req = http.get({host: 'www.12306.cn'}); req.on('response', function(res) { res.setEncoding('utf8'); res.on('data', function (data) { console.log(data); }); });
運行這段代碼,一樣可以在控制檯得到 12036 的 HTML 代碼
http.ClientRequest 像 http.ServerResponse 一樣也提供了 write 和 end 函數,用於向服務器發送請求體,通常用於 POST、PUT 等操作。所有寫結束以後必須調用 end 函數以通知服務器,否則請求無效。
http.ClientRequest 還提供了以下函數。
request.abort():終止正在發送的請求。
request.setTimeout(timeout, [callback]):設置請求超時時間,timeout 爲毫秒數。當請求超時以後,callback 將會被調用。
4、http.ClientResponse
http.ClientResponse 與 http.ServerRequest 相似,提供了三個事件 data、end和 close,分別在數據到達、傳輸結束和連接結束時觸發,其中 data 事件傳遞一個參數chunk,表示接收到的數據。
http.ClientResponse 也提供了一些屬性,用於表示請求的結果狀態
http.ClientResponse 還提供了以下幾個特殊的函數。
response.setEncoding([encoding]):設置默認的編碼,當 data 事件被觸發時,數據將會以 encoding 編碼。默認值是 null,即不編碼,以 Buffer 的形式存儲。常用編碼爲 utf8。
response.pause():暫停接收數據和發送事件,方便實現下載功能。
response.resume():從暫停的狀態中恢復。