Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

async_receive_from stops receiving after a few packets under Linux

I have a setup with multiple peers broadcasting udp packets (containing images) every 200ms (5fps).

While receiving both the local stream as external streams works fine under Windows, the same code (except for the socket->cancel(); in Windows XP, see comment in code) produces rather strange behavior under Linux:

  • The first few (5~7) packets sent by another machine (when this machine starts streaming) are received as expected;
  • After this, the packets from the other machine are received after irregular, long intervals (12s, 5s, 17s, ...) or get a time out (defined after 20 seconds). At certain moments, there is again a burst of (3~4) packets received as expected.
  • The packets sent by the machine itself are still being received as expected.

Using Wireshark, I see both local as external packets arriving as they should, with correct time intervals between consecutive packages. The behavior also presents itself when the local machine is only listening to a single other stream, with the local stream disabled.

This is some code from the receiver (with some updates as suggested below, thanks!):

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;
}

When the timer exceeds the 20 seconds, the error message "Operation canceled" is returned, but it is difficult to get any other information about what is going on.

Can anyone identify a problem or give me some hints to get some more information about what is going wrong? Any help is appreciated.

like image 762
P.D.P. Avatar asked Dec 14 '25 16:12

P.D.P.


1 Answers

Okay, what you're doing is that when you call receive_with_timeout, you're setting up the two asynchronous requests (one for the recv, one for the timeout). When the first one completes, you cancel the other.

However, you never invoke ioservice::run_one() again to allow it's callback to complete. When you cancel an operation in boost::asio, it invokes the handler, usually with an error code indicating that the operation has been aborted or canceled. In this case, I believe you have a handler dangling once you destroy the deadline service, since it has a pointer onto the stack for it to store the result.

The solution is to call run_one() again to process the canceled callback result prior to exiting the function. You should also check the error code being passed to your timeout handler, and only treat it as a timeout if there was no error.

Also, in the case where you do have a timeout, you need to execute run_one so that the async_recv_from handler can execute, and report that it was canceled.

like image 53
Dave S Avatar answered Dec 16 '25 08:12

Dave S



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!