C++ Windows中boost::asio的最佳缓冲区大小

C++ Windows中boost::asio的最佳缓冲区大小,c++,boost,boost-asio,C++,Boost,Boost Asio,我将boost::asio用于需要从服务器接收可变长度消息的客户端(Windows 10,Visual C++)。 消息非常频繁(每秒超过10条消息),每条消息大约有40-100字节 我使用streambuf和async\u read\u some的方式如下: void Client::readStart(void) { boost::asio::streambuf::mutable_buffers_type buf = _inbox.prepare(std::max((size_t)1

我将boost::asio用于需要从服务器接收可变长度消息的客户端(Windows 10,Visual C++)。 消息非常频繁(每秒超过10条消息),每条消息大约有40-100字节

我使用
streambuf
async\u read\u some
的方式如下:

void Client::readStart(void)
{
    boost::asio::streambuf::mutable_buffers_type buf = _inbox.prepare(std::max((size_t)1024, _socket->available()));

    // Start an asynchronous read and call readHandler when it completes or fails
    _socket->async_read_some(buf,
        boost::bind(&Client::readHandler,
        this,
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));
}
i、 e.我正在尝试使用收件箱动态调整缓冲区大小。准备(std::max((size_t)1024,_socket->available())以便在由于客户端仍在处理以前的邮件而累积了许多邮件时使用更大的缓冲区

我发现我不能总是像inbox.prepare(262144)那样使用更大的缓冲区,因为
readHandler
会被调用大量数据,而不是更频繁地调用

即使尝试动态缓冲区分配,我也会遇到奇怪的延迟和数据积累

这是我的日志:

  2017-05-09 09:02:25 <debug> Received 1024 bytes
  2017-05-09 09:02:25 <debug> Received 372 bytes
  2017-05-09 09:02:25 <debug> Received 844 bytes
  2017-05-09 09:02:25 <debug> Received 169 bytes
  2017-05-09 09:02:25 <debug> Received 1024 bytes
  2017-05-09 09:02:25 <debug> Received 379 bytes
  2017-05-09 09:02:25 <debug> Received 1385 bytes
  2017-05-09 09:02:25 <debug> Received 1421 bytes
  2017-05-09 09:02:25 <debug> Received 108 bytes
  2017-05-09 09:02:25 <debug> Received 1024 bytes
  2017-05-09 09:02:25 <debug> Received 1768 bytes
  2017-05-09 09:02:27 <debug> Received 65536 bytes
  2017-05-09 09:02:33 <debug> Received 65536 bytes
  2017-05-09 09:02:40 <debug> Received 65536 bytes
  2017-05-09 09:02:47 <debug> Received 65536 bytes
  2017-05-09 09:02:55 <debug> Received 65536 bytes
  2017-05-09 09:03:01 <debug> Received 65536 bytes
  2017-05-09 09:03:07 <debug> Received 65536 bytes
  2017-05-09 09:03:15 <debug> Received 65536 bytes
  2017-05-09 09:03:35 <debug> Received 65536 bytes
  2017-05-09 09:03:41 <debug> Received 65536 bytes
  2017-05-09 09:03:46 <debug> Received 65536 bytes
  2017-05-09 09:03:50 <debug> Received 65536 bytes
  2017-05-09 09:03:58 <debug> Received 65536 bytes
  2017-05-09 09:04:02 <debug> Received 65536 bytes
  2017-05-09 09:04:11 <info> Disconnected by remote host
试图阻止TCP/IP堆栈对数据包进行分组,但没有帮助

编辑2:


在我的处理代码中似乎有一个瓶颈,所以我实际上接收数据的速度不够快,服务器的Nagle算法产生了下面R.Joiny所描述的问题。

我写了一条注释,太长了,所以我决定回答,虽然我不是100%确定,但99%确定

@MSalters在这方面有一点(尽管即使是巨型帧也远小于64K)。TCP包的大小可以精确到64K,这显然显示在您的日志中。以太网MTU也不影响tcp包大小,因为如果套接字决定将所有tcp包打包成一个最大64K大小的包,当然它会通过多个以太网包发送,但接收套接字在接收最后一个以太网包后完成1个tcp包

这是评论。我想说的是:

服务器快速发送数据=服务器程序快速写入套接字缓冲区

  • 然后,套接字决定使用计时器等待更多数据,如果数据在计时器处于活动状态时出现,则将数据添加到传出的tcp包中。由于发送速度非常快,因此tcp包的最大大小几乎总是64K
  • 现在,套接字发送包,因此操作系统将其拆分为MTU大小的部分
操作系统正在将大于MTU的数据包传递给网络适配器,网络适配器驱动程序正在分解这些数据包,以便它们适合MTU。(来源:)

  • 然后,接收套接字获取所有这些以太网包,但看到它们都是一个TCP包,并等待直到接收到最后一个以太网包
  • 它构建所有小型以太网包的tcp包,并将其写入接收缓冲区,该缓冲区
  • …唤醒异步读取处理程序
这可能是


问题的解决方案:
  • 如果您有权访问服务器的代码,请使用其套接字进行编辑(Nagle)

  • 如果没有,您必须定义一种带有结束标志字节或类似内容的协议,以便知道每个小包的结束位置。(您仍然需要访问服务器:D)

  • 连接关闭的错误是客户端清空缓冲区速度不够快的问题。但这两种情况都会发生,因为随时间发送的数据x始终是相同的。(10倍~100字节/秒或1倍10000字节/10秒相同)


编辑:
我建议使用线程安全循环缓冲区之类的方法,将数据写回tcp_客户端线程,并将其弹出到主线程中,以计算数据。有了这种结构,我曾经能够接收500字节的数据并保存到一个csv中,该csv在1ms内发送给我。我在BeagleBoneBlack上用ArchLinux和我的(也用boost/asio实现的)应用程序托管tcp服务器完成了所有这些工作。

你能识别Wireshark中发送的每个以太网包吗?可能发送网络缓冲区决定将它们放在一个以太网包中,因此读取处理程序的调用频率较低,但数据较多。我不确定,因为这通常只在非常高的发送速率下出现,但为了检查这一点,您永远不会得到大于1500字节的入站TCP段,因为路径MTU、以太网等。@R.Joiny 65536字节不能在单个以太网数据包中,EJP是正确的。其他地方一定有问题。在任何情况下,我都会尝试更好地分析Wireshark跟踪,如果发现任何相关信息,我会更新问题。当速度从100M提高到1G时,以太网没有移到巨型帧(9kb)吗?这就是忽略本地主机连接。谢谢你的回答,它似乎和你描述的完全一样。不幸的是,我无法访问服务器代码,但我在代码中也发现了一个问题:我认为它处理数据的速度足够快,而我自己的代码中肯定存在瓶颈,这可能就是服务器的Nagle算法将所有数据包塞进一起的原因:因为我没有足够快地确认数据包。因此,即使我无法访问服务器的代码,如果我能够消除瓶颈,它可能会阻止服务器对数据包进行分组。如果您的TCP Ack向服务器发送消息,说明他开始分组的缓冲区太小。我根据我的项目经验编辑了我的答案,我曾经做过一次。也许会有帮助:)谢谢。同时,我能够缓解瓶颈,这是由于将接收到的消息存储到Sqlite数据库中,现在我再也无法接收完整的65k缓冲区,服务器也不再断开我的连接,因此现在我可以尝试继续使用单线程异步模型(您的双线程模型,一个线程用于接收,一个线程用于计算,对我来说更难“正确”地编写和维护,所以我更喜欢
boost::system::error_code error;
_socket->set_option(tcp::no_delay(true), error);