【问题标题】:Variable-size buffer for receiving UDP packets用于接收 UDP 数据包的可变大小缓冲区
【发布时间】:2013-12-06 08:53:29
【问题描述】:

我有一个 UDP 套接字,它将接收一些可能不同大小的数据包,我异步处理:

socket.async_receive_from(boost::asio::buffer(buffer, 65536), senderEndpoint, handler);

这里的问题是,为了处理不同的大小,我有一个很大的缓冲区,这可以通过可变大小的缓冲区来解决。

据我了解,在使用async_receive_from 时,一次只使用一个数据包调用处理程序,因为数据包边界保留在 UDP 中。那么,有没有办法为async_receive_from 提供一个空缓冲区,以便 Asio 可以增长以适应数据包大小

还请注意,我包装了数据包,因此对于传输到此套接字的每个数据包,前 4 个字节是数据包的长度。

【问题讨论】:

  • 为什么?只需指定您需要的最大缓冲区。 receive 方法会告诉你实际收到了多少字节。
  • 这个程序将在移动平台上运行,而不是在电脑上运行——所以它的内存占用应该是轻量级的。我认为有比分配大缓冲区更有效的方法。
  • 一次分配一个大缓冲区比分配许多较小的缓冲区要有效得多。
  • 您可以通过null_buffers 懒惰地分配适当大小的缓冲区。此外,包含长度的前 4 个字节可能是不必要的,因为available() 返回可供读取的数据报的大小,而receive() 将出列最多一个数据报。

标签: c++ boost udp buffer boost-asio


【解决方案1】:

要获得更准确的答案,这里有详细解释的代码。

首先,我们需要在不填充缓冲区的情况下调用接收处理程序。这是使用boost::asio::null_buffer() 完成的(如Tanner 所述,请参阅reactor-style operations 了解更多信息)。

void UDPConnection::receive()
{
    socket.async_receive(boost::asio::null_buffers(), receive_handler);
}

现在,当接收到一个数据包时,receive_handler 将被调用,没有任何缓冲区需要填充。

现在,对于处理程序:

void UDPConnection::handleReceive(const boost::system::error_code& error, unsigned int)
{
    // Standard check: avoid processing anything if operation was canceled
    // Usually happens when closing the socket
    if(error == boost::asio::error::operation_aborted)
        return;

使用socket.available(),我们可以获得要读取的字节数。 重要提示:这个数字不一定是数据包的大小!对于我的测试,这个数字总是大于数据包大小(即使是 8 kB 数据包,这是我的计算机可以处理的最大数据包)。

    // Prepare buffer
    unsigned int available = socket.available();
    unsigned char* buffer = new unsigned char[available];

神奇的地方发生了:“真正的”接收调用在这里完成,并且通常会很快,因为它只会填充缓冲区。此调用只会使一个数据包出列(即使在调用时套接字中有多个数据包)。返回值在这里很重要,因为可能只有一部分缓冲区已被填充。例如:available=50,packetSize=13

    // Fill it
    boost::asio::ip::udp::endpoint senderEndpoint;
    boost::system::error_code ec;
    unsigned int packetSize = socket.receive_from(boost::asio::buffer(buffer, available), senderEndpoint, 0, ec);

现在,只是标准的错误检查/处理/等等......

    if(ec)
    {
        // ...
    }

    // Process packet
    // ...
    receive();
}

【讨论】:

  • 这是否意味着您需要进行额外的数据复制,因为现在 Asio 需要将数据存储在内部缓冲区中,然后才将其复制到您的缓冲区中?您还可以为每个数据包动态分配缓冲区,而不是只分配一次,即使它是 8KB,它仍然不是那么大。
  • 大概就是这样。一旦我有时间,我可能会改进这个答案(并消除其中显示的内存泄漏!)
【解决方案2】:

您可以手动完成。 my_socket.available() 返回在套接字上排队等待读取的下一个 Udp 数据包的大小。所以你可以用它来检查下一个数据包有多大,相应地增加你的缓冲区然后接收它。但是,我同意评论者的观点,即这很可能比仅使用最大可能的大小作为缓冲区效率低。除非最大值可能比平均数据包大很多,并且您的应用通常根本不需要接收它。

但这是一个优化问题。您的问题的答案是 available() 并自己增加缓冲区。

编辑:我知道这在异步情况下不太理想,因为 available() 只有在调用 available() 时已经在等待时才返回下一个 udp 数据包的大小。

【讨论】:

  • 对于异步情况,reactor-style operations 提供了一个干净的解决方案来延迟分配适当大小的缓冲区。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-10
  • 1970-01-01
  • 1970-01-01
  • 2016-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多