c++20协程+asio可实现直观同步风格的网络库:通过use_awaitable直接co_await异步操作;用when_all组合connect与超时;session协程自动管理生命周期;co_spawn驱动accept循环,无需回调嵌套。

用 C++20 协程 + Asio 实现一个轻量协程网络库,核心不是“造轮子”,而是把 co_await 和 Asio 的异步操作自然衔接起来,让 TCP 连接、读写、超时等逻辑写得像同步代码一样直观。
1. 基础:把 asio 的 async_xxx 封装成可 co_await 的 awaitable
Asio 本身不直接返回 awaitable,但提供了 use_awaitable 令牌(C++20 模式下需启用 ASIO_ENABLE_BOOST_BIND_HPP 或使用 Asio 1.28+ 的原生协程支持)。最简封装方式是:
✅ 推荐做法(Asio ≥ 1.26,启用 ASIO_ENABLE_COROUTINES):
- 在编译时定义
ASIO_ENABLE_COROUTINES(或用asio::use_awaitable) - 所有支持 async 的对象(如
tcp::socket,steady_timer)可直接co_await socket.async_read_some(..., use_awaitable) - 无需手写
await_suspend/await_resume,Asio 内部已实现
⚠️ 注意:确保 io_context 运行在支持协程的线程中(比如用 co_spawn 启动协程)。
立即学习“C++免费学习笔记(深入)”;
2. 写一个协程化的 TCP 连接器(connect_with_timeout)
这是典型场景:连接带超时,失败/超时都统一处理,不用回调嵌套。
示例代码结构:
task<std::error_code> connect_with_timeout(
tcp::socket& sock,
const tcp::endpoint& ep,
std::chrono::milliseconds timeout_ms) {
auto ex = sock.get_executor();
steady_timer timer{ex, timeout_ms};
// 并发等待 connect 或 timer
auto [ec1, ec2] = co_await when_all(
[&]()->task<std::error_code> {
co_return co_await sock.async_connect(ep, use_awaitable);
}(),
[&]()->task<std::error_code> {
co_await timer.async_wait(use_awaitable);
co_return make_error_code(errc::timed_out);
}()
);
if (!ec1) co_return ec1; // connect 成功
if (!ec2) { // timer 触发,主动关闭 socket
boost::system::error_code ignored;
sock.close(ignored);
co_return ec2;
}
co_return ec1 ? ec1 : ec2;
}
? 关键点:when_all 是 Asio 提供的协程组合子(需 #include <asio></asio>),类似 Go 的 select。
3. 协程化会话(session):读-处理-写的闭环
每个连接启动一个协程,生命周期清晰,错误可自然传播:
task<void> session(tcp::socket sock) {
try {
char buf[1024];
for (;;) {
auto [ec, n] = co_await sock.async_read_some(
asio::buffer(buf), use_awaitable);
if (ec == asio::error::eof) break;
if (ec) throw std::system_error{ec};
// 处理请求(例如 echo)
auto [wec, wn] = co_await sock.async_write_some(
asio::buffer(buf, n), use_awaitable);
if (wec) throw std::system_error{wec};
}
} catch (const std::exception& e) {
// 自动清理:sock 离开作用域自动关闭
}
}
✅ 优势:无手动资源管理、无状态机、异常即断连、栈语义清晰。
4. 启动服务:accept 循环用 co_spawn 驱动
避免阻塞主线程,也不用 while+poll,用协程“挂起等待”新连接:
<code>task<void> server_loop(tcp::acceptor& acceptor) {
for (;;) {
tcp::socket sock{acceptor.get_executor()};
auto [ec] = co_await acceptor.async_accept(sock, use_awaitable);
if (ec) continue;
// 每个连接派生独立协程,不阻塞 accept 循环
co_spawn(acceptor.get_executor(),
[=]() mutable -> task<void> { co_await session(std::move(sock)); },
detached);
}
}
// 启动入口
int main() {
asio::io_context ioc;
tcp::acceptor acceptor{ioc, {tcp::v4(), 8080}};
co_spawn(ioc, [&]()->task<void> { co_await server_loop(acceptor); }, detached);
ioc.run();
}
? 提示:生产环境建议加连接数限制、缓冲区复用、日志和错误监控,但骨架已具备可维护性。
基本上就这些——没有宏魔法,不依赖第三方协程调度器,纯 Asio + C++20 标准特性。难点不在语法,而在理解 executor 绑定、协程生命周期与 io_context 的协作关系。写熟几个 awaitable 封装后,网络逻辑反而比传统回调更直白。










