Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading UDP socket twice discards remaining bytes after first call

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.

like image 647
Marco Avatar asked Mar 20 '13 12:03

Marco


1 Answers

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;
}
like image 99
Omnifarious Avatar answered Oct 18 '22 12:10

Omnifarious