1. 程式人生 > 程式設計 >手把手教你用 Node 實現 HTTP 協議(三)

手把手教你用 Node 實現 HTTP 協議(三)

手把手教你用 Node 實現 HTTP 協議(三)

上一章介紹瞭如何解析 HTTP 請求報文,這一章我們來講解如何進行報文的收發和 TCP 連線的建立。

TCP 是一條全雙工通訊通道,我們可以通過使用 Node 的 net 模組來建立一個 TCP 程式,監聽來自客戶端的請求,使用方法如下:

net.createServer((socket) => {
  socket.on('data',(data: Buffer) => {
    // ... 處理接收的客戶端資訊
  });

  socket.on('error',error => {
    // ... 處理出錯資訊
  });
});
複製程式碼

data 事件中所接收到的資料屬於位元組流資料(Buffer 物件),我們需要使用 Buffer 物件自帶的 toString 方法來進行對位元組流的解析,將其轉化為 utf-8 格式的字元。

然後將轉化後的字串交由我們的 HttpParser 處理,然後將序列化的請求物件掛載在 http.createServer((req,res) => {}) 中的 req 上,同時可以通過 res.end(message) 進行報文響應。

我們將上述步驟解析一下可得知,我們還需要兩個類,一個用來承載 Request 資訊,一個用於處理響應結果的 Response,那我們現在來新建這兩個類:

// 用於處理請求資訊
import HttpParser,{ HttpMessage } from "./HttpParser"; class IncomingMessage { private httpParser: HttpParser; public httpMessage: HttpMessage; constructor(message: string) { this.httpParser = new HttpParser(message); this.httpMessage = this.httpParser.httpMessage; } } export default
IncomingMessage; 複製程式碼
// 用於響應處理結果
import * as net from 'net';
// ResponseFormatter 就是反序列化 JSON 資料的類,可從原始碼倉庫檢視
import ResponseFormatter from './ResponseFormatter';

class ServerResponse {
  private socket: net.Socket;
  private resFormatter: ResponseFormatter;
  
  constructor(socket: net.Socket) {
    this.socket = socket;
    this.resFormatter = new ResponseFormatter();
  }

  public setHeader(key: string,val: string) {
    this.resFormatter.setHeader(key,val);
  }
  
  public end(status: number,body: string) {
    const resFormatter = this.resFormatter;
    resFormatter.setStatus(status);
    resFormatter.setBody(body);
    // 下面三步就是向客戶端傳送 TCP 位元組流資料
    this.socket.write(resFormatter.format());
    this.socket.pipe(this.socket);
    this.socket.end();
  }
}

export default ServerResponse;
複製程式碼

最後我們在我們的事件監聽中,加入這兩個處理物件:

socket.on('data',(data: Buffer) => {
  // ... 處理接收的客戶端資訊
  const message = data.toString('utf-8'); // 解碼位元組流資料
  this.request = new IncomingMessage(message); // 封裝 request 物件
  this.response = new ServerResponse(socket); // 封裝 response 物件
  this.handler(this.request,this.response); // 將兩個物件作為引數傳入回撥函式
});
複製程式碼

現在我們只需要正確的將回撥函式新增到 HTTP 物件中即可,我們最後的 HTTP 類的實現如下:

import * as net from 'net';
import * as EventEmitter from 'events';
import IncomingMessage from "./IncomingMessage";
import ServerResponse from "./ServerResponse";

type Handler = (request: IncomingMessage,response: ServerResponse) => void;

class HTTP extends EventEmitter{
  handler: Handler;
  request: IncomingMessage;
  response: ServerResponse;
  server: net.Server;
  socket: net.Socket;

  constructor(handler: Handler) {
    super();
    this.handler = handler;
    this.createServer();
  }

  private createServer(): void {
    this.server = net.createServer((socket) => {
      socket.on('data',(data: Buffer) => {
        const message = data.toString('utf-8');
        this.request = new IncomingMessage(message);
        this.response = new ServerResponse(socket)
        this.handler(this.request,this.response);
      });

      socket.on('error',error => {
        this.emit('error',error)
      });
    });
  }

  public listen(port: number,cb: any = () => { }): void {
    this.server.listen(port,cb);
    this.server.on('error',error => this.emit('error',error));
  }
}

const createServer = (handler: Handler) => {
  return new HTTP(handler)
}

export default {
  createServer
}
複製程式碼

完成最後的 HTTP 類後,我們就可以通過第一章的方法去建立一個 HTTP 服務端了,並且可以處理請求資訊,將請求資訊如數返回給客戶端。

至此,一個 HTTP 協議就被我們實現了!

原始碼地址,歡迎 Star

原文地址,歡迎 Star