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();
}
null_buffers
. Also, the first 4 bytes containing the length is likely unnecessary, asavailable()
returns the size of the datagram available for reading, andreceive()
will dequeue a max of one datagram. – Tanner Sansbury