基于 Boost 的静态 HTTP 服务器

基于 Boost 的静态 HTTP 服务器

本文介绍了如何借助C++Boost库设计并开发一个简单 静态HTTP服务器

http_server


项目概览

定位:理解 Asio 异步模型与服务器底层实现,而非业务层开发

指标 数据
代码规模 3300 行
测试用例 140 个gTest单元测试

为什么使用 Boost 作为依赖?

Boost库是一组高质量的、经过同行评审的C++库集合。 这些库旨在扩展C++标准库的功能,提供更多的工具和功能,使开发者能够更高效地编写复杂的程序。

Boost就是一个庞大的C++工具库,其Asio与Beast组件刚好符合开发一个简单的静态HTTP服务器需求。

技术选型总览

选型 候选方案 选择 理由
语言 C++ / Go / Nodejs C++17 熟悉,学习底层实现,而非业务抽象
网络库 手写 epoll / libuv / Boost.Asio Boost.Asio 成熟异步模型,抽象层级合适,跨平台
HTTP 协议库 手写解析 / Boost.Beast Boost.Beast parser / serializer 抽象,避免协议细节重复造轮子
交付方式 自研 / nginx 二次开发 自研 学习效率优先

为什么不使用Go语言,Dragon、Nodejs框架等建立在更成熟生态之上的技术栈?

使用Go、Nodejs提供的接口就能够简易开发一个高并发的异步、协程服务器 为什么还要使用C++编写一个呢?

Go、Dragon、Nodejs等语言/框架提供的接口进行了高度的抽象, 抽象层次太高,隐藏了大量的实现细节,让用户只需要专注于业务层次, 该项目的定位是建立对底层实现与业务层应用间的理解,而非业务层面的开发

为什么不使用现成的Nginx为首的服务器软件?

为什么不使用现成的服务?

Nginx 这一静态服务器标杆那样,从底层进行编写能够减少包装极大程度的压榨性能, 并使用sendfile()、tcp_nopush()、gzip_static()、CPU核心绑定、系统级的文件句柄配置 等技巧, 进行文件零拷贝、减少内核调用与I/O开销、异步非阻塞事件驱动开销等优化, 以达到最极致的rps优化

这些内核级技巧正是 nginx 的性能壁垒:同为 i5-1235U 平台实测(qps/MHz 归一化),本服务器约为 nginx 的 67~73%,差距来源即为上述尚未实现的优化(sendfile 大文件零拷贝、CPU 绑定等列入 TODO 按需推进)

除非阅读源码,相比于自己进行开发,虽然能理解底层的实现与优化,但是学习效率太低

为什么不做libc底层包装?

既然要使用C++从底层开发,为什么不使用操作系统提供的libc库?

对于该静态HTTP服务器,从更原子化的方式进行项目编写其时间成本对个人而言是极为庞大的, 而且大多数的成本集中在底层封装上,虽然能够从操作系统内核层次理解上层服务器设计,

对于服务器而言, UNIX/Linux 提供了一切皆文件 的抽象, 让网络流的收发被简化为文件的接收、读取、处理、发送 操作, 对于网络报文对象而言,则是字符串匹配、字节流解析操作, 在原语层面提供了良好的抽象方式,

Boost提供的包装是更易用的接口封装,减少了底层交互的过多暴露, 只需要理解底层功能核心libc接口的原理,使用上层的简易包装进行实现,能节省开发时间, 与理解接口以及服务器设计这一目标是相符的

为什么不手写HTTP报文解析?

为什么使用 Boost.Beast ?

与原计划于预期目标相悖:学习静态HTTP服务器设计与并发功能实现


Beast 提供的接口抽象

排除上述备选方案后,Boost.Asio/Beast 提供了服务器开发的完整骨架。 Beast 的价值在于把 HTTP/1.1 协议中最容易出错的部分收敛为以下三个抽象:

tcp_stream ------ 套接字与超时的合成

cpp 复制代码
// /includes/server.hpp
beast::tcp_stream stream_;                              // TCP 连接流
beast::flat_buffer buffer_;                             // 平坦的缓存区域
boost::optional<http::request_parser<http::string_body>> parser_;   // 请求分析器

beast::tcp_stream 将 TCP 套接字与超时控制合为一体, expires_after() 为每次异步读写设置超时,读超时 / 空闲超时统一为流上的一个调用, 避免了为每个连接自建定时器的繁琐

request_parser ------ 报文边界的处理者

HTTP/1.1 报文的解析繁琐点在于:Content-Length / chunked 编码的 body 边界、 keep-alive 下多次请求的帧切分、请求行与头部的字段校验。 http::request_parser 配合 flat_buffer 异步消费字节流:

  • 内部状态机管理解析进度,body_limit() 超限时 async_read 直接返回 413
  • 每次读前 emplace 新实例,天然隔离前后请求的解析状态
  • 解析完成后经 parser_->get() 取出请求报文,交由业务层处理

