## C++20 协程(Co-routine)实现异步网络请求的简易框架

C++20 引入了协程(Coroutine)概念,极大地方便了异步编程。相比传统的回调、Future/Promise 或基于事件循环的模型,协程以同步的语法实现异步逻辑,代码更易读、维护成本更低。本文将通过一个简易的框架示例,演示如何使用 C++20 协程来实现异步网络请求,并在此基础上扩展到多路复用、错误处理和超时控制。

1. 设计目标

  • 简洁:仅依赖标准库(` `, “, “)和一个轻量级网络库(本例使用 `asio` 的纯头文件版本)。
  • 可组合:协程函数返回 std::future 或自定义 Task 对象,便于链式调用。
  • 错误传播:异常在协程链中传播,最终由 std::futureget() 捕获。
  • 超时:支持 asio::steady_timer 进行超时处理,超时时抛出 std::runtime_error

2. 依赖与环境

  • 编译器:支持 C++20 并开启协程实验特性,例如 -std=c++20 -fcoroutines(GCC/Clang)或 /std:c++20(MSVC)。
  • Boost.ASIO:使用 asio.hpp(不必链接 Boost)来完成异步 I/O。
  • CMake:示例项目使用 CMake 3.20+。
cmake_minimum_required(VERSION 3.20)
project(async_co_cpp20)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(async_co main.cpp)
find_package(Boost 1.70 REQUIRED COMPONENTS system)
target_link_libraries(async_co PRIVATE Boost::system)

3. 关键实现细节

3.1 `Task

`:协程返回类型 “`cpp template struct Task { struct promise_type { std::coroutine_handle continuation; std::exception_ptr exception; T value; Task get_return_object() { return Task{std::coroutine_handle ::from_promise(*this)}; } std::suspend_never initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { if (continuation) continuation.resume(); return {}; } void return_value(T v) { value = std::move(v); } void unhandled_exception() { exception = std::current_exception(); } void set_continuation(std::coroutine_handle h) { continuation = h; } }; using handle_type = std::coroutine_handle ; handle_type coro; Task(handle_type h) : coro(h) {} ~Task() { if (coro) coro.destroy(); } T get() { if (coro.promise().exception) std::rethrow_exception(coro.promise().exception); return std::move(coro.promise().value); } }; “` #### 3.2 异步读取与协程协作 “`cpp Task async_read(Asio::ip::tcp::socket& sock, std::size_t size) { auto buffer = std::make_shared>(size); std::promise prom; auto handler = [prom = std::move(prom), buffer](auto&& self, const boost::system::error_code& ec, std::size_t n) mutable { if (ec) { prom.set_exception(std::make_exception_ptr(boost::system::system_error(ec))); } else { prom.set_value(std::string(buffer->data(), n)); } }; sock.async_read_some(boost::asio::buffer(*buffer), handler); co_return co_await prom.get_future(); } “` #### 3.3 超时包装 “`cpp Task async_read_with_timeout(Asio::ip::tcp::socket& sock, std::size_t size, std::chrono::steady_clock::duration timeout) { Asio::steady_timer timer(sock.get_executor()); timer.expires_after(timeout); std::promise prom; auto read_handler = [prom = std::move(prom)](auto&& self, const boost::system::error_code& ec, std::size_t n) mutable { prom.set_value(std::string(self->data(), n)); }; sock.async_read_some(boost::asio::buffer(buffer), read_handler); auto timer_handler = [prom = std::move(prom)](auto&& self, const boost::system::error_code& ec) mutable { if (!ec) prom.set_exception(std::make_exception_ptr(std::runtime_error(“timeout”))); }; timer.async_wait(timer_handler); co_return co_await prom.get_future(); } “` #### 3.4 主协程示例 “`cpp Task main_coroutine() { try { Asio::io_context io; Asio::ip::tcp::resolver resolver(io); auto endpoints = co_await resolver.async_resolve(“example.com”, “80”, asio::use_awaitable); Asio::ip::tcp::socket sock(io); co_await Asio::async_connect(sock, endpoints, asio::use_awaitable); std::string request = “GET / HTTP/1.1\r\nHost: example.com\r\n\r\n”; co_await sock.async_write_some(asio::buffer(request), asio::use_awaitable); std::string response = co_await async_read_with_timeout(sock, 4096, std::chrono::seconds(5)); std::cout ` 作为返回类型,以获得更完整的 I/O 适配。 后续可以继续实现: – **连接池**:使用协程实现多路复用连接,减少重连成本。 – **重试策略**:在协程链中实现指数退避重试。 – **多协议支持**:在同一框架中添加 HTTP/2、WebSocket 等。 通过上述示例,读者可以快速入门 C++20 协程的异步网络编程,进而在实际项目中灵活应用。

发表评论