Node.js 學習(二): 網絡編程

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' 當數據塊發生流動時觸發。數據可以是 BufferString
  • '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 模塊即是將 connectionrequest 的過程進行了封裝。

以服務器端為例, 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 請求報文頭后,將會觸發該事件。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容