message_generator ------ 业务与连接的边界

http::message_generator 是 Beast 对"响应"的抽象, 既可以是立即构造的普通响应,也可以是尚未完成的异步响应

业务层接口据此定义为:

cpp 复制代码
// includes/router.hpp
using Handler = std::function<http::message_generator(const http::request<http::string_body>&)>;

业务模块只需要处理"一个请求对象"并返回"一个响应生成器", 连接生命周期与写回时机均由网络层负责------这是本服务器 request_handler 解耦设计的基础


项目报告

功能模块

功能 模块 说明
静态文件服务 static_file_service GET/HEAD,LRU 内容缓存 + 路径解析缓存
条件请求 static_file_service / utils ETag / Last-Modified,304 空 body
Range 断点续传 static_file_service 206/416,shared_slice_body 零拷贝切片
gzip 内容协商 static_file_service / utils 预压缩缓存,Vary 头,Range 不压缩
动态 API dynamic_api_service 端点注册表,405 + Allow,OPTIONS
路由分发 router 精确匹配 + 前缀匹配
限流 / 超时 / body 限制 server 503 + Retry-After、expires_after、413
配置 / 日志 / 缓存 config / logger / cache 三级优先级、六级别日志轮转、泛型 LRU
优雅关闭 graceful_shutdown 信号捕获 -> 会话排空 -> 强制超时
路径穿越防护 utils 轻量检查 + weakly_canonical 双重校验

三层架构

graph TD subgraph 网络层 listener[listener] session[session] end subgraph 处理层 request_handler[request_handler] router[router] end subgraph 服务层 static_service[static_file_service] dynamic_api[dynamic_api_service] cache[cache] end listener-->|accept|session session-->|async_read->请求报文|request_handler request_handler-->|路由匹配|router router-->|分发|static_service router-->|分发|dynamic_api static_service-.->|读缓存|cache static_service-->|message_generator 响应|session dynamic_api-->|message_generator 响应|session

项目压测

平台:i5-1235U(10 核 12 线程)/ Fedora 44(kernel 7.1.6)

目标文件 /app/index.html(9KB)

服务端 8 线程、关访问日志,wrk 30s

配置 QPS
t6-c100 ~153k
t6-c400 ~138k
t6-c1000 ~110k

nginx 对比(qps/MHz 归一化,消除锁频平台跨轮次偏差):当前平台同等条件下约为 nginx 的 67~73% 完整 18 组压测矩阵(t4/t6/t8 * c100~c1000)见项目 /docs/stress-test/

工程化

  • CI:GitHub Actions,ubuntu-24.04 + g++,Debug/Release 双构建 + ccache + ctest
  • Docker:多阶段构建(编译->运行) + 非 root 用户
  • 公网验证:cloudflared 临时隧道暴露 HTTPS URL 实测
  • SELinux:容器标签适配(开发平台为 Fedora 44)

本文为项目总览,聚焦技术选型与最终成果;实现细节将在以下文章中展开:

  • 框架设计:request_handler 解耦、三层架构与每连接 strand、缓存设计演进
  • HTTP 协议细节:304 条件请求、Range 断点续传、gzip 协商、405/OPTIONS 语义
  • 性能方法论:压测方案设计、优化链条、最终成绩
  • 项目工程化:基于 gtest 的测试体系、路径穿越防护、CI / Docker / cloudflared提供的网络边缘隧道服务进行公网验证

END

相关推荐
iLogIng1 小时前
基于 Boost 的 HTTP 服务器:框架设计
http
DsirNg1 天前
一文讲清网络协议:从打开网页到理解 HTTP、HTTPS 与 TLS
http·https·dns·网络基础·入门教程·tls·web 安全
奈斯先生Vector1 天前
2026 AIGC API 网关韧性评测:为什么 HTTP 200 不等于有效交付
网络协议·http·aigc
谏书稀1 天前
C++/Python 混合编程(CPython)中 GIL饥饿 导致的 HTTP 服务无响应问题与解决方案
c++·python·http
全麦面包 time展天2 天前
瀚海拾贝(一)HTTP协议/IIS 原理及ASP.NET运行机制浅析【图解】
网络协议·http·asp.net
godwaskillde2 天前
Java 11 HttpClient如何配置HTTP代理?连接测试与异常处理示例
java·开发语言·http
小白说大模型2 天前
从0开始学计算机网络:HTTP 协议的进化史
人工智能·网络协议·计算机网络·http
无糖可乐没有灵魂2 天前
Security ❀ Https TLS抓包操作与解密配置
网络协议·http·https
就叫年华吧丶2 天前
Puppeteer 运行一段时间后报 `net::ERR_BLOCKED_BY_CLIENT`:一次 Chromium HTTPS 自动升级问题的完整排查
网络协议·http·https·puppeteer·chromium