一、引言:为什么用协程实现 HTTP 服务器?

传统 HTTP 服务器的编程模型大致分为:

  • 多线程阻塞型:每连接一线程,简洁但扩展性差

  • 事件驱动模型(如 epoll + 状态机):高性能但逻辑复杂

  • 回调式异步(如 Boost::Asio):性能好但写法晦涩

C++20 引入 原生协程(coroutine) 后,我们可以用“同步风格”编写异步逻辑,兼顾性能与可读性。

本文将带你从零实现一个基于协程 + epoll 的轻量 HTTP 服务器,支持多连接并发、非阻塞 I/O 以及协程调度。


二、项目目标与能力规划

项目目标

实现一个简易 HTTP 服务:

  • 使用 epoll 管理连接

  • 每个连接由一个协程处理

  • 实现 HTTP 请求解析 + 回应

  • 支持并发数千连接的负载能力


三、项目结构概览

 

swift

复制编辑

/http_coro_server/ │ ├── main.cpp // 主程序,启动监听与事件循环 ├── coroutine_task.hpp // Task 协程封装 ├── epoll_loop.hpp // epoll 封装与事件调度 ├── connection.hpp // 每个连接处理逻辑(协程) └── utils.hpp // socket/非阻塞工具函数


四、基础组件 1:协程返回类型 Task

 

cpp

复制编辑

// coroutine_task.hpp template<typename T = void> struct Task { struct promise_type { std::optional<T> value; Task get_return_object() { return Task{std::coroutine_handle<promise_type>::from_promise(*this)}; } std::suspend_always initial_suspend() noexcept { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void return_value(T v) { value = v; } void unhandled_exception() { std::exit(1); } }; std::coroutine_handle<promise_type> handle; Task(std::coroutine_handle<promise_type> h) : handle(h) {} ~Task() { if (handle) handle.destroy(); } void start() { handle.resume(); } };


五、基础组件 2:设置非阻塞 socket 工具

 

cpp

复制编辑

// utils.hpp int set_non_blocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); return fcntl(fd, F_SETFL, flags | O_NONBLOCK); } int create_server_socket(int port) { int fd = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in addr{AF_INET, htons(port), INADDR_ANY}; bind(fd, (sockaddr*)&addr, sizeof(addr)); listen(fd, SOMAXCONN); set_non_blocking(fd); return fd; }


六、基础组件 3:epoll 封装与事件管理

 

cpp

复制编辑

// epoll_loop.hpp class EpollLoop { int epoll_fd; std::unordered_map<int, std::coroutine_handle<>> waiters; public: EpollLoop() { epoll_fd = epoll_create1(0); } void add(int fd, uint32_t events, std::coroutine_handle<> h) { epoll_event ev{events, {.fd = fd}}; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev); waiters[fd] = h; } void resume_events() { epoll_event events[64]; int n = epoll_wait(epoll_fd, events, 64, 10); for (int i = 0; i < n; ++i) { int fd = events[i].data.fd; if (waiters.count(fd)) { auto h = waiters[fd]; waiters.erase(fd); h.resume(); } } } void remove(int fd) { epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr); waiters.erase(fd); } };


七、Awaitable 类型封装:ReadAwaiter / WriteAwaiter

 

cpp

复制编辑

struct ReadAwaiter { int fd; EpollLoop& loop; bool await_ready() { return false; } void await_suspend(std::coroutine_handle<> h) { loop.add(fd, EPOLLIN | EPOLLET, h); } void await_resume() {} }; struct WriteAwaiter { int fd; EpollLoop& loop; bool await_ready() { return false; } void await_suspend(std::coroutine_handle<> h) { loop.add(fd, EPOLLOUT | EPOLLET, h); } void await_resume() {} };


八、协程:每个连接的处理逻辑

 

cpp

复制编辑

// connection.hpp Task<> handle_connection(int client_fd, EpollLoop& loop) { char buf[1024]; std::string request; while (true) { co_await ReadAwaiter{client_fd, loop}; int n = read(client_fd, buf, sizeof(buf)); if (n <= 0) break; request.append(buf, n); if (request.find("\r\n\r\n") != std::string::npos) break; } std::string response = "HTTP/1.1 200 OK\r\n" "Content-Length: 13\r\n" "Content-Type: text/plain\r\n\r\n" "Hello, world!"; co_await WriteAwaiter{client_fd, loop}; send(client_fd, response.c_str(), response.size(), 0); close(client_fd); }


九、main 函数:接受连接 + 事件循环

 

cpp

复制编辑

