簡體   English   中英

async_receive_from在Linux下的幾個數據包后停止接收

[英]async_receive_from stops receiving after a few packets under Linux

我有一個設置,多個對等體每200毫秒(5fps)廣播udp數據包(包含圖像)。

雖然接收本地流作為外部流在Windows下工作正常,但相同的代碼(在Windows XP中, socket->cancel();請參閱代碼中的注釋)在Linux下產生相當奇怪的行為:

  • 其他機器發送的前幾個(5~7)數據包(當本機開始流式傳輸時)按預期接收;
  • 在此之后,來自另一台機器的數據包在不規則的長間隔(12s,5s,17s,......)之后被接收或者獲得超時(在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秒時,將返回錯誤消息“取消操作”,但很難獲得有關正在發生的事情的任何其他信息。

任何人都可以識別問題或給我一些提示,以獲得有關出錯的更多信息嗎? 任何幫助表示贊賞。

好的,你正在做的是當你調用receive_with_timeout ,你正在設置兩個異步請求(一個用於recv,一個用於超時)。 當第一個完成時,您取消另一個。

但是,您永遠不會再次調用ioservice::run_one()以允許它的回調完成。 取消boost :: asio中的操作時,它會調用處理程序,通常會顯示一個錯誤代碼,指示操作已被中止或取消。 在這種情況下,我相信你有一個處理程序懸掛一旦你銷毀截止日期服務,因為它有一個指向堆棧的指針,以便存儲結果。

解決方案是再次調用run_one()以在退出函數之前處理已取消的回調結果。 您還應該檢查傳遞給超時處理程序的錯誤代碼,並且只在沒有錯誤時將其視為超時。

此外,在您確實有超時的情況下,您需要執行run_one以便async_recv_from處理程序可以執行,並報告它已被取消。

使用Xubuntu 12.04進行全新安裝而不是使用Ubuntu 10.04進行舊安裝后,現在一切都按預期工作。 也許是因為新的安裝運行了一個更新的內核,可能改進了網絡? 無論如何,使用更新版本的發行版重新安裝解決了我的問題。

如果其他人使用較舊的內核獲得意外的網絡行為,我建議在安裝了較新內核的系統上進行嘗試。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM