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

为什么boost::asio::async_write第一次调用正常,第二次调用出错?

问题:Boost.Asio第二次调用async_write发送数据异常

第一次调用async_write可以成功通过TCP发送数据,但第二次调用async_write时就出现异常。如下是完整的复现代码:

#include <algorithm>
#include <array>
#include <boost/asio.hpp>
#include <boost/range.hpp>
#include <chrono>
#include <iostream>
#include <random>
#include <vector>

const std::size_t buf_size = 500*1024;
const int test_cycles = 1.024e5;

namespace asio = boost::asio;

int main()
{
  std::vector<char> send_data(buf_size);

  std::vector<char> recv_buf(buf_size);

  asio::io_service ios;

  asio::ip::tcp::socket socket1(ios);
  asio::ip::tcp::socket socket2(ios);
  asio::ip::tcp::acceptor acceptor(ios, {asio::ip::tcp::v4(), 55557});
  socket1.connect({asio::ip::address_v4::loopback(), 55557});
  acceptor.accept(socket2);

  for (std::size_t i = 0; i < 1; ++i)
  {
      auto start = std::chrono::steady_clock::now();
      for(int j=0; j < test_cycles; ++j)
      {
            size_t written_bytes = 0;
            auto to_send_data = send_data;
            asio::async_write(socket1,
                asio::dynamic_buffer(send_data),
                [&](auto ec, auto n)
                {
                    if(!ec)
                    {
                        std::cout << "successfully sent " << n << std::endl;
                    }
                    else
                    {
                        std::cout << ec.message() << std::endl;
                    }

                    if(0==n)
                    {
                        std::cout << "send error" << std::endl;
                    }

                    written_bytes = n;
                });

            asio::async_read(socket2, asio::buffer(recv_buf),
                [&](auto ec, auto n)
                {
                    if(!ec)
                    {
                        //std::cout << "received " << n << std::endl;
                    }
                    else
                    {
                        std::cout << ec.message() << std::endl;
                    }

                    if(0==n)
                    {
                        std::cout << "received error" << std::endl;
                    }

                    if(written_bytes != n)
                    {
                        std::cout << "received is not same with the sent" << std::endl;
                    }
                });
                
                ios.run();
                ios.reset();
        }
        
        auto end = std::chrono::steady_clock::now();
        std::chrono::duration<float> elapsed = end - start;
        std::cout << elapsed.count() << " seconds\n";
        std::cout << (buf_size * test_cycles / elapsed.count() / 1024 / 1024/ 1024) << " GB/s\n";
  }
}

程序运行输出如下:

successfully sent 512000
successfully sent 0
send error

我找到一个可行的变通方案可以让程序正常运行,相关改动如下:

auto to_send_data = send_data;
asio::async_write(socket1,
    asio::dynamic_buffer(to_send_data),

请问原来的代码出错的根本原因是什么?

补充更新

  • 我尝试在VSCode IDE中给std::vector::resize的实现打断点(测试环境为Ubuntu),断点无效显示为灰色,我确认程序是Debug模式编译的。使用GDB设置断点也提示"Function "std::vector::resize" not defined."。
  • 我给相关operator()的实现打断点后发现,default分支从未被触发,也就是说start的值始终为1。

问题解答

你出错的核心原因是boost::asio::dynamic_buffer的消耗式设计:
当你给async_write传入asio::dynamic_buffer(send_data)时,async_write操作完成后,dynamic_buffer会自动调用send_data.erase(0, n)移除已经成功发送的前n个字节。第一次调用async_write成功发送512000字节后,send_data的长度就被清空为0,第二次再调用async_write传入同一个send_data的dynamic_buffer时,缓冲区已经没有可发送的内容,自然就返回发送了0字节的结果。

而你修改后的方案每次都先拷贝得到新的to_send_data临时对象,每次传入dynamic_buffer的都是全新的、长度为512k的vector,操作完成后临时对象直接销毁,不会影响下一次循环的发送缓冲区,所以可以正常运行。

针对你补充的断点问题:
std::vector是模板类,成员函数默认会被内联优化,就算开启Debug模式,部分编译器也不会生成独立的resize函数符号,所以GDB和VSCode找不到对应的函数断点。你可以把断点打在async_write的回调函数里,打印send_data.size()就能验证上述结论,第一次回调后send_data.size()的值就是0。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 07:45:06