首次调用后,读取UDP套接字会丢弃剩余字节

时间:2013-03-20 12:41:50

标签: c++ udp boost-asio

我的目标是分两步从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()来电时阻止。根据 manual

  

调用将阻塞,直到满足下列条件之一:

     

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

为什么缓冲区为空?如果我发布

echo '0123456789' | nc -u localhost 1234

第二次,receive()调用取消阻止,但也输出01。哪里 剩下的数据23456789是我第一次输入的,我怎么能在后续访问它 receive()致电?

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

1 个答案:

答案 0 :(得分: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;
}