基于Asio的异步聊天客户端Lambda回调未获参数问题求助
问题描述
开发基于Asio的异步聊天客户端时,Lambda回调函数无法获取error_code与bytes_transferred参数,导致回调不执行,程序无法正常工作。改用普通函数后问题依旧,服务端运行正常可正常收发数据,推测问题出在客户端类中,相关代码如下:
问题代码片段:
void ReadLoop() { if(!socket_.is_open()) { std::cout << "Trimp" << std::endl; } std::cout << "Socket: " << socket_.get_executor() << '\n'; std::cout << "Buffer: " << input_buffer_ << '\n'; asio::async_read_until(socket_, asio::dynamic_buffer(input_buffer_), '\n', [&](error_code error, size_t bytes_transferred) { if (error == asio::error::eof) { std::cout << "Connection closed by peer\n"; return; } if (error) { std::cerr << "Error: " << error.message() << " (" << error.value() << " - " << error.category().name() << ")\n"; } std::cout << "Received message: " << input_buffer_.substr(0, bytes_transferred); input_buffer_.erase(0, bytes_transferred); }); }
完整客户端类代码:
class AsyncClient { public: AsyncClient(asio::io_context& io_context, const std::string& server_address, const std::string& server_port) : io_context_(&io_context), socket_(io_context), resolver_(io_context), server_address_(server_address), server_port_(server_port) { } void Start() { auto endpoints = resolver_.resolve(server_address_, server_port_); std::cout << "Socket: " << socket_.get_executor() << '\n'; asio::async_connect(socket_, endpoints, [&](error_code error, const asio::ip::tcp::endpoint&) { if (!error) { std::cout << "Connected to server\n"; StartMain(); } else { std::cerr << "Error while connecting to server: " << error.message() << "\n"; } }); } void StartMain() { while(true) { ReadLoop(); WriteLoop(); } } void SendMessage(const std::string& message) { std::cout << "You are here 4" << "\n"; asio::async_write(socket_, asio::buffer(message + "\n"), [&](error_code error, size_t bytes_transferred) { if (error) { std::cerr << "Error while writing to server: " << error.message() << "\n"; } else { std::cout << "Yeas" << '\n'; } }); } private: void ReadLoop() { if(!socket_.is_open()) { std::cout << "Trimp" << std::endl; } std::cout << "Socket: " << socket_.get_executor() << '\n'; std::cout << "Buffer: " << input_buffer_ << '\n'; asio::async_read_until(socket_, asio::dynamic_buffer(input_buffer_), '\n', [&](error_code error, size_t bytes_transferred) { if (error == asio::error::eof) { std::cout << "Connection closed by peer\n"; return; } if (error) { std::cerr << "Error: " << error.message() << " (" << error.value() << " - " << error.category().name() << ")\n"; } std::cout << "Received message: " << input_buffer_.substr(0, bytes_transferred); input_buffer_.erase(0, bytes_transferred); }); } void WriteLoop() { std::string message; std::getline(std::cin, message); if (message.empty()) { WriteLoop(); return; } else { SendMessage(message); } } asio::io_context* io_context_; asio::ip::tcp::socket socket_; asio::ip::tcp::resolver resolver_; std::string server_address_; std::string server_port_; std::string input_buffer_; };
问题根源
- 阻塞循环卡死IO线程:
StartMain()中的while(true)循环持续调用ReadLoop()和WriteLoop(),而WriteLoop()里的std::getline(std::cin, message)是阻塞操作,直接占用Asio的IO线程,导致事件循环无法处理异步操作的回调。 - 重复发起异步操作:每次循环都会调用
ReadLoop(),导致大量重叠的async_read_until被发起,Asio无法正确处理这些冲突的异步任务,进一步干扰回调执行。 - Lambda引用捕获失效:使用
[&]捕获局部变量引用,当ReadLoop()函数返回后,引用的对象可能已失效,导致回调执行时出现未定义行为。
修复方案
1. 改用异步回调驱动循环
移除StartMain()的阻塞循环,在async_read_until的回调中再次发起读操作,形成异步读循环;将用户输入改为单独线程处理,避免阻塞IO线程。
2. 修正对象生命周期管理
让AsyncClient继承std::enable_shared_from_this<AsyncClient>,使用shared_from_this()捕获对象智能指针,保证异步操作期间对象不会被销毁。
3. 优化写操作逻辑
使用队列管理待发送消息,避免同时发起多个async_write操作,保证写操作的顺序执行。
修改后的完整代码
#include <asio.hpp> #include <iostream> #include <memory> #include <string> #include <deque> using asio::ip::tcp; using error_code = asio::error_code; class AsyncClient : public std::enable_shared_from_this<AsyncClient> { public: AsyncClient(asio::io_context& io_context, const std::string& server_address, const std::string& server_port) : socket_(io_context), resolver_(io_context), server_address_(server_address), server_port_(server_port) { } void Start() { auto self(shared_from_this()); resolver_.async_resolve(server_address_, server_port_, [this, self](error_code ec, tcp::resolver::results_type endpoints) { if (!ec) { async_connect(socket_, endpoints, [this, self](error_code ec, const tcp::endpoint&) { if (!ec) { std::cout << "Connected to server\n"; StartReadLoop(); // 单独线程处理用户输入,避免阻塞IO线程 std::thread(&AsyncClient::HandleUserInput, this).detach(); } else { std::cerr << "Connect error: " << ec.message() << "\n"; } }); } else { std::cerr << "Resolve error: " << ec.message() << "\n"; } }); } private: void StartReadLoop() { auto self(shared_from_this()); asio::async_read_until(socket_, asio::dynamic_buffer(input_buffer_), '\n', [this, self](error_code ec, size_t bytes_transferred) { if (!ec) { std::cout << "Received message: " << input_buffer_.substr(0, bytes_transferred); input_buffer_.erase(0, bytes_transferred); StartReadLoop(); // 回调完成后再次发起读操作,形成异步循环 } else if (ec == asio::error::eof) { std::cout << "Connection closed by peer\n"; } else { std::cerr << "Read error: " << ec.message() << " (" << ec.value() << " - " << ec.category().name() << ")\n"; } }); } void SendMessage(const std::string& message) { auto self(shared_from_this()); // 将写操作提交到IO线程执行 asio::post(socket_.get_executor(), [this, self, message]() { bool write_in_progress = !write_queue_.empty(); write_queue_.push_back(message + "\n"); if (!write_in_progress) { DoWrite(); } }); } void DoWrite() { auto self(shared_from_this()); asio::async_write(socket_, asio::buffer(write_queue_.front()), [this, self](error_code ec, size_t) { if (!ec) { write_queue_.pop_front(); if (!write_queue_.empty()) { DoWrite(); } } else { std::cerr << "Write error: " << ec.message() << "\n"; } }); } void HandleUserInput() { std::string message; while (std::getline(std::cin, message)) { if (!message.empty()) { SendMessage(message); } } } tcp::socket socket_; tcp::resolver resolver_; std::string server_address_; std::string server_port_; std::string input_buffer_; std::deque<std::string> write_queue_; // 写操作队列,避免重叠写 }; int main() { try { asio::io_context io_context; auto client = std::make_shared<AsyncClient>(io_context, "127.0.0.1", "8080"); client->Start(); io_context.run(); // 启动Asio事件循环 } catch (std::exception& e) { std::cerr << "Exception: " << e.what() << "\n"; } return 0; }
关键说明
- 异步循环:读操作在回调中递归发起,形成无阻塞的异步读循环,完全由Asio事件驱动。
- 生命周期安全:通过
shared_from_this()保证对象在异步操作完成前不会被销毁,避免引用失效问题。 - 线程分离:用户输入在独立线程处理,不会阻塞Asio的IO线程,确保事件循环能正常处理回调。
- 写队列:用队列管理待发送消息,保证写操作的顺序执行,避免重叠写操作导致的异常。
内容的提问来源于stack exchange,提问作者Jésus Christophe
相关产品推荐
相关产品推荐

