Asio 是一个跨平台的 C++ 库,用于网络和低级 I/O 编程,提供了一套异步操作的框架。Boost.Asio 是其最知名的版本,但独立(standalone)版本的 Asio 允许在不依赖 Boost 的情况下使用。
Asio 库概述 Standalone Asio 是一个轻量级的网络编程库,设计目标是提供高效、异步的 I/O 操作。它支持以下功能:
网络编程 :TCP、UDP、ICMP 等协议的客户端和服务器开发。异步 I/O :通过回调、协程(C++20 起支持)或 Future/Promise 模型处理异步操作。跨平台 :支持 Windows、Linux、macOS 等操作系统。低级 I/O :文件操作、串口通信、定时器等。与 Boost.Asio 相比,standalone Asio 不依赖 Boost 的其他组件(如 Boost.System),因此更轻量,适合需要减少依赖的场景。
安装与配置 下载 Standalone Asio 可从 GitHub 或其官方网站(think-async.com)获取最新版本。下载后,解压到项目目录。
配置 头文件 :将 Asio 的 include 目录添加到项目的头文件路径。核心头文件位于 asio/include/asio.hpp。 编译 :Asio 是头文件库 ,无需单独编译库文件,只需包含头文件。 确保编译器支持 C++11 或更高版本(推荐 C++17/C++20 以使用协程等高级功能)。 宏定义 :使用 standalone Asio 时,需定义 ASIO_STANDALONE 宏以启用独立模式。 如果不使用系统提供的错误码(如 std::error_code),可定义 ASIO_NO_EXCEPTIONS 或 ASIO_NO_TYPEID 来禁用异常或 RTTI。 示例编译命令(g++):1 g++ -std=c++17 -Ipath/to/asio/include -DASIO_STANDALONE main.cpp -pthread
注意:需要链接 pthread(Linux/Unix)或 ws2_32(Windows)等系统库。 依赖 Standalone Asio 不依赖 Boost,但需要标准库支持(如 <thread>、<chrono> 等)。 如果使用 SSL 功能,需链接 OpenSSL 库。 核心概念 Asio 的核心是异步操作 和事件循环 ,以下是关键组件:
io_contextasio::io_context 是 Asio 的核心调度器,负责管理异步操作的事件循环。每个 io_context 维护一个任务队列,处理 socket、定时器等异步事件。 通常在程序启动时创建,并通过 run() 方法运行事件循环。 异步操作 Asio 支持以下异步模型:
回调 :通过传递回调函数(如 lambda 或函数对象)处理异步操作结果。协程(C++20) :使用 co_await 和 co_yield 简化异步代码。Future/Promise :通过 std::future 或 Asio 的 use_future 实现同步风格的异步调用。错误处理 Asio 使用 std::error_code 表示操作结果,允许显式检查错误,而非抛出异常。 异步操作的回调函数通常接受一个 std::error_code 参数。 主要类 Sockets :asio::ip::tcp::socket(TCP)、asio::ip::udp::socket(UDP)。Endpoints :asio::ip::tcp::endpoint(表示 IP 地址和端口)。Acceptors :asio::ip::tcp::acceptor(用于服务器接受连接)。Timers :asio::steady_timer(用于定时任务)。Buffers :asio::buffer(用于数据读写)。基本用法 以下是 standalone Asio 的典型使用场景和代码示例。
简单的 TCP 客户端 创建一个连接到服务器的 TCP 客户端,发送消息并接收响应。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <asio.hpp> #include <iostream> using asio::ip::tcp;int main () { try { asio::io_context io_context; tcp::socket socket (io_context) ; tcp::endpoint endpoint (asio::ip::make_address("127.0.0.1" ), 8080 ) ; socket.connect (endpoint); std::string message = "Hello, Asio!" ; asio::write (socket, asio::buffer (message)); char reply[1024 ]; size_t reply_length = socket.read_some (asio::buffer (reply)); std::cout << "Reply: " << std::string (reply, reply_length) << std::endl; socket.close (); } catch (std::exception& e) { std::cerr << "Exception: " << e.what () << std::endl; } return 0 ; }
说明 :
asio::write 和 read_some 是同步操作,适合简单场景。asio::buffer 包装数据以供读写。使用 try-catch 处理可能的异常。 异步 TCP 服务器 实现一个异步 TCP 服务器,接受客户端连接并回显消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include <asio.hpp> #include <iostream> #include <memory> using asio::ip::tcp;class Session : public std::enable_shared_from_this<Session> {public : Session (tcp::socket socket) : socket_ (std::move (socket)) {} void start () { do_read (); }private : void do_read () { auto self (shared_from_this()) ; socket_.async_read_some ( asio::buffer (data_, max_length), [this , self](std::error_code ec, std::size_t length) { if (!ec) { do_write (length); } }); } void do_write (std::size_t length) { auto self (shared_from_this()) ; asio::async_write ( socket_, asio::buffer (data_, length), [this , self](std::error_code ec, std::size_t ) { if (!ec) { do_read (); } }); } tcp::socket socket_; enum { max_length = 1024 }; char data_[max_length]; };class Server {public : Server (asio::io_context& io_context, short port) : acceptor_ (io_context, tcp::endpoint (tcp::v4 (), port)) { do_accept (); }private : void do_accept () { acceptor_.async_accept ( [this ](std::error_code ec, tcp::socket socket) { if (!ec) { std::make_shared <Session>(std::move (socket))->start (); } do_accept (); }); } tcp::acceptor acceptor_; };int main () { try { asio::io_context io_context; Server server (io_context, 8080 ) ; io_context.run (); } catch (std::exception& e) { std::cerr << "Exception: " << e.what () << std::endl; } return 0 ; }
说明 :
使用 async_read_some 和 async_write 实现异步读写。 std::enable_shared_from_this 确保 Session 对象的生命周期安全。io_context.run() 运行事件循环,处理所有异步操作。服务器持续监听 8080 端口,接受新连接并创建 Session 处理。 使用定时器 Asio 提供 steady_timer 用于定时任务,适合实现延迟或定时操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <asio.hpp> #include <iostream> void print (const std::error_code& ) { std::cout << "Timer expired!" << std::endl; }int main () { asio::io_context io_context; asio::steady_timer timer (io_context, std::chrono::seconds(5 )) ; timer.async_wait (&print); io_context.run (); return 0 ; }
说明 :
steady_timer 在 5 秒后触发回调函数 print。async_wait 是异步等待定时器到期。使用 C++20 协程 如果编译器支持 C++20,Asio 提供协程支持,简化异步代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <asio.hpp> #include <iostream> using asio::ip::tcp;using asio::awaitable;awaitable<void > echo (tcp::socket socket) { char data[1024 ]; for (;;) { auto n = co_await socket.async_read_some (asio::buffer (data), asio::use_awaitable); co_await asio::async_write (socket, asio::buffer(data, n), asio::use_awaitable) ; } }awaitable<void > server (asio::io_context& io_context, short port) { tcp::acceptor acceptor (io_context, tcp::endpoint(tcp::v4(), port)) ; for (;;) { auto socket = co_await acceptor.async_accept (asio::use_awaitable); co_spawn (io_context, echo (std::move (socket)), asio::detached); } }int main () { try { asio::io_context io_context; co_spawn (io_context, server (io_context, 8080 ), asio::detached); io_context.run (); } catch (std::exception& e) { std::cerr << "Exception: " << e.what () << std::endl; } return 0 ; }
说明 :
使用 co_await 和 awaitable 简化异步操作,代码更像同步风格。 co_spawn 用于启动协程。需要编译器支持 C++20 和 Asio 的协程功能(确保 Asio 版本足够新)。 高级用法 多线程支持 Asio 支持多线程运行 io_context:
创建多个线程,每个线程调用 io_context.run()。 确保 socket 和其他资源不被多个线程同时访问(通常每个 socket 绑定到一个 io_context)。 示例:
1 2 3 4 5 6 7 8 9 asio::io_context io_context;Server server (io_context, 8080 ) ; std::vector<std::thread> threads;for (int i = 0 ; i < 4 ; ++i) { threads.emplace_back ([&io_context] { io_context.run (); }); }for (auto & t : threads) { t.join (); }
SSL/TLS 支持 Asio 支持 SSL/TLS(需要 OpenSSL)。示例:
1 2 3 4 5 #include <asio/ssl.hpp> asio::ssl::context ssl_context (asio::ssl::context::tls) ; ssl_context.load_verify_file ("ca.pem" ); asio::ssl::stream<tcp::socket> ssl_socket (io_context, ssl_context) ;
配置 ssl::context 并使用 ssl::stream 包装普通 socket。 自定义分配器 Asio 允许自定义内存分配器以优化性能:
1 2 asio::io_context io_context; asio::io_context::work work (io_context) ;
UDP 示例 UDP 是无连接协议,适合广播或低延迟场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <asio.hpp> #include <iostream> using asio::ip::udp;int main () { asio::io_context io_context; udp::socket socket (io_context, udp::endpoint(udp::v4(), 0 )) ; udp::endpoint receiver (asio::ip::make_address("127.0.0.1" ), 8080 ) ; socket.send_to (asio::buffer ("Hello UDP" ), receiver); io_context.run (); return 0 ; }
注意事项 错误处理 :始终检查 std::error_code 或使用异常处理。 异步操作可能因网络中断、超时等失败,需妥善处理。 性能 :避免在回调中执行耗时操作,防止阻塞事件循环。 使用多线程或多 io_context 提高并发性能。 平台差异 :Windows 使用 IOCP,Linux 使用 epoll,macOS 使用 kqueue,Asio 自动适配。 某些功能(如文件 I/O)可能在特定平台上不可用。 调试 :启用日志或使用调试器检查异步操作的状态。 确保 io_context.run() 被调用,否则异步任务不会执行。 Demo:使用 cpp-httplib + Asio 协程 实现简单时间服务器 下面给出一个最小可运行的示例,使用 standalone Asio 的协程(C++20)处理后端逻辑,并使用 cpp-httplib 提供 HTTP 接口。该示例将在 /time 路径返回服务器当前时间的字符串。
说明与依赖:
需要编译器支持 C++20 协程(例如 g++ 11+ 或 clang 对应版本)。 需要将 Asio 的 include 路径加入编译器,并定义宏 ASIO_STANDALONE。 需要包含 cpp-httplib 的单头文件 httplib.h。 编译时示例命令(Linux): g++ -std=c++20 -I/path/to/asio/include -I/path/to/cpp-httplib -DASIO_STANDALONE time_server.cpp -pthread -o time_server 示例代码(time_server.cpp):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #include <asio.hpp> #include <asio/awaitable.hpp> #include <asio/use_awaitable.hpp> #include <asio/co_spawn.hpp> #include <asio/detached.hpp> #define CPPHTTPLIB_OPENSSL_SUPPORT #include "httplib.h" #include <chrono> #include <ctime> #include <iostream> #include <string> using asio::ip::tcp;using asio::awaitable;using asio::use_awaitable;static std::string current_time_string () { using namespace std::chrono; auto now = system_clock::now (); std::time_t t = system_clock::to_time_t (now); char buf[64 ]; std::strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S" , std::localtime (&t)); return std::string (buf); }awaitable<void > asio_worker (asio::io_context& io_ctx) { co_return ; }int main () { try { asio::io_context io_ctx (1 ) ; asio::co_spawn (io_ctx, asio_worker (io_ctx), asio::detached); std::thread asio_thread ([&]{ try { io_ctx.run(); } catch (const std::exception& e) { std::cerr << "Asio thread exception: " << e.what() << std::endl; } }) ; httplib::Server svr; svr.Get ("/time" , [](const httplib::Request& , httplib::Response& res) { res.set_content (current_time_string (), "text/plain; charset=utf-8" ); }); std::cout << "HTTP time server listening on http://0.0.0.0:8080/time\n" ; svr.listen ("0.0.0.0" , 8080 ); io_ctx.stop (); if (asio_thread.joinable ()) asio_thread.join (); } catch (const std::exception& e) { std::cerr << "Exception: " << e.what () << std::endl; return 1 ; } return 0 ; }
运行后,访问:http://127.0.0.1:8080/time 将会返回类似: 2025-09-23 19:01:02
注意事项:
该示例把 Asio 的 io_context 与 cpp-httplib 的事件循环分离到不同线程,避免了两者事件循环直接冲突。 在更复杂的场景中,可将 HTTP 请求处理委派到 Asio 协程中以完成异步 I/O(例如通过 socket)。 如果需要把全部逻辑都放到 Asio 协程中(例如自实现 HTTP 或使用支持 Asio 的 HTTP 库),可以用 acceptor + tcp socket + awaitable 来完全基于 Asio 实现。