You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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_;
};
问题根源
  1. 阻塞循环卡死IO线程:StartMain()中的while(true)循环持续调用ReadLoop()和WriteLoop(),而WriteLoop()里的std::getline(std::cin, message)是阻塞操作,直接占用Asio的IO线程,导致事件循环无法处理异步操作的回调。
  2. 重复发起异步操作:每次循环都会调用ReadLoop(),导致大量重叠的async_read_until被发起,Asio无法正确处理这些冲突的异步任务,进一步干扰回调执行。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 17:25:02