从 0 构建自己的 HTTP 服务器

了解HTTP协议与TCP协议,并重 0 开始构建一个服务器。

http 与 net 库的对比

Node.js 提供了两个用于处理 HTTP 请求的库:http 和 net。这两个库都提供了类似的功能,但也有一些关键的区别。

http 库

http 库是 Node.js 内置的 HTTP 库。它提供了一个简单易用的 API,用于创建和处理 HTTP 服务器和客户端。http 库的 API 基于事件驱动,这使得它非常适合处理大量并发请求。

net 库

net 库是 Node.js 提供的另一个 HTTP 库。它提供了一个更底层的 API,用于创建和处理 HTTP 连接。net 库的 API 更灵活,可以用于创建自定义 HTTP 服务器和客户端。

区别

以下是 http 库和 net 库的一些关键区别:

特性 http 库 net 库
易用性 易于使用 更复杂
功能 提供基本的 HTTP 功能 提供更丰富的 HTTP 功能
性能 通常比 net 库更快 通常比 http 库更慢
适用场景 适用于大多数 HTTP 应用程序 适用于需要自定义 HTTP 服务器或客户端的应用程序

除了上述区别之外,http 库和 net 库还有一些其他的区别:

  • http 库使用事件驱动,而 net 库使用回调函数。 这意味着 http 库的 API 更简洁,而 net 库的 API 更灵活。
  • http 库提供了一个 Server 类,用于创建 HTTP 服务器。 net 库没有提供专门的 HTTP 服务器类,但可以使用 Server 类来创建 HTTP 服务器。
  • http 库提供了一个 Client 类,用于创建 HTTP 客户端。 net 库没有提供专门的 HTTP 客户端类,但可以使用 Socket 类来创建 HTTP 客户端。

使用 net 实现一个 TCP 服务器

使用 net 模块创建一个基础服务器。

js 复制代码
const net = require('net')


// Uncomment this to pass the first stage
const server = net.createServer(socket => {
   socket.on('close', () => {
     socket.end()
     server.close()
   })

  socket.on('data', (request) => {
    const htmlStr = `
      <h1>hello world!</h1>
    `
    socket.write('HTTP/1.1 200 OK \r\n\r\n ' + htmlStr, 'utf8')
    socket.end()
  })
})

server.listen(4221, 'localhost')

它监听4221端口,socket.on(),中回调函数 request 是 http 请求体。他的报文通常如下:

bash 复制代码
POST / HTTP/1.1
Host: localhost:4221
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.207.132.170 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*\/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
BODY

当时 GET 请求时,POST 替换为 GET,最后一行的 body 去掉。服务器通常要根据请求内容,做出不同的响应。因此需要解析请求体。

下面代码是一个基本的HTTP请求解析器:

js 复制代码
// 解析请求
function parseRequest(requestData) {
  // 使用'\r\n'(回车和换行)作为分隔符
  const [request, ...requestHeaders] = requestData.split('\r\n')
  let body = null
  const n = requestHeaders.length

  if (requestHeaders[n - 2] == '') {
    body = requestHeaders.pop()
    requestHeaders.pop()
  }
  const [method, path, version] = request.split(' ')
  const headers = {}
  
  requestHeaders.forEach(header => {
    if (!header) return
    const [key, value] = header.split(': ')
    headers[key] = value
  })
  return { method, path, version, headers, body }
}

// 请求
  socket.on('data', data => {
    const requestData = data.toString()
    const request = parseRequest(requestData)
    console.log('requestData: ', requestData)
    console.log('request: ', request)
  })
  • 检查requestHeaders数组中倒数第二个元素(即requestHeaders[n - 2])是否为空字符串。这是一个检查,用于确定请求是否是带有正文的POST请求。
  • 函数返回一个包含解析后的请求信息的对象,包括method(方法)、path(路径)、version(版本)、headers(标头)和body(如果是POST请求则包括正文)。

我们直接使用 TCP 协议建立连接虽然可以实现 HTTP 协议通讯,但是我们需要自己去解析 HTTP 请求内容,并按照 HTTP 协议的规范组织响应内容。这显然很麻烦。

但是Node.js 提供了更加简单的创建 HTTP 服务的模块 ------ http 模块。

http 库实现

使用Node.js创建简单HTTP服务器的示例

js 复制代码
const http = require('http')

const server = http.createServer((req, res) => {
  const { pathname } = new URL(`http://${req.headers.host}${req.url}`)
  if (pathname === '/') {
    res.writeHead(200, { 'Content-Type': 'text/html' })
    res.end('<h1>Hello world</h1>')
  } else {
    res.writeHead(404, { 'Content-Type': 'text/html' })
    res.end('<h1>Not Found</h1>')
  }
})

server.on('clientError', (err, socket) => {
  socket.end('HTTP/1.1 400 Bad Request\r\n\r\n')
})

server.listen(8080, () => {
  console.log('opened server on', server.address())
})

使用http.createServer()方法创建了一个HTTP服务器实例,并传入一个回调函数作为参数。这个回调函数会在每次有HTTP请求到达服务器时执行。

总结

http 模块比 net 模块用起来更简单,不需要自己解析 HTTP 请求的内容,或者自己拼接响应的内容,直接使用回调函数中的 req、res 对象来处理请求或响应即可。

以上示例代码都可在此找到:Github

参考

相关推荐
垣宇9 小时前
Vite 和 Webpack 的区别和选择
前端·webpack·node.js
爱吃南瓜的北瓜9 小时前
npm install 卡在“sill idealTree buildDeps“
前端·npm·node.js
翻滚吧键盘9 小时前
npm使用了代理,但是代理软件已经关闭导致创建失败
前端·npm·node.js
浪九天10 小时前
node.js的版本管理
node.js
浪九天12 小时前
node.js的常用指令
node.js
浪九天14 小时前
Vue 不同大版本与 Node.js 版本匹配的详细参数
前端·vue.js·node.js
小纯洁w1 天前
Webpack 的 require.context 和 Vite 的 import.meta.glob 的详细介绍和使用
前端·webpack·node.js
熬夜不洗澡1 天前
Node.js中不支持require和import两种导入模块的混用
node.js
bubusa~>_<1 天前
解决npm install 出现error,比如:ERR_SSL_CIPHER_OPERATION_FAILED
前端·npm·node.js
天下皆白_唯我独黑1 天前
npm 安装扩展遇到证书失效解决方案
前端·npm·node.js