手把手教你用 Node 实现 HTTP 协议(三)

手把手教你用 Node 实现 HTTP 协议(三)

上一章介绍了如何解析 HTTP 请求报文,这一章咱们来说解如何进行报文的收发和 TCP 链接的创建。git

TCP 是一条全双工通讯通道,咱们能够经过使用 Node 的 net 模块来建立一个 TCP 进程,监听来自客户端的请求,使用方法以下:github

net.createServer((socket) => {
  socket.on('data', (data: Buffer) => {
    // ... 处理接收的客户端信息
  });

  socket.on('error', error => {
    // ... 处理出错信息
  });
});

data 事件中所接收到的数据属于字节流数据(Buffer 对象),咱们须要使用 Buffer 对象自带的 toString 方法来进行对字节流的解析,将其转化为 utf-8 格式的字符。socket

而后将转化后的字符串交由咱们的 HttpParser 处理,而后将序列化的请求对象挂载在 http.createServer((req, res) => {}) 中的 req 上,同时能够经过 res.end(message) 进行报文响应。函数

咱们将上述步骤解析一下可得知,咱们还须要两个类,一个用来承载 Request 信息,一个用于处理响应结果的 Response,那咱们如今来新建这两个类:this

// 用于处理请求信息
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;

最后咱们在咱们的事件监听中,加入这两个处理对象:code

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 类的实现以下:orm

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 服务端了,而且能够处理请求信息,将请求信息如数返回给客户端。server

至此,一个 HTTP 协议就被咱们实现了!对象

源码地址,欢迎 Star进程

原文地址,欢迎 Star

相关文章
相关标签/搜索