Reading UDP socket twice discards remaining bytes after first call

boost-asio, c++, udp

Solution

UDP is a packet oriented protocol. You have to read an entire packet at a time.

The technical term for this is 'message boundaries preserved'. Which means that it will send all the data from a single `write` or `send` operation in one chunk. And when you `read` or `recv` it will pick up the data from a particular chunk then discard that chunk never to be seen again.

This is by design.

You should decide on a maximum packet length in your protocol, then always read that length. If you write less than that length, you will read fewer bytes than you ask for.

Also, keep in mind that there is no guarantee in UDP that any given chunk will arrive. So you may not have as many reads as writes if you're counting on both sides.

Something like this:

#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;
}

Problem

My goal is to read data from a UDP socket in two steps. The problem is that if I write more data to the socket than is read in the first step. The result is that the remaining data vanishes. I reduced my code to the following snippet: ``` #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; } ``` When I write data to the socket as follows: ``` echo '0123456789' | nc -u localhost 1234 ``` The program outputs the first two bytes `01` and then it blocks. Instead, I expected the output: ``` 01 23 ``` However, it blocks at the second `receive()` call. According to the manual: The call will block until one of the following conditions is true: ∙ The supplied buffers are full. […] ∙ An error occurred. Why is the buffer empty? If I launch ``` echo '0123456789' | nc -u localhost 1234 ``` a second time, the `receive()` call unblocks, but outputs `01` as well. Where did the remaining data `23456789` go that I input the first time and how can I access it in a subsequent `receive()` call? Some background: The use case is reading a variable length packet, which means reading the header first, and then after the header is processed (which includes information about the packet length) continue reading the payload.

Original source