[asio :: udp]非クロスプラットフォームの動作

したがって、状況を想像してください。UDP経由でデータを受信するクロスプラットフォームサーバーがあります。 Asioを使用して、ソケットを作成し、受信したデータ用のバッファーを作成して、リスニングを開始します。

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); 


受信したデータグラムに、バッファに割り当てたよりも多くのデータがある場合はどうなりますか?

WIN / LINUXプラットフォームでは動作不一致があります。 Linuxでは、操作はスムーズに進み、要求されたとおりに正確に読み取られ、残りは破棄されます。 Winでは、読み取りデータも取得しますが、その後、例外も取得します。

これが起こる理由は次のとおりです。

receive_from関数の関数は、次のコードの実行に限定されます

 #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 for Winおよびrecvmsg for Linuxは、ソケットからデータを受信するために使用されます。 関数の結果(成功/失敗)は、次の関数によって決定されます。

 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; } 


WSARecvFrom関数の説明から、エラーステータスが返されることがわかります。

エラーが発生せず、受信操作がすぐに完了した場合、WSARecvFromはゼロを返します。 それ以外の場合、SOCKET_ERRORの値が返され、WSAGetLastErrorを呼び出すことで特定のエラーコードを取得できます。


その1つは次のとおりです。

WSAEMSGSIZE
メッセージが指定されたバッファーに対して大きすぎたため、(信頼性の低いプロトコルのみ)バッファーに収まらなかったメッセージの後続部分はすべて破棄されました。


recvファミリー関数は、正常に読み取られたバイト数を返します。エラーの場合は-1、errnoにステータスが返されます。 しかし、彼女の間違いのなかには、上記のようなものはありません。 Linuxは間違いではないことがわかりましたか? あまり好きではありません。 エラーコードは、関数に渡されるrecvmsg関数のmsg_flagsフィールドに書き込まれます(つまり、asioで使用されます)。

MSG_TRUNC
指定されたバッファよりも大きい場合でも、実際のパケット長を返します。 このフラグは、パケットプロトコルでのみ使用できます。


したがって、Winでの同じ操作はエラーと見なされ、Linuxでは正しいと見なされます。 これは明らかにasioに考慮されていません。

続く困難の1つは、データグラムがすべて読み込まれなかったことをWinで判断できないことです。

この問題の可能な解決策は、 利用可能な機能を使用することです 。 しかし、ここでも、さらに恐ろしい不一致が生じます。 Winプラットフォーム上のこの関数は、UDPソケット内のデータの総量(すべてのデータグラムのデータの合計)を返します。Linuxプラットフォームでは、サイズはデータグラムの最初(次の読み取りで処理されるもの)のみです。

そして、上記のすべての完全な例は次のとおりです。

ソース

出力勝利:

例外:receive_from:データグラムソケットに送信されたメッセージが内部メッセージバッファよりも大きいか、別のネットワークパラメータを超えました。 また、メッセージを受信するためのバッファがメッセージのサイズよりも小さい可能性があります。
いくつかのテスト
利用可能:36
利用可能:18
利用可能:0

出力Linux:

いくつかのテスト
利用可能:18
利用可能:18
利用可能:0


Boostバージョン1.44および1.49でテストされました。

ご清聴ありがとうございました。

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


All Articles