【发布时间】:2017-05-09 07:38:50
【问题描述】:
我将 boost::asio 用于需要从服务器接收可变长度消息的客户端(Windows 10、Visual C++)。 消息非常频繁(每秒超过 10 条消息),每条消息大约 40-100 字节。
我以这种方式使用streambuf 和async_read_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));
}
即我正在尝试使用_inbox.prepare(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
如您所见,在 09:02:25 之前一切正常,然后数据开始累积,readHandler 很少被调用(每次调用之间 7-8 秒)并带有大量数据(65536 字节)。
最后,远程主机断开连接。断开连接是由于服务器向我的客户端发送的 TCP ZeroWindow Probes(使用 Wireshark 跟踪),即我的 TCP 缓冲区已满。
我真的不明白为什么readHandler 被如此不频繁地调用并且有这么多数据(我确信这不是客户端 100% CPU 的问题:客户端处理消息的速度很快CPU负载小).
编辑:
我正在使用以下代码在套接字上禁用 Nagle 算法:
boost::system::error_code error;
_socket->set_option(tcp::no_delay(true), error);
试图阻止 TCP/IP 堆栈对数据包进行分组,但没有帮助。
编辑 2:
我的处理代码中似乎存在瓶颈,因此我实际上接收数据的速度不够快,并且服务器的 Nagle 算法产生了下面 R. Joiny 描述的问题。
【问题讨论】:
-
你能识别 Wireshark 中每个发送的以太网包吗?也许发送网络缓冲区决定将它们放在一个以太网包中,因此您的读取处理程序被调用的频率较低,但数据更多。我不确定,因为这通常只会以非常高的发送率出现,但检查一下不会有伤害
-
由于路径 MTU、以太网等原因,您永远不会获得大于 1500 字节的入站 TCP 段。
-
@R.Joiny 65536 字节不能在单个以太网数据包中,EJP 是对的。其他地方肯定有问题。在任何情况下,我都会尝试更好地分析 Wireshark 跟踪并在发现任何相关内容时更新问题。
-
当速度从 100M 变为 1G 时,以太网没有移动到巨型帧 (9kb) 吗?那就是忽略本地主机连接。
标签: c++ boost boost-asio