1. 網絡編程
1.1. Net 模塊
Node 中的 net 模塊是對網絡通信的一種異步包裝, 它為服務端和客戶端提供數據流函數。
1.1.1. net.Server 類
該類用于生成 TCP 連接或本地服務器, 它的實例是一個 EventEmitter
,與其相關的事件:
'close'
當服務器關閉的被觸發,注意如果某個連接存在,事件不會被觸發。'connection'
當客戶端建立新的連接觸發該事件,回調函數參數是<net.Socket>
實例。-
'error'
當異常發生時觸發。這里注意處理 net.Server 對象的'close'
事件,因為除非手動調用server.close()
否則不會觸發'close'
事件。server.on('error', (e) => { if (e.code == 'EADDRINUSE') { console.log('Address in use, retrying...'); setTimeout(() => { server.close(); server.listen(PORT, HOST); }, 1000); } });
'listening'
在調用 server.listen 后, 服務器被綁定后觸發。
該類的重要方法:
-
server.listen([port][,hostname][,backlog][,callback])
服務端在指定主機和端口上接受連接。此方法會觸發
'listening'
事件, 方法最后一個參數callback
是該事件的偵聽器。 -
server.close([callback])
停止接受新建的連接但是保持已建立的連接,這個方法是異步的,當所有的連接斷開后服務器會觸發
'close'
事件,該方法的參數是這個事件的偵聽器。
1.1.2. net.Socket 類
該類是 TCP 連接或本地服務器端口的抽象。net.Socket
實例是一個雙向數據流接口,它可以由用戶使用客戶端命令(connect()
)創建,也可以由服務端的 Node 通過用戶傳遞的 'connection'
事件創建。它同樣也是一個 EventEmitter
,與其相關的事件:
-
'close'
端口被完全關閉后觸發一次。如果是傳輸錯誤該事件返回的參數had_error
會置為true
。 -
'connect'
當一個端口連接建立完成時觸發。 -
'data'
當數據塊發生流動時觸發。數據可以是Buffer
或String
。 -
'drain'
當寫入緩存為空時觸發。 -
'end'
當另外一端的端口發出 FIN 數據包時觸發。 -
'error'
當異常發生時觸發。'close'
事件會在該事件觸發后立即觸發。 -
'lookup'
在解析主機后并在發出連接前觸發。主要用于 DNS。 -
'timeout'
端口無動作一段時間后觸發。
該類的重要方法:
-
socket.connect(options[,connectListener])
在指定套接字上打開連接。該方法會在后面的工廠函數
net.createConnection()
中調用。 -
socket.end([data][,encoding])
半關閉套接字, 它會發送一個 FIN 數據包。
-
socket.write(data,[,encoding][,callback])
在套接字上發送數據,當所有數據發送結束將會執行回調函數。
1.1.3. 工廠函數
-
net.connect(options[,connectListener])
返回一個
net.Socket
實例,并通過socket.connect
方法打開連接。回調函數是'connect'
事件的偵聽器。 -
net.createConnection(options[,connectListener])
返回一個新的
net.Socket
實例,同時建立連接。 -
net.createServer()
建立一個新服務器,返回
net.Server
實例。 該函數的回調函數是'connection'
事件偵聽器。
1.2. TCP 服務
1.2.1. 創建 TCP 服務器端
const net = require('net');
const server = net.createServer((socket) => {
socket.on('end', function() {
console.log('client disconnected');
});
socket.write('Welcome to the Earth!\n');
socket.pipe(socket);
});
server.on('error', (err) => {
throw err;
});
server.listen({port: 8124}, () => {
console.log('opened server on', server.address());
});
1.2.2. 創建 TCP 客戶端
const net = require('net');
const client = net.connect({port: 8124}, () => {
console.log('client connected');
client.write('hello world!\r\n');
});
client.on('data', (data) => {
console.log(data.toString());
client.end();
});
client.on('end', () => {
console.log('disconnected from server');
});
client.on('error', (err) => {
throw err;
});
1.3. 構建 HTTP 服務
無論是 HTTP 請求報文還是 HTTP 響應報文,報文內容都包含兩個部分: 報文頭和報文體。
1.3.1. http 模塊
TCP 服務以 connection 為單位進行服務(可以看作是 'connection'
事件),HTTP 服務以 request 為單位進行服務(同理可以看作是 'request'
事件)。http 模塊即是將 connection 到 request 的過程進行了封裝。
以服務器端為例, http 模塊將連接所用套接字的讀寫抽象為 IncomingMessage 和 ServerResponse 對象。 在請求響應過程中, http 模塊將網絡連接讀來的數據,通過調用二進制模塊 http_parser 進行解析,在解析完請求報文的報頭后,觸發 'request'
事件,調用業務邏輯,然后再通過對連接的寫操作,將響應返回到客戶端。
1.3.1.1. HTTP 請求
請求報文將會通過 http_parser 進行解析,并抽象為 IncomingMessage 對象:
-
mssage.method
字符形式的請求方法,只讀。 -
message.url
請求 URL 字符串。 -
message.httpVersion
http 版本號。 -
message.headers
頭部變量和數值所對應的鍵/值對。
IcomingMessage 對象是一個只讀流接口(Readable Stream),如果業務邏輯需要讀取報文體中的數據,則要在這個數據流結束后才能進行。
1.3.1.2. HTTP 響應
HTTP 響應封裝了對底層連接的寫操作,可以將其看成一個可寫的流對象(Writable Stream)。http 模塊將 HTTP 響應抽象為 ServerResponse 對象。
ServerResponse 對象對報文頭的操作:
-
response.setHeader(name, value)
設置響應報文頭,可以多次設置。 -
response.writeHeader(statusCode[,statusMessage][,headers])
發送響應報文頭。
ServerResponse 對象對報文體的操作:
-
response.write(chunk,[,encoding][,callback])
發送響應報文體的區塊,注意一旦開始了數據發送,報文頭的發送將不再生效。 -
response.end([data][,encoding][,callback])
每一個響應必須調用該方法,調用該方法意味著所有響應報文都已發送,服務器認為這次事務結束。
1.3.1.3. HTTP 服務的事件
對于 Node 來說,可以將 HTTP 服務抽象為 http.Server
對象,該對象繼承自 net.Server
對象,所以 HTTP 服務的實例是一個 EventEmitter
,與其相關的事件有:
-
'connection'
當一個新的 TCP 數據流被建立,該事件觸發并返回一個net.Socket
實例。 -
'request'
建立 TCP 連接后,當請求數據到達服務器端, http 模塊底層解析出 HTTP 請求報文頭后,將會觸發該事件。