3

200ms(5fps)ごとにudpパケット(イメージを含む)をブロードキャストする複数のピアを使用するセットアップがあります。

外部ストリームとして両方のローカルストリームを受信することはWindowsで正常に機能しますが、同じコード(socket->cancel();Windows XPを除く、コードのコメントを参照)は、Linuxではかなり奇妙な動作を生成します。

  • 別のマシンによって送信された最初の数(5〜7)パケット(このマシンがストリーミングを開始したとき)は、期待どおりに受信されます。
  • この後、他のマシンからのパケットは、不規則で長い間隔(12秒、5秒、17秒など)の後に受信されるか、タイムアウト(20秒後に定義)を取得します。ある瞬間に、期待どおりに(3〜4)パケットのバーストが再び受信されます。
  • マシン自体によって送信されたパケットは、まだ期待どおりに受信されています。

Wiresharkを使用すると、ローカルと外部の両方のパケットが適切に到着し、連続するパッケージ間の時間間隔が正しいことがわかります。この動作は、ローカルマシンが他の単一のストリームのみをリッスンしていて、ローカルストリームが無効になっている場合にも発生します。

これは受信者からのコードです(以下に提案されているようにいくつかの更新があります、ありがとう!):

Receiver::Receiver(port p)
{
  this->port = p;
  this->stop = false;
}

int Receiver::run()
{
  io_service io_service;
  boost::asio::ip::udp::socket socket(
    io_service,
    boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(),
    this->port));
  while(!stop)
  {
    const int bufflength = 65000;
    int timeout = 20000;
    char sockdata[bufflength];
    boost::asio::ip::udp::endpoint remote_endpoint;
    int rcvd;

    bool read_success = this->receive_with_timeout(
           sockdata, bufflength, &rcvd, &socket, remote_endpoint, timeout);

    if(read_success)
    {
      std::cout << "read succes " << remote_endpoint.address().to_string() << std::endl;
    }
    else
    {
      std::cout << "read fail" << std::endl;
    }
  }
  return 0;
}

void handle_receive_from(
  bool* toset, boost::system::error_code error, size_t length, int* outsize)
{
  if(!error || error == boost::asio::error::message_size)
  {
    *toset = length>0?true:false;
    *outsize = length;
  }
  else
  {
    std::cout << error.message() << std::endl;
  }
}

// Update: error check
void handle_timeout( bool* toset, boost::system::error_code error)
{
  if(!error)
  {
    *toset = true;
  }
  else
  {
    std::cout << error.message() << std::endl;
  }
}

bool Receiver::receive_with_timeout(
  char* data, int buffl, int* outsize,
  boost::asio::ip::udp::socket *socket,
  boost::asio::ip::udp::endpoint &sender_endpoint, int msec_tout)
{
  bool timer_overflow = false;
  bool read_result = false;

  deadline_timer timer( socket->get_io_service() );

  timer.expires_from_now( boost::posix_time::milliseconds(msec_tout) );
  timer.async_wait( boost::bind(&handle_timeout, &timer_overflow,
    boost::asio::placeholders::error) );

  socket->async_receive_from(
    boost::asio::buffer(data, buffl), sender_endpoint,
    boost::bind(&handle_receive_from, &read_result,
    boost::asio::placeholders::error,
    boost::asio::placeholders::bytes_transferred, outsize));

  socket->get_io_service().reset();

  while ( socket->get_io_service().run_one())
  {
    if ( read_result )
    {
      timer.cancel();
    }
    else if ( timer_overflow )
    {
      //not to be used on Windows XP, Windows Server 2003, or earlier
      socket->cancel();
      // Update: added run_one()
      socket->get_io_service().run_one();
    }
  }
  // Update: added run_one()
  socket->get_io_service().run_one();
  return read_result;
}

タイマーが20秒を超えると、「操作がキャンセルされました」というエラーメッセージが返されますが、他に何が起こっているかについての情報を取得することは困難です。

誰かが問題を特定したり、何が問題になっているのかについてさらに情報を得るためのヒントを教えてもらえますか?どんな助けでも大歓迎です。

4

2 に答える 2

1

さて、あなたがしていることは、あなたが呼び出すときreceive_with_timeout、あなたは2つの非同期要求を設定しているということです(1つはrecv用、もう1つはタイムアウト用)。最初の1つが完了すると、もう1つをキャンセルします。

ioservice::run_one()ただし、コールバックを完了するために再度呼び出すことはありません。boost :: asioで操作をキャンセルすると、ハンドラーが呼び出されます。通常、操作が中止またはキャンセルされたことを示すエラーコードが表示されます。この場合、期限サービスを破棄するとハンドラーがぶら下がっていると思います。これは、結果を格納するためのポインターがスタックにあるためです。

解決策は、関数を終了する前に、run_one()を再度呼び出して、キャンセルされたコールバックの結果を処理することです。また、タイムアウトハンドラーに渡されるエラーコードを確認し、エラーがなかった場合にのみタイムアウトとして扱う必要があります。

また、タイムアウトがある場合はrun_oneasync_recv_fromハンドラーが実行できるように実行し、キャンセルされたことを報告する必要があります。

于 2012-08-10T14:15:40.600 に答える
1

Ubuntu10.04での古いインストールの代わりにXubuntu12.04でクリーンインストールした後、すべてが期待どおりに機能するようになりました。おそらくそれは、新しいインストールが新しいカーネルを実行しているためであり、おそらくネットワークが改善されているのでしょうか?とにかく、ディストリビューションの新しいバージョンで再インストールすると、私の問題は解決しました。

古いカーネルで予期しないネットワーク動作が発生した場合は、新しいカーネルがインストールされているシステムで試してみることをお勧めします。

于 2012-08-10T23:47:47.293 に答える