async_receive_from在Linux下的几个数据包后停止接收

时间:2012-08-10 10:36:36

标签: c++ linux udp boost-asio

我有一个设置,多个对等体每200毫秒(5fps)广播udp数据包(包含图像)。

虽然接收本地流作为外部流在Windows下工作正常,但相同的代码(Windows XP中的socket->cancel();除外,请参阅代码中的注释)在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秒时,会返回错误消息“取消操作”,但很难获得有关正在发生的事情的任何其他信息。

任何人都可以识别问题或给我一些提示,以获得有关出错的更多信息吗?任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:1)

好的,你正在做的是当你调用receive_with_timeout时,你正在设置两个异步请求(一个用于recv,一个用于超时)。当第一个完成时,您取消另一个。

但是,您再也不会再调用ioservice::run_one()来完成回调。取消boost :: asio中的操作时,它会调用处理程序,通常会显示一个错误代码,指示操作已被中止或取消。在这种情况下,我相信你有一个处理程序悬挂一旦你销毁截止日期服务,因为它有一个指向堆栈的指针,以便存储结果。

解决方案是再次调用run_one()以在退出函数之前处理取消的回调结果。您还应该检查传递给超时处理程序的错误代码,并且只在没有错误的情况下将其视为超时。

此外,如果您确实有超时,则需要执行run_one以便async_recv_from处理程序可以执行,并报告它已被取消。

答案 1 :(得分:1)

使用Xubuntu 12.04进行全新安装,而不是使用Ubuntu 10.04进行旧安装后,现在一切正常。也许是因为新的安装运行了一个更新的内核,可能改进了网络?无论如何,使用更新版本的发行版重新安装解决了我的问题。

如果其他人使用较旧的内核获得意外的网络行为,我建议在安装了较新内核的系统上进行尝试。