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

Boost Asio异步客户端问题:读写回调未触发及协程版疑问

Boost Asio异步TCP客户端问题排查与最佳实现

一、基于回调的客户端核心问题

你的回调版本代码存在以下关键错误,导致读写处理函数未触发:

  1. Socket类型不匹配:代码定义了Defer<tcp::socket>类型,但类成员socket_使用的是普通tcp::socket。Defer类型的socket默认使用deferred token,而你调用async_connect时传入了bind回调,两种异步完成机制冲突,导致回调无法被正确触发。
  2. 冗余死循环:main函数中io_service.run()之后的while(1){}毫无意义,run()返回时说明所有异步操作已完成,此时io_service已停止,后续操作不会被处理。
  3. 缺少写操作同步:未处理并发写请求的情况,多次调用write可能导致数据混乱。

修正后的回调版本代码

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <deque>

namespace asio = boost::asio;
using asio::ip::tcp;
using boost::system::error_code;

class TCPClient {
public:
    TCPClient(asio::io_context& io_context, tcp::resolver::iterator endpoint_iterator)
        : io_context_(io_context), socket_(io_context) {
        connect(endpoint_iterator);
    }

    void write(const std::string& message) {
        io_context_.post(boost::bind(&TCPClient::do_write, this, message));
    }

    void close() {
        io_context_.post(boost::bind(&TCPClient::do_close, this));
    }

private:
    void connect(tcp::resolver::iterator endpoint_iterator) {
        asio::async_connect(socket_, endpoint_iterator,
            boost::bind(&TCPClient::handle_connect, this,
                asio::placeholders::error));
    }

    void handle_connect(const error_code& error) {
        if (!error) {
            std::cout << "connected" << std::endl;
            write("Hello, World!");
            read();
        } else {
            std::cout << "connection failed: " << error.message() << std::endl;
        }
    }

    void do_write(const std::string& message) {
        if (write_in_progress_) {
            write_queue_.push_back(message);
            return;
        }
        write_in_progress_ = true;
        asio::async_write(socket_,
            asio::buffer(message),
            boost::bind(&TCPClient::handle_write, this,
                asio::placeholders::error));
    }

    void handle_write(const error_code& error) {
        write_in_progress_ = false;
        if (!error) {
            std::cout << "write successfully" << std::endl;
            if (!write_queue_.empty()) {
                do_write(write_queue_.front());
                write_queue_.pop_front();
            }
        } else {
            std::cout << "write failed: " << error.message() << std::endl;
            close();
        }
    }

    void read() {
        socket_.async_read_some(asio::buffer(data_, max_length),
            boost::bind(&TCPClient::handle_read, this,
                asio::placeholders::error,
                asio::placeholders::bytes_transferred));
    }

    void handle_read(const error_code& error, size_t bytes_transferred) {
        if (!error) {
            std::cout << "Received: " << std::string(data_, bytes_transferred) << std::endl;
            read();
        } else {
            std::cout << "read failed: " << error.message() << std::endl;
            close();
        }
    }

    void do_close() {
        error_code ec;
        socket_.close(ec);
        if (ec) {
            std::cout << "close failed: " << ec.message() << std::endl;
        }
    }

    asio::io_context& io_context_;
    tcp::socket socket_;
    enum { max_length = 1024 };
    char data_[max_length] = {0};
    bool write_in_progress_ = false;
    std::deque<std::string> write_queue_;
};

int main() {
    try {
        asio::io_context io_context;
        tcp::resolver resolver(io_context);
        auto endpoint_iterator = resolver.resolve("localhost", "8989");
        TCPClient client(io_context, endpoint_iterator);
        io_context.run();
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    return 0;
}

二、协程版本的问题

你的协程版本存在以下错误:

  1. 内部io_context未运行:协程内创建的io_context没有调用run(),导致异步操作无法被调度执行。
  2. 硬编码端点:直接使用from_string("127.0.0.1")不够灵活,应该通过resolver解析地址。
  3. 缺少持续读取逻辑:仅读取一次就结束,无法持续接收服务器数据。

修正后的协程版本代码

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/use_awaitable.hpp>

namespace asio = boost::asio;
using asio::ip::tcp;
using asio::awaitable;
using asio::co_spawn;
using asio::detached;
using asio::use_awaitable;

awaitable<void> connectAndCommunicate(asio::io_context& io_context) {
    try {
        tcp::socket socket(io_context);
        auto endpoints = co_await tcp::resolver(io_context).async_resolve("localhost", "8989", use_awaitable);
        co_await asio::async_connect(socket, endpoints, use_awaitable);

        std::cout << "connected" << std::endl;
        std::string message = "Hello, World!";
        co_await asio::async_write(socket, asio::buffer(message), use_awaitable);
        std::cout << "write successfully" << std::endl;

        char data[1024];
        while (true) {
            std::size_t length = co_await socket.async_read_some(asio::buffer(data), use_awaitable);
            std::cout << "Received: " << std::string(data, length) << std::endl;
        }
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
}

int main() {
    try {
        asio::io_context io_context;
        co_spawn(io_context, connectAndCommunicate(io_context), detached);
        io_context.run();
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    return 0;
}

三、最佳实现方式

  • 优先使用协程版本:Boost Asio的C++20协程支持让异步代码逻辑更线性,避免回调地狱,错误处理可通过try-catch统一处理,代码可读性和可维护性远高于回调版本。
  • 回调版本适配场景:若需兼容C++17及以下环境,回调版本仍是可行方案,但必须注意异步操作的同步问题(如写队列),避免并发操作导致的数据错误。

内容的提问来源于stack exchange,提问作者Jelal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 17:02:06