使用独立asio库实现异步TCP服务器遇I/O中止错误求助
我正尝试用独立于Boost的asio库实现异步TCP服务器,但调用acceptor的async_accept函数时,出现错误提示:‘The I/o operation has been aborted because of either a thread exit or an application requests’,进程无法继续执行。换过端口没用,但同步版本服务器能正常运行。
报错的startAsyncAccept函数代码
void startAsyncAccept() { acceptor.async_accept(socket, [&](const asio::error_code& error) { if(!error) { std::cout << "Client is connected" << "\n"; startAsyncRead(); } else { std::cerr << "It is: " << error.message() << '\n'; return 0; } startAsyncAccept(); } ); }
完整异步服务器代码
class Server { public: Server(asio::io_context& io_context) : acceptor(io_context, asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 2000)), socket(io_context) { startAsyncAccept(); } ~Server() {} private: // Members asio::ip::tcp::acceptor acceptor; asio::ip::tcp::socket socket; asio::streambuf buffer; std::vector<asio::ip::tcp::socket> sockets; // Functions void startAsyncAccept() { acceptor.async_accept(socket, [&](const asio::error_code& error) { if(!error) { std::cout << "Client is connected" << "\n"; startAsyncRead(); } else { std::cerr << "It is: " << error.message() << '\n'; return 0; } startAsyncAccept(); } ); } void startAsyncRead() { asio::async_read_until(socket, buffer, '\n', [&](const asio::error_code& error, size_t length) { if(!error) { std::string message(asio::buffers_begin(buffer.data()), asio::buffers_begin(buffer.data()) + length); std::cout << "Received from client: " << message; for(auto& clients : sockets) { asio::async_write(clients, asio::buffer(message), [&](const asio::error_code& error, size_t length) { if(error) { std::cerr << "Failed to write to client: " << error.message() << "\n"; } } ); } buffer.consume(length); startAsyncRead(); } else { std::cout << "Client disconnected." << std::endl; removeClient(); } } ); } void startAsyncWrite() { asio::async_write(socket, asio::buffer("Connected to chat Server. \n"), [&](const asio::error_code& error, size_t length) { if(error) { std::cerr << "Failed to write to the client: " << error.message() << "\n"; removeClient(); } } ); } void removeClient() { auto it = std::find_if(sockets.begin(), sockets.end(), [&](const auto& client_socket) { return &client_socket == &socket; }); if (it != sockets.end()) { sockets.erase(it); } } };
问题原因及修复方案
1. 回调中错误的return 0导致监听中断
异步回调lambda的返回类型是void,返回0属于语法错误;更关键的是,错误分支直接返回会跳过后续的startAsyncAccept()调用,服务器停止监听新连接。如果此时io_context没有其他待处理任务,会直接退出,触发I/O操作中止错误。
修复:将return 0改为return;,并且无论是否出错,都要重新调用startAsyncAccept()(仅在acceptor被主动关闭时停止):
void startAsyncAccept() { acceptor.async_accept(socket, [&](const asio::error_code& error) { if(!error) { std::cout << "Client is connected" << "\n"; // 转移socket到容器,避免被下一次accept覆盖 sockets.push_back(std::move(socket)); // 重新初始化socket用于下一次监听 socket = asio::ip::tcp::socket(acceptor.get_executor().context()); // 注意:此处需操作容器中刚加入的socket,而非成员变量socket startAsyncReadForClient(sockets.back()); } else { std::cerr << "It is: " << error.message() << '\n'; // acceptor被主动关闭时,停止监听 if (error == asio::error::operation_aborted) return; } // 持续监听新连接 startAsyncAccept(); } ); }
2. 成员变量socket被复用,导致旧连接失效
Server类用单个成员变量socket处理所有客户端连接,当第二个客户端连接时,async_accept会覆盖该socket,导致第一个客户端的连接被强制断开,进而触发I/O中止错误。
修复:
- 每次accept成功后,将socket转移到
sockets容器中,再重新创建新的socket用于下一次监听。 - 为每个客户端单独维护socket和buffer,建议创建
ClientSession类封装客户端的所有操作,避免共享成员变量。
3. startAsyncRead绑定错误的socket
当前startAsyncRead操作的是Server类的成员变量socket,但该socket在accept下一个客户端时会被覆盖,导致之前的客户端读操作失效。
修复:为每个客户端的socket单独启动读操作,比如新增startAsyncReadForClient函数并传入对应的socket:
void startAsyncReadForClient(asio::ip::tcp::socket& client_socket) { auto buffer = std::make_shared<asio::streambuf>(); asio::async_read_until(client_socket, *buffer, '\n', [this, &client_socket, buffer](const asio::error_code& error, size_t length) { if(!error) { std::string message(asio::buffers_begin(buffer->data()), asio::buffers_begin(buffer->data()) + length); std::cout << "Received from client: " << message; for(auto& clients : sockets) { asio::async_write(clients, asio::buffer(message), [](const asio::error_code& error, size_t) { if(error) { std::cerr << "Failed to write to client: " << error.message() << "\n"; } } ); } buffer->consume(length); startAsyncReadForClient(client_socket); } else { std::cout << "Client disconnected." << std::endl; removeClient(client_socket); } } ); } void removeClient(asio::ip::tcp::socket& client_socket) { auto it = std::find_if(sockets.begin(), sockets.end(), [&](auto& sock) { return &sock == &client_socket; }); if (it != sockets.end()) { sockets.erase(it); } }
4. 确保io_context持续运行
在主函数中,需要保证io_context始终有工作任务,避免没有异步操作时进程退出。可以使用asio::io_context::work:
int main() { asio::io_context io_context; asio::io_context::work work(io_context); // 防止io_context无任务时退出 Server server(io_context); io_context.run(); return 0; }
内容的提问来源于stack exchange,提问作者Jésus Christophe

