📜 ⬆️ ⬇️

[asio :: udp] Non-cross-platform behavior

So imagine the situation: we have a cross-platform server that should receive data via UDP. Armed with Asio, you create a socket, create a buffer for the received data, and start listening.

udp::socket receiver(ios, udp::endpoint(udp::v4(), port)); char read_buf[buf_len]; udp::endpoint sender_point; receiver.receive_from(buffer(read_buf, sizeof(read_buf)), sender_point); 


What happens if the received datagram has more data than you allocated for the buffer?

There will be a mismatch of behavior on WIN / LINUX platforms. On Linux, the operation will go smoothly, it will be read as much as requested, the rest will be discarded. On Win, you also get the read data, but afterwards you will also get an exception.
')
And this is why this will happen:

The function of the receive_from function comes down to executing the following code

 #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) int result = error_wrapper(::WSARecvFrom(s, bufs, recv_buf_count, &bytes_transferred, &recv_flags, addr, &tmp_addrlen, 0, 0), ec); *addrlen = (std::size_t)tmp_addrlen; ... if (result != 0) return socket_error_retval; ec = boost::system::error_code(); return bytes_transferred; #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) msghdr msg = msghdr(); init_msghdr_msg_name(msg.msg_name, addr); msg.msg_namelen = *addrlen; msg.msg_iov = bufs; msg.msg_iovlen = count; int result = error_wrapper(::recvmsg(s, &msg, flags), ec); *addrlen = msg.msg_namelen; if (result >= 0) ec = boost::system::error_code(); return result; 


WSARecvFrom functions for Win and recvmsg for Linux are used to get data from the socket. The result of the function (successful / not successful) is determined by the following function.

 template <typename ReturnType> inline ReturnType error_wrapper(ReturnType return_value, boost::system::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) ec = boost::system::error_code(WSAGetLastError(), boost::asio::error::get_system_category()); #else ec = boost::system::error_code(errno, boost::asio::error::get_system_category()); #endif return return_value; } 


From the description of the function WSARecvFrom it follows that it returns the error status:

If no error occurs, WSARecvFrom returns zero. Otherwise, it can be retrieved by WSAGetLastError.


One of which is just:

WSAEMSGSIZE
This has been the case.


The functions of the recv family return the number of bytes read successfully, or -1 in the case of an error and the status in errno. But among her mistakes there is no similar to the above. It turns out for Linux is not an error? Not certainly in that way. In the msvdr structure passed to the recvmsg function (namely, it is used in asio), the error code is written in the msg_flags field:

MSG_TRUNC
Returns the actual packet length, even if it was larger than the buffer provided. This flag can only be used with packet protocols.


Thus, one and the same operation in Win is regarded as erroneous, and on Linux as correct. What is clearly not considered asio.

One of the resulting difficulties is the impossibility under Win to determine that the datagram was not read all.

A possible solution to this problem would be to use the available function. But here we get even more frightening non-conformity. This function on Win platforms returns the total number of data in a UDP socket (the sum of the data of all datagrams), and on Linux platforms, the size is only the first (the one that will be processed during the next reading) of the datagram.

And here is a complete example of all of the above:

source .

Output win:

Exception: receive_from: The message sent to the datagram socket was larger than the internal message buffer or a different network parameter was exceeded. It is also possible that the buffer for receiving the message was smaller than the size of the message.
Some test
Available: 36
Available: 18
Available: 0

output linux:

Some test
Available: 18
Available: 18
Available: 0


Tested on boost versions 1.44 and 1.49.

Thanks for attention.

Source: https://habr.com/ru/post/139452/


All Articles