手把手教你用 Node 實現 HTTP 協議(三)
阿新 • • 發佈:2019-12-31
手把手教你用 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 協議就被我們實現了!