[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でテストされました。



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



All Articles