Variable-size buffer for receiving UDP packets
boost, boost-asio, buffer, c++, udp
Solution
For a more precise answer, here is a detailed and explained code.
First, we need to call the receive handler without filling a buffer. This is done using `boost::asio::null_buffer()` (see reactor-style operations for more information, as stated by Tanner).
void UDPConnection::receive()
{
socket.async_receive(boost::asio::null_buffers(), receive_handler);
}
Now, when a packet will be received, `receive_handler` will be called without any buffer to be filled.
Now, for the 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;
With `socket.available()`, we can get the number of bytes to be read. Important: this number is not necessarily the size of the packet! For my tests, this number was always greater than the packet size (even with 8 kB packets, which was the largest my computer could handle).
// Prepare buffer
unsigned int available = socket.available();
unsigned char* buffer = new unsigned char[available];
Here the magic happens: the "real" receive call is done here, and will normally be fast, since it will just fill a buffer. This call will only dequeue one packet (even if at the time of the call there was more than one in the socket). The return value is important here, as only a part of the buffer may have been filled. Ex: 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);
Now, just standard error-checking / processing / etc...
if(ec)
{
// ...
}
// Process packet
// ...
receive();
}
Problem
I have an UDP socket that will receive some packets, of potentially different sizes, and I handle this asynchronously: ``` socket.async_receive_from(boost::asio::buffer(buffer, 65536), senderEndpoint, handler); ``` The problem here is that to handle the different sizes I have a big buffer, something that could be addressed with variable size buffers. To my understanding, when using `async_receive_from`, the handler is called with only one packet at a time, because the packet boundaries are preserved in UDP. So, is there a way to give an empty buffer to `async_receive_from` that Asio will grow to fit the packet size ? Also note that I wrap packets, so for every packet transiting to this socket, the 4 first bytes are the length of the packet.