5

我的目标是分两步从 UDP 套接字读取数据。问题是,如果我向套接字写入的数据多于第一步读取的数据。结果是剩余的数据消失了。

我将代码简化为以下代码段:

#include <boost/asio/ip/udp.hpp>
using namespace boost::asio;

int main() {
  io_service net_io;
  ip::udp::socket net_sock( net_io, ip::udp::endpoint( ip::udp::v4(), 1234 ) );

  uint8_t data[2];

  net_sock.receive( buffer( data, 2 ) );
  std::cout << data[0] << data[1] << std::endl;

  net_sock.receive( buffer( data, 2 ) );
  std::cout << data[0] << data[1] << std::endl;


  net_sock.close();
  return EXIT_SUCCESS;
}

当我将数据写入套接字时,如下所示:

echo '0123456789' | nc -u localhost 1234

程序输出前两个字节01,然后阻塞。相反,我期望输出:

01
23

但是,它在第​​二次receive()调用时阻塞。根据 手册

调用将阻塞,直到以下条件之一为真:

∙ 提供的缓冲区已满。[…]
∙ 发生错误。

为什么缓冲区是空的?如果我启动

echo '0123456789' | nc -u localhost 1234

第二次,receive()呼叫解除阻塞,但也输出01。我第一次输入的剩余数据23456789去了哪里,如何在后续 receive()调用中访问它?

一些背景:用例是读取可变长度的数据包,这意味着首先读取标头,然后在标头处理后(包括有关数据包长度的信息)继续读取有效负载。

4

1 回答 1

6

UDP 是一种面向数据包的协议。您必须一次读取整个数据包。

对此的技术术语是“保留消息边界”。这意味着它将发送来自单个writesend一个块中的操作的所有数据。当你readrecv它会从一个特定的块中获取数据时,然后丢弃那个块,再也不会被看到。

这是设计使然。

您应该确定协议中的最大数据包长度,然后始终读取该长度。如果您写入的长度小于该长度,您将读取的字节数将少于您要求的字节数。

另外,请记住,UDP 不能保证任何给定的块都会到达。因此,如果您同时计算双方,您的读取次数可能少于写入次数。

像这样的东西:

#include <boost/asio/ip/udp.hpp>
using namespace boost::asio;

const unsigned int max_datagram_size = 65536;

int main() {
  io_service net_io;
  ip::udp::socket net_sock( net_io, ip::udp::endpoint( ip::udp::v4(), 1234 ) );

  uint8_t data[max_datagram_size];

  // I like declaring all values that I do not expect to change as const.
  const int recved_size = net_sock.receive( buffer( data, max_datagram_size ) );
  if (recved_size >= 0) {
    std::cout << ::std::string(data, recved_size) << '\n';
  } else {
    std::cout << "There was some sort of error receiving data.\n";
  }


  net_sock.close();
  return EXIT_SUCCESS;
}
于 2013-03-20T12:43:18.267 回答