receiving partial UDP packets from mio::udp::UdpSocket.recv

842 Views Asked by At

I'm using mio::udp::UdpSocket to receive a response to a request from a client. It looks like I'm getting partial UDP packets on the triggered event. I'm not sure if this is a bug in the mio library or not.

I've tried PollOpt::level(), all(), empty(), edge(), etc. I think I generally want level() based on the poll() docs, but none of them work. By adding a sleep of 20 ms I get the full packets.

As reference, when using the blocking std::net::UdpSocket I see no issues. Honestly, if the std::net::SocketOpts was stable I'd just use that. The intention of using mio is to gain timeout on the socket, it looks like net2 is going to replace std::net, but even net2 doesn't have timeout on recv.

Here's the code for the eventloop:

sleep_ms(20);

let mut event_loop: EventLoop<Response> = try!(EventLoop::new());

if event_loop.timeout_ms((), 5000).is_err() { return Err(ClientError::TimerError) };
try!(event_loop.register_opt(&self.socket, RESPONSE, EventSet::readable(), PollOpt::all()));

let mut response: Response = Response::new(&self.socket);

try!(event_loop.run_once(&mut response));

Here's the code for the Handler:

fn ready(&mut self, _: &mut EventLoop<Self>, token: Token, events: EventSet) {
  match token {
    RESPONSE => {
      if !events.is_readable() {
        debug!("got woken up, but not readable: {:?}", token);
        return
      }

      let recv_result = self.socket.recv_from(&mut self.buf);
      if recv_result.is_err() {
        // debug b/c we're returning the error explicitly
        debug!("could not recv_from on {:?}: {:?}", self.socket, recv_result);
        self.error = Some(recv_result.unwrap_err().into());
        return
      }

      if recv_result.as_ref().unwrap().is_none() {
        // debug b/c we're returning the error explicitly
        debug!("no return address on recv_from: {:?}", self.socket);
        self.error = Some(ClientError::NoAddress);
        return
      }

      let addr = Some(recv_result.unwrap().unwrap());
      debug!("bytes: {:?} from: {:?}", self.buf.len(), addr);
    },
    _ => error!("unrecognized token: {:?}", token),
  }
}
1

There are 1 best solutions below

0
On

Just to follow up, the bug in the above logic, is that run_once() runs for a tick, not for one 'event', which was a bad assumption (though to be fair, the interface is currently not documented well).

In any case, this is not a partial packet issue, it's an issue of the packet not being delivered before the run_once() logic ran, didn't see anything on the socket and immediately returned.

I've changed my handler to do an event_loop.shutdown() after receiving the packet and using run() instead of run_once().