为了得到更精确的答案,这里有一份详细解释的代码。
首先,我们需要调用接收处理程序而不填充缓冲区。这可以使用boost :: asio :: null_buffer()
来完成(有关更多信息,请参见Tanner所述的反应器式操作)。
void UDPConnection::receive()
{
socket.async_receive(boost::asio::null_buffers(), receive_handler);
}
现在,当收到数据包时,receive_handler
函数将被调用,而无需填充任何缓冲区。
现在,对于这个处理程序:
void UDPConnection::handleReceive(const boost::system::error_code& error, unsigned int)
{
if(error == boost::asio::error::operation_aborted)
return;
通过socket.available()
,我们可以获得要读取的字节数。重要提示:这个数字不一定是数据包的大小!对于我的测试来说,这个数字总是大于数据包的大小(即使是8 kB的数据包,这是我电脑能处理的最大值)。
// Prepare buffer
unsigned int available = socket.available();
unsigned char* buffer = new unsigned char[available];
这里是魔法发生的地方:真正的接收调用在这里完成,一般会很快,因为它只会填充缓冲区。即使在调用时套接字中有多个数据包,此调用也仅会出队一个数据包。这里返回值很重要,因为可能只有缓冲区的一部分被填充。例如: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);
现在,只需要进行标准的错误检查/处理/等等...
if(ec)
{
}
receive();
}
null_buffers
来惰性地分配适当大小的缓冲区。此外,包含长度的前4个字节可能是不必要的,因为available()
返回可供读取的数据报的大小,而receive()
最多只会出队一个数据报。 - Tanner Sansbury