// main.cpp #include "coroutine_task.hpp" #include "utils.hpp" #include "epoll_loop.hpp" #include "connection.hpp" int main() { int server_fd = create_server_socket(8080); EpollLoop loop; while (true) { sockaddr_in client_addr; socklen_t len = sizeof(client_addr); int client_fd = accept(server_fd, (sockaddr*)&client_addr, &len); if (client_fd >= 0) { set_non_blocking(client_fd); handle_connection(client_fd, loop).start(); } loop.resume_events(); // 激活等待事件的协程 } close(server_fd); }


十、性能测试建议

可以使用 Apache Benchmark 或 wrk 进行测试:

 

bash

复制编辑

ab -n 10000 -c 100 http://127.0.0.1:8080/

或:

 

bash

复制编辑

wrk -t4 -c100 -d10s http://localhost:8080/


十一、可拓展方向

功能描述
路由系统根据 path 分发协程函数处理
支持 POST / JSON 请求使用简单 parser 解析请求体
Keep-Alive / 连接复用支持复用 client_fd 管理多个请求
异步文件读取结合协程读取 HTML/文件资源
TLS/SSL 支持整合 OpenSSL,使用协程方式发送加密响应


十二、项目优势与可落地性

  • C++20 原生协程,无需 Boost、libuv 等繁重依赖

  • epoll + 协程方式性能优于线程池架构

  • 易于理解与拓展,可逐步演化为微型 Web 框架

  • 支持高并发请求处理,适合 IoT、嵌入式、微服务网关等场景


十三、总结

我们实现了一个完整的协程驱动 HTTP Server:

  • 基于 C++20 coroutine + epoll 构建事件驱动框架

  • 每个连接一个协程,逻辑清晰,处理自然

  • 使用 Awaitable 类型管理 I/O 阻塞

  • 实现非阻塞 accept、read、write

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/911806.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/911806.shtml
英文地址,请注明出处:http://en.pswp.cn/news/911806.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《视频:点亮数字时代的光影魔方》

视频的前世今生&#xff1a;从诞生到爆火 视频的发展历程是一部充满创新与变革的历史&#xff0c;它见证了科技的飞速进步和人类对信息传播与娱乐方式不断追求的过程。从早期的雏形到如今的全面普及&#xff0c;视频经历了多个重要阶段&#xff0c;每一个阶段都伴随着关键节点与…

秋招Day14 - MySQL - 运维

百万级别以上的数据如何删除&#xff1f; 这么大量的DELETE操作可能会导致长时间锁表 可以进行批量删除&#xff0c;把要删除的数据分为多个小批次处理。 也可以采用创建新表&#xff0c;把不需要删除的数据迁移过来&#xff0c;然后废弃旧表。需要检查新表空间是否足够、分…

(C++)vector数组相关基础用法(C++教程)(STL库基础教程)

源代码&#xff1a; #include <iostream> #include <vector> #include <string> using namespace std;int main(){char a;int b;int c;vector <char> numbers;cout<<"请输入一组字符&#xff08;按下#结束&#xff09;:\n";while(1){…

面试的问题

主题&#xff1a;LLM相关、多模态相关、python编程、java编程 参见&#xff1a;小红书面试相关的帖子 LLM相关&#xff1a; 02.大语言模型架构/1.attention/1.attention.md qzl66/llm_interview_note - Gitee.com 02.大语言模型架构/Transformer架构细节/Transformer架构细…

【EDA软件】【应用功能子模块网表提供和加载编译方法】

1.背景 使用者做FPGA应用开发&#xff0c;将开发成果交给自己的客户&#xff0c;但是并不想提供RTL源码以及加密的源码&#xff0c;只想提供网表文件。 2.方法 2.1 指定应用功能子模块设置为Top层&#xff1b; 2.2 运行综合&#xff0c;在outputs文件夹下会生成该应用功能子…

Spring:多数据源配置多个事务管理器DEMO

Spring配置文件&#xff1a; 配置2个事务管理器&#xff1a;txManager和txManager2 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLS…

通用 Excel 导出功能设计与实现:动态列选择与灵活配置

在企业级应用开发中&#xff0c;数据导出是高频需求。本文介绍一种支持动态列选择、灵活配置的通用 Excel 导出方案&#xff0c;通过前后端协同设计&#xff0c;实现导出字段、列顺序、数据格式的自定义&#xff0c;满足多样化业务场景。 一、功能架构设计 核心特性 动态字段…

安全壁垒 - K8s 的 RBAC、NetworkPolicy 与 SecurityContext 精要

安全壁垒 - K8s 的 RBAC、NetworkPolicy 与 SecurityContext 精要 如果说 Kubernetes 是我们构建云原生应用的“城市”,那么我们已经学会了如何规划道路(网络)、建设住宅(Pod 调度)、提供水电(存储)以及智能调节城市规模(自动伸缩)。现在,是时候为这座城市安装“城门…

服务器开放端口如何设置,本地内网开通应用端口让外网访问连接步骤

在互联网时代&#xff0c;服务器扮演着至关重要的角色&#xff0c;为了让本地搭建部署的服务器能够正常提供互联网服务&#xff0c;我们需要开放特定端口以供外部网络的客户端访问&#xff0c;本文将带领大家深入了解内网本地服务器如何设置端口开放给公网访问。 服务器开放端…

【深度学习新浪潮】什么是上下文工程?

什么是上下文工程? 上下文工程(Context Engineering) 是指通过设计、优化与大语言模型(LLM)交互时的输入内容(即“上下文”),引导模型生成更符合预期、更精准回答的系统性方法。这里的“上下文”通常包括 提示词(Prompt)、示例(Few-Shot Examples)、历史对话记录、…

Ansible ad-hoc模式常用三大模块“script、shell、command“应用笔记

script模块 - 外卖厨师 相当于你把做好的菜谱&#xff08;脚本文件&#xff09;分发给别人厨房执行 适合场景&#xff1a; ✓ 需要复杂菜谱&#xff08;多步骤脚本&#xff09; ✓ 保证每家分店味道一致&#xff08;环境标准化&#xff09; 示例&#xff1a;把《红烧肉制作指…

双重检查锁定实现的单例模式为什么需要volatile

今天介绍一下 单例模式(Singleton) 应用场景&#xff1a;配置管理类、数据库连接池、线程池 实现方式&#xff1a;双重检查锁定、静态内部类、枚举 public class ConfigManager {private static volatile ConfigManager instance;private ConfigManager() {}public static C…

Flink流水线+Gravitino+Paimon集成

1.数据源管理 1.1 添加Gravitino数据源 添加成功之后&#xff0c;会在Gravitino中创建一个名为配置的中的meatalake 1.2. 添加Paimon数据源 属性gravitinoId可以关联前面创建的Gravitino数据源&#xff0c;关联后&#xff0c;会在gravitino下创建一个该数据源的catalog。 2. …

关系代数详解与SQL示例

关系代数详解与SQL示例 关系代数是关系数据库的理论基础&#xff0c;它提供了一组操作符用于操作关系&#xff08;表&#xff09; 1. 基本操作 1.1 选择 (Selection, σ) 选择操作从关系中选择满足特定条件的元组&#xff08;行&#xff09;。 关系代数表示&#xff1a;σ条…

Android14音频子系统-Linux音频子系统ASoC-ALSA

文章目录 1、术语2、概述1&#xff09;资料快车 3、预备工作1&#xff09;codec - UDA1340 - 硬件规格2&#xff09;ASOC-ALSA代码重点目录介绍3&#xff09;ASOC-ALSA层级介绍4&#xff09;了解基本的软硬件架构 4、数据结构5、代码分析1&#xff09;Machine1、总体流程介绍2、…

零基础入门Java+大模型(持续更新)

0.初始一些常见的概念 AI&#xff1a;人工智能 大模型划分&#xff1a;&#xff08;本章了解一下这个就行&#xff09;NLP模型-->自然语言模型&#xff08;AI现在爆火的原因&#xff0c;就是自然语言模型这一块取得了很大的成就&#xff09;。 LLM&#xff1a;大语言模型…

数据库系统总结

数据库系统概述 数据库系统&#xff08;Database System, DBS&#xff09;是用于高效管理、存储和检索数据的软件系统。 数据库系统的组成包括&#xff1a;数据库、硬件、软件、人员。 三级模式-两级映像 内模式&#xff1a;管理如何存储物理的数据&#xff0c;对数据的存储…

2026-软件工程-《软件质量测试与保证》-期末复习—习题汇总

题量: 20 满分: 100 作答时间:06-04 17:30至06-22 23:59 智能分析 80分 一. 单选题&#xff08;共10题&#xff0c;50分&#xff09; (单选题)白盒测试设计测试用例的依据是( )。 A. 代码逻辑结构 B. 代码注释说明 C. 需求规格说明书 D. 用户使用场景 我的答案:A:代码逻辑结构…

量化面试绿皮书:35. 蒙蒂霍尔问题

文中内容仅限技术学习与代码实践参考&#xff0c;市场存在不确定性&#xff0c;技术分析需谨慎验证&#xff0c;不构成任何投资建议。 35. 蒙蒂霍尔问题 蒙提霍尔问题是一个基于美国老电视节目《让我们做个交易》的概率谜题&#xff0c;该问题以该节目的主持人命名。假设你现在…

如何防范 SQL 注入攻击以及SQL 注入防范技巧

在互联网高度发展的时代&#xff0c;网络安全问题日益突出&#xff0c;SQL 注入攻击成为众多网站和应用程序面临的严重威胁之一。本文将详细介绍如何防范 SQL 注入攻击&#xff0c;通过多个关键方面的详细阐述&#xff0c;帮助开发者和网站管理者构建更安全的网络环境&#xff…