5

私の目標は、2 つのステップで UDP ソケットからデータを読み取ることです。問題は、最初のステップで読み取られるよりも多くのデータをソケットに書き込む場合です。その結果、残りのデータは消失します。

コードを次のスニペットに減らしました。

#include <boost/asio/ip/udp.hpp>
using namespace boost::asio;

int main() {
  io_service net_io;
  ip::udp::socket net_sock( net_io, ip::udp::endpoint( ip::udp::v4(), 1234 ) );

  uint8_t data[2];

  net_sock.receive( buffer( data, 2 ) );
  std::cout << data[0] << data[1] << std::endl;

  net_sock.receive( buffer( data, 2 ) );
  std::cout << data[0] << data[1] << std::endl;


  net_sock.close();
  return EXIT_SUCCESS;
}

次のようにソケットにデータを書き込むと:

echo '0123456789' | nc -u localhost 1234

プログラムは最初の 2 バイト01を出力してからブロックします。代わりに、次の出力が期待されました。

01
23

ただし、2 回目のreceive()呼び出しでブロックされます。マニュアルによると :

次の条件のいずれかが true になるまで、呼び出しはブロックされます。

∙ 提供されたバッファがいっぱいです。[…]
∙ エラーが発生しました。

バッファが空なのはなぜですか? 立ち上げたら

echo '0123456789' | nc -u localhost 1234

2 回目は、receive()呼び出しのブロックが解除されますが、同様に出力01されます。最初に入力した残りのデータはどこ23456789にあり、その後の receive()呼び出しでどのようにアクセスできますか?

背景: 使用例は可変長パケットの読み取りです。つまり、最初にヘッダーを読み取り、次にヘッダーが処理された後 (パケット長に関する情報を含む)、ペイロードの読み取りを続けます。

4

1 に答える 1

6

UDP はパケット指向のプロトコルです。一度にパケット全体を読み取る必要があります。

これの専門用語は、「メッセージ境界の保持」です。つまり、 1 つのwriteまたはsend操作からのすべてのデータが 1 つのチャンクで送信されます。そして、特定のチャンクからデータを取得するときはreadrecvそのチャンクを破棄して、二度と見られないようにします。

これは仕様によるものです。

プロトコルの最大パケット長を決定し、常にその長さを読み取る必要があります。その長さよりも短い書き込みを行うと、必要なバイト数よりも少ないバイト数を読み取ることになります。

また、UDP では、特定のチャンクが到着するという保証はないことに注意してください。そのため、両側でカウントしている場合、書き込みほど多くの読み取りがない場合があります。

このようなもの:

#include <boost/asio/ip/udp.hpp>
using namespace boost::asio;

const unsigned int max_datagram_size = 65536;

int main() {
  io_service net_io;
  ip::udp::socket net_sock( net_io, ip::udp::endpoint( ip::udp::v4(), 1234 ) );

  uint8_t data[max_datagram_size];

  // I like declaring all values that I do not expect to change as const.
  const int recved_size = net_sock.receive( buffer( data, max_datagram_size ) );
  if (recved_size >= 0) {
    std::cout << ::std::string(data, recved_size) << '\n';
  } else {
    std::cout << "There was some sort of error receiving data.\n";
  }


  net_sock.close();
  return EXIT_SUCCESS;
}
于 2013-03-20T12:43:18.267 に答える