C++ 轻量Web服务框架库 libuvcpp 支持HTTP/1.1、HTTP/2、HTTP/3(计划支持)

发布时间:2026/9/24 17:33:27
C++ 轻量Web服务框架库 libuvcpp 支持HTTP/1.1、HTTP/2、HTTP/3(计划支持) 用 C 写 Web 服务凭什么不行——libuvcpp 1.3.0 发布与 HTTP/2、HTTP/3 路线图项目地址libuvcpp项目ReleaseLatest版本1.3.0 作者zhuweiye 许可证MIT一、一个被忽视的事实C 没有好用的 Web 框架今天的 Web 服务世界基本是微服务和插件式服务的天下。你随手打开一个后端项目大概率是 Java 的 Spring Boot、Python 的 FastAPI/Django、Node.js 的 Express/NestJS或者 Go 的 Gin。它们各有各的生态优势但你会发现一个共同点——上手成本低开箱即用。那 C 呢C 在 Web 服务领域的处境一直很尴尬。业内普遍认为 C 只适合写嵌入式、客户端产品、游戏引擎、插件系统不适合开发 Web 应用几乎成了一种默认认知。但事实是C 不是不能写 Web 服务而是一直没有一个轻量、好用、容易上手的 Web 服务框架。这才是问题的根源。二、libuvcpp 是什么2.1 站在 libuv 的肩膀上libuv 是 Node.js 的底层异步事件引擎——跨平台、高性能、经过大规模生产验证。它几乎是 C/C 世界最可靠的异步 I/O 基础设施。但 libuv 是 C 风格的 API回调是裸函数指针handle 要手动管理生命周期缓冲区要自己 malloc/free。让一个习惯了 C 的开发者去用它体验并不好。而让 C 程序员去适应 Node.js 那种一切皆回调 JS 语法的开发范式同样不是一个好的选择——语言割裂、调试困难、性能也不好控制。libuvcpp 就是这两者之间的中间地带。它在 libuv 的事件循环、句柄和请求之上提供了一层薄而符合 C 习惯的封装保持 libuv 的性能不变同时引入 RAII 资源管理、std::function回调以及面向 TCP、UDP、HTTP、WebSocket 的高级客户端/服务端抽象。2.2 更大的目标为 C Web 生态打地基libuvcpp 的野心不止于此。它的真正目标是填补 C 在 Web 服务框架领域的空白为 C 生态提供一个轻量、好用、容易上手的 Web 服务框架基础。在这个基础之上进一步构建面向应用层的 net / web / webapp 框架——让 C 开发者也能像写 Spring Boot 或 Express 那样快速搭起一个符合现代 Web 应用标准的服务端程序。而符合现代 Web 应用标准意味着三件事缺一不可安全、性能、易用。三、架构分层libuvcpp 按层次模块组织从底层工具到应用框架逐层向上。到 1.3.0HTTP/2 已经成为一个独立的层级应用层 ┌──────────────┐ │ web (HTTP/WS) │ ← uvcpp_http_client/server, uvcpp_ws_client/server ├──────────────┤ │ http2 │ ← h2 会话/连接层、nghttp2 胶水UVCPP_ENABLE_NGHTTP2ON ├──────────────┤ │ ssl (TLS) │ ← uvcpp_ssl, uvcpp_ssl_context (OpenSSL 封装) ├──────────────┤ │ net │ ← uvcpp_tcp_client/server, uvcpp_udp_client/server ├──────────────┤ │ handle req │ ← uvcpp_loop, uvcpp_tcp, uvcpp_timer, uvcpp_write, ... ├──────────────┤ │ uvcpp core │ ← uvcpp_buf, uvcpp_thread, uvcpp_alloc, ... └──────────────┘再往上还有一层webapp—— 建在 web 模块之上的应用框架写业务 handler 就行不用拼报文。四、核心设计类封装 std::function回调4.1 把 handle 变成类libuvcpp 将 libuv 的核心句柄和请求封装为对应的 C 类命名统一为uvcpp_前缀的下划线风格类别类事件循环uvcpp_loop流式句柄uvcpp_tcp,uvcpp_pipe,uvcpp_udp,uvcpp_tty定时器与钩子uvcpp_timer,uvcpp_idle,uvcpp_prepare,uvcpp_check信号与进程uvcpp_signal,uvcpp_process文件系统uvcpp_fs,uvcpp_fs_event,uvcpp_fs_poll轮询uvcpp_poll,uvcpp_async请求uvcpp_write,uvcpp_connect,uvcpp_shutdown,uvcpp_work,uvcpp_getaddrinfo,uvcpp_getnameinfo,uvcpp_udp_send,uvcpp_random每个句柄类都遵循 RAII析构时自动关闭句柄的生命周期不再需要手动干预。这看似是小改动但在实际项目中大幅减少了忘记uv_close导致句柄泄漏这类低级错误。值得单独一提的是1.3.0 之前uvcpp_handle的拷贝构造、拷贝赋值与clone()被从公开接口删除——它们的实现其实是memcpy一个活着的uv_handle_t连着 loop 指针与邻居指针结果是双重释放或者把活句柄从自己的循环队列里摘掉。这是典型的用起来像 C 但实际是 C 语义的坑现在被彻底封死了。4.2std::function风格的异步回调libuv 的 C 回调需要通过req-data裸指针来传递上下文libuvcpp 直接使用std::function/ lambda可以自由捕获外部变量类型安全无需void*转换。这消除了 libuv 编程中最容易出错的环节之一。五、能力全景Net 模块UVCPP_BUILD_NETON默认开启类说明uvcpp_tcp_client高级 TCP 客户端双模式 API异步回调 / 同步wait()带超时uvcpp_tcp_serverTCP 服务端bind()listen(连接回调, backlog)uvcpp_udp_clientUDP 客户端双模式发送/接收uvcpp_udp_serverUDP 服务端bind()/recv_start()TCP 服务端有一点值得注意连接回调是listen()的第一个参数没有单独的on_connection()。所有连接共用同一个读回调通过set_read_callback()设置数据、对端关闭、读错误被明确拆成三个事件不用再去猜数据怎么不来了。Web 模块UVCPP_BUILD_WEBON类说明uvcpp_http_clientHTTP 客户端支持 keep-alive、流式解析、双模式send()/send_wait()set_http2_enabled()开 h2uvcpp_http_serverHTTP 服务端路由注册、每连接解析器、Upgrade 检测set_http2_enabled()开 h2uvcpp_http_parser流式 HTTP 解析器封装 llhttpPIMPL 模式uvcpp_http_request/uvcpp_http_response请求/响应对象序列化与工厂方法uvcpp_ws_clientWebSocket 客户端RFC 6455解析ws:///wss://uvcpp_ws_serverWebSocket 服务端通过on_upgrade()从 HTTP 自动升级uvcpp_ws_connectionWebSocket 连接send_text()/send_binary()/send_ping()/send_close()uvcpp_ws_parser流式 WebSocket 帧解析器RFC 64558 状态状态机uvcpp_http_commonHTTP 方法/状态码枚举、版本枚举HVER_10/11/20Web 应用框架UVCPP_BUILD_WEBAPPON建在 web 模块之上的应用层——写业务 handler 就行不用拼报文。类说明uvcpp_web_app应用本体配置、路由、中间件、生命周期start/stop/joinuvcpp_web_router模式路由 ——/user/:id参数、/files/*path通配静态 参数 通配uvcpp_web_request/uvcpp_web_response请求/响应封装查询串、表单、Cookie、路径参数、chunked、send_fileuvcpp_web_context每请求上下文中间件链、post()、hold()/release()uvcpp_web_static静态文件服务ETag、Last-Modified、Range/206/416、LRU 缓存、SPA 回落uvcpp_web_upload/uvcpp_web_multipartmultipart 流式落盘随机叶子名 六条大小上限uvcpp_web_stream请求体流式接收on_data/on_end/pause/resumeuvcpp_web_ws应用上的 WebSocket 端点uvcpp_web_ws_clientWebSocket 客户端回调装在客户端上 可选自动重连uvcpp_web_work_limit工作线程池准入闸门uv_queue_work的背压uvcpp_log/uvcpp_log_console两级日志等级 模块sink 可插拔uvcpp_web_jsonnlohmann/json 的收口层——不让异常穿透 libuv 回调HTTP/2 模块UVCPP_ENABLE_NGHTTP2ON基于 nghttp2 静态链入提供 h2 会话/连接层和 ALPN 协商。本库的 h2 只走 TLS ALPN——不做 h2c、不做 prior-knowledge、不做 RFC 8441、不做:protocol。UVCPP_ENABLE_NGHTTP2在UVCPP_ENABLE_OPENSSLOFF时会强制关闭给一条 warning而不是留一个根本跑不起来的配置——因为本库的 HTTP/2 没有明文形态。SSL 模块UVCPP_ENABLE_OPENSSLON类说明uvcpp_ssl_contextSSL/TLS 上下文证书/密钥加载自签名证书生成uvcpp_ssl每连接 SSL 封装handshake()/read()/write()/shutdown()TLS 侧有一个安全细节值得强调tls_verify_mode::PEER_STRICT在客户端侧真的会校验主机名——connect()收到的那个名字被钉给证书数字 IP 字面量走X509_VERIFY_PARAM_set1_ip_asc()其余走set1_host()名字对不上的对端建立不起来。它此前与PEER完全等价服务端侧至今仍然等价本库的服务端不发 SNI、也不要求客户端证书没有可校验的名字。Expand 模块内存池TCMalloc 风格的内存池页堆、span 分配器、线程缓存、enterprise 分配器。用于降低高频异步场景下的分配开销把性能压榨到更接近底层。v1.1.0 起默认关闭UVCPP_BUILD_EXPANDOFF需要显式传-DUVCPP_BUILD_EXPANDON才启用。预编译产物是带池发布的使用者什么都不用传——包里的uvcpp/uvcpp_config.h给出这个包实际用的值自己再定义成别的值会直接#error而不是静默的分配器错配。六、v1.3.0 新增重头戏多事件循环横向扩展一条事件循环最多只能占满一个核。这是所有单线程异步框架的天花板。1.3.0 之前libuvcpp 也有这个限制。现在set_loops(n)会在同一个进程内起1 条接受者循环 n−1 条工作循环接受这条路留在一个线程上连接的 I/O 摊到各条工作循环。#includewebapp/uvcpp_web_app.husingnamespaceuvcpp;intmain(){uvcpp_web_app app;app.set_host(0.0.0.0).set_port(8080).set_access_log(false);// 1 条接受者 3 条工作循环。不能链式且必须在 start()/run() 之前。constintrcapp.set_loops(4);if(rc!0)return1;app.get(/json,[](uvcpp_web_request,uvcpp_web_responseresp,uvcpp_web_next){resp.json_str({\hello\:\world\});resp.end();});app.start();// n 1 只能 start()/start_background()run(md) 会被拒app.join();return0;}打开它之前值得知道这几条在start()/run()之前调用。路由注册也要在start()之前——n 1时这条从建议变成必须。set_loops返回int所以不能接在set_host(...).set_port(...)这条链上。成功返0n不在1..64内返UV_EINVAL运行时已经起来过返UV_EBUSY。n 1与从不调用它逐字节相同——不建格子、不装钩子、不多起线程。档位拧到 1 不付任何代价。n 1时run(md)会被UV_EINVAL拒掉。用start()/start_background()收尾用stop()与join()。接受者那条循环不承载任何连接。loop_count()报一共有几条循环connection_count_at(i)报每格各有多少——下标0是接受者它恒为 0负载由各条工作循环分担。socket 转手有自己的一套原语net/uvcpp_socket_handoff.hWindows 走WSADuplicateSocketWWSASocketW其余走dup()。Windows 上这条腿压着一个已知的 libuv 缺陷——开之前先读doc/net-guide.md§4 与RELEASE.md。七、快速上手7.1 构建仅核心mkdirbuildcdbuild cmake..-DCMAKE_BUILD_TYPERelease-DUVCPP_BUILD_TESTSON cmake--build.--configRelease--parallelctest --output-on-failure-CRelease7.2 完整构建Web WebApp SSL 压缩# Linux: 先安装系统依赖sudoapt-getinstalllibssl-dev zlib1g-dev cmake..-DCMAKE_BUILD_TYPERelease-DUVCPP_BUILD_TESTSON\-DUVCPP_BUILD_WEBON\-DUVCPP_BUILD_WEBAPPON\-DUVCPP_ENABLE_OPENSSLON\-DUVCPP_ENABLE_ZLIBON\-DUVCPP_BUILD_EXAMPLESON cmake--build.--configRelease--parallel./examples/Release/webapp_demo7.3 TCP Echo 服务端#includeuvcpp.h#includenet/uvcpp_tcp_server.h#includeiostreamintmain(){uvcpp::uvcpp_tcp_server server;server.bind(127.0.0.1,8080);// 所有连接共用这一个读回调数据、对端关闭、读错误是三个明确的事件// 不用再去猜数据怎么不来了。server.set_read_callback([](uvcpp::uvcpp_tcp_clientclient,constuvcpp::net_read_resultr){if(r.is_data()){std::cout收到: std::string(r.data,r.size)std::endl;// 传了回调才是异步写不传等价于 write_wait()会在 loop 线程上等死。client.write(r.data,r.size,[](int){});}else{std::cout客户端断开std::endl;}});// 连接回调是 listen() 的第一个参数不是另一个 on_connection()。// 读回调要在 listen() 之前设好。server.listen([](uvcpp::uvcpp_tcp_client*client){std::cout客户端已连接std::endl;});std::coutEcho 服务运行在 :8080std::endl;server.run();return0;}7.4 HTTP GET 请求#includeweb/uvcpp_http_client.h#includeiostreamintmain(){uvcpp::uvcpp_http_client client;client.get(http://httpbin.org/get,[](constuvcpp::uvcpp_http_responsersp,interr){if(err)return;std::cout状态码: static_castint(rsp.status_code)std::endl;std::cout响应体: std::string(rsp.body.get_const_data(),rsp.body.size())std::endl;});client.run();return0;}7.5 WebSocket 服务端#includeweb/uvcpp_ws_server.h#includeiostreamintmain(){uvcpp::uvcpp_ws_server server;server.bind(127.0.0.1,8080);server.on_connection([](uvcpp::uvcpp_ws_connection*conn){std::coutWS 客户端已连接std::endl;conn-on_text([conn](conststd::stringmsg){std::cout收到: msgstd::endl;conststd::string reply回显: msg;conn-send_text(reply.c_str(),reply.size());});// 会话结束是 (关闭码, 原因) 两个参数不是一个连接指针。conn-on_close([](uvcpp::ws_close_code code,conststd::stringreason){std::coutWS 客户端断开: static_castint(code) reasonstd::endl;});});server.listen();server.run();return0;}7.6 最激动人心的部分WebApp有了 webapp 模块你几乎感受不到自己是在写 C 网络程序#includewebapp/uvcpp_web_app.husingnamespaceuvcpp;intmain(){uvcpp_web_app app;app.set_port(8080).use(web_middleware_access_log()).use(web_middleware_cors());app.get(/hello,[](uvcpp_web_requestreq,uvcpp_web_responseresp,uvcpp_web_next next){resp.json_str({\hello\:\world\});resp.end();});app.serve_static(/assets,./public);app.websocket(/echo,[](uvcpp_web_ws_requestws){uvcpp_ws_connection*cws.connection();c-on_text([c](conststd::stringm){c-send_text(m.c_str(),m.size());});});app.start();// bind 之后在后台线程跑事件循环app.join();return0;}路由、中间件、静态文件、上传、WebSocket、日志——该有的都有。完整指南见仓库里的doc/webapp-guide.md。八、v1.3.0 版本亮点8.1 HTTP/2 正式集成延续 1.2.0HTTP/2RFC 9113支持已经完整落地通过UVCPP_ENABLE_NGHTTP2ON显式开启底层基于 nghttp2 静态链入。设计上的关键取舍本库的 h2 只走 TLS ALPN——不做 h2c、不做 prior-knowledge、不做 RFC 8441、不做:protocol。没有 ALPN 就没有可协商的东西自动升级只能靠猜。因此uvcpp_web_app零配置自动协商ALPN 在 h2 / h1.1 之间选择底层的uvcpp_http_client/uvcpp_http_server仍默认 HTTP/1.1需要显式set_http2_enabled()。8.2 多事件循环横向扩展落地1.3.0 新增uvcpp_tcp_server::set_loops(n)和uvcpp_web_app::set_loops(n)让单进程能突破单核天花板。这是 1.3.0 最大的能力增量细节见上一节。8.3 实测数据不是估算见doc/benchmark.md数据来自用本库构建的真实 webapp每条空闲连接 4.62 KiB4 734 B八档最小二乘拟合R² 0.999987外推到 100 万连接约 4.42 GiB单事件循环 75k RPS10 分钟 soak 跑 3 840 万请求0 错误。仓库里另有一套压测靶场在bench/下、由UVCPP_BUILD_BENCH开关控制默认 OFF不进 CI。8.4 大量性能与语义修复1.1.x → 1.2.x 全部折叠进 1.3.0从 1.1.0 到 1.3.0跨越 1.1.x 和 1.2.x 两条开发线落地的改动按主题分组性能方面响应序列化不再走std::ostringstream用户态 −10.19%RPS 91 330 → 93 688九个端点报文逐字节不变读缓冲不再清零——libuv 对 TCP 流给 64 KiB、一次请求跑两趟每请求白清 128 KiB用户态 −22.88%QPS 12.02%写队列把能带走的整批拼成一次写段/请求 3.43 → 2.06−40%每请求 CPU 约 −30%RPS 38 839 → 57 067每请求内存分配次数从 16 次降到 13 次响应头表按需预留 上下文对象合并分配uvcpp_buf的 14 个先 resize 再 memcpy入口不再清零马上要被盖掉的那段。语义与安全修复错误路径不再编造状态码连接中途断开时不再交付编出来的200HEAD与GET的头完全一致压缩响应也一样206 Partial Content一律不压缩——Content-Range与Content-Encoding自相矛盾WebSocket 服务端强制客户端掩码、校验文本帧的 UTF-8客户端也真的掩码了tls_verify_mode::PEER_STRICT在客户端侧真的校验主机名名字对不上的对端建立不起来内存池 SUPER 档256 KiB不再静默泄漏三条释放路径在free之后读块头的 use-after-free 已修。8.5 每个包都带调试版动态库每份预编译包里现在同时携带 release 和 debug 两档共享库uvcppd.dll/libuvcppd.dll/libuvcppd.soMSVC 那份还带uvcppd.pdb。方便单步进库内部排查问题。注意它仅用于调试不可再分发——调试版 CRT 不可再分发且不包含在包内。8.6 十二篇文档 三道文档门禁1.2.1 起新增十二篇指南使用者向八篇lowlevel-guide.md、net-guide.md、ssl-guide.md、web-http-guide.md、web-ws-guide.md、http2-guide.md、expand-guide.md、webapp-support-guide.md与贡献者向四篇CONTRIBUTING.md、build-guide.md、testing-guide.md、release-process.md。并且进了三道文档门禁构建系统的选项与 README 选项表双向闭合、链接/路径/锚点可解析、每篇指南都被正文提到check_docs.py22 篇文档里的文件:行号引用能解析且与内容哈希锁一致check_doc_lines.py每个cpp片段都对着一个已 stage 的包、不加任何-D真编译check_doc_snippets.py——所以文档里的示例可以照抄。8.7 六平台预编译Windows x64 / arm64MinGW-w64 和 MSVC 各一Linux x64 / arm64glibcUbuntu 22.04 构建。MinGW-w64 的 DLL 静态链接了 libuv、llhttp、zlib、OpenSSL 和 MinGW 运行时在任何受支持的 Windows 上无需额外依赖即可运行。九、这条路走了三年libuvcpp 不是一时兴起的产物。三年前我做了第一版尝试——VLibuv。那是一次不太成熟的探索暴露了很多问题接口设计不合理、内存管理混乱、抽象层次没找对。但正是这些踩坑为后面 libuvcpp 的设计打下了基础。这三年里我不断尝试、不断重构也在结合 AI 辅助编码的方式提高迭代效率。直到2026 年 2 月libuvcpp 发布了第一个稳定的 C API 封装版本标志着一个可以真正被外部项目使用的基础层成型。从那一刻开始我的工作重心转向了高级框架功能的设计和开发——在 libuvcpp 之上构建真正的 Web 服务能力。v1.2.0 的 HTTP/2 落地、v1.3.0 的多事件循环横向扩展都是这个方向上的重要里程碑。也特别感谢本仓第一位外部贡献者 sercebr——仓库里有若干条修复来自他报的 issue。十、路线图HTTP/2 完善与 HTTP/310.1 当前HTTP/2 已落地继续打磨HTTP/2 在 1.3.0 中已经是稳定能力。下一个版本会继续打磨把配置进一步简化、把客户端/服务端两条路径的覆盖补齐、把与 webapp 的整合做得更顺。已知的一项待办流级背压pause_stream()/resume_stream()、peer_window_size()已经在协议层实现但暂无应用层调用方——框架侧没有任何调用点h2 上边收边给的流式请求体因此仍不可达。这会是下一阶段的重点之一。如果你有 HTTP/2 的使用场景欢迎在 GitHub 上提 issue 讨论。10.2 更远的未来HTTP/3HTTP/3 目前处于规划阶段。它基于 QUIC 协议运行在 UDP 之上与 HTTP/2 的 TCP 传输模型有本质区别。这一阶段的主要工作包括QUIC 传输层在 libuv 的uvcpp_udp之上封装 QUIC 连接管理或集成成熟的 QUIC 实现如 ngtcp2、quiche作为帧层与 HTTP/2 API 风格统一让两种协议共享尽可能多的会话抽象上层业务代码可以平滑切换TLS 1.3 集成QUIC 强制要求 TLS 1.3需要在传输层完成与 TLS 库的对接。具体方案会随着 HTTP/2 的持续打磨和社区反馈逐步明确。如果你对 HTTP/3 的实现有想法或者有相关的使用场景欢迎在 GitHub 上参与讨论——你的需求会影响这个功能的优先级和设计方向。整体路线可以概括为阶段内容状态v1.1.0核心封装 Net Web WebApp SSLAPI 稳定✅ 已发布v1.2.0HTTP/2nghttp2 ALPN调试库性能优化六平台预编译✅ 已发布v1.3.0多事件循环横向扩展set_loops大量性能与语义修复十二篇指南✅ 已发布下一版本HTTP/2 流级背压打通至应用层、易用性提升 开发中远期HTTP/3基于 QUIC 规划中十一、写在最后这不是一个人的项目libuvcpp 的目标很明确给 C 一个真正可用、好用、容易上手的 Web 库。它不满足于做一个能跑的封装层而是要长期维护、持续演进逐步补齐现代 Web 应用所需的每一个关键能力——协议、安全、性能、易用性。这个项目会一直做下去不会半途而废。但一个人走得快一群人走得远。如果你也是那个相信C 可以写 Web 服务的开发者如果你也厌倦了在 Java/Python/Node.js 之间做语言妥协欢迎加入进来。不管是提 issue、写文档、贡献代码还是仅仅提一个你希望看到的功能——每一点参与都会让这个还很稚嫩的 C Web 库往成熟稳定靠近一步。项目地址Github for libuvcpp如果这个项目对你有帮助欢迎 star 支持。有任何问题或建议也欢迎通过 GitHub issue 交流。让我们一起把 C Web 这件事做出来。