1

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),
  }
}
bluejekyll
  • 155
  • 2
  • 8
  • 3
    I would suggest 100% confirming that all the data you are trying to get is sent in a single datagram by analyzing the UDP packets on the wire. – David Schwartz Sep 15 '15 at 07:49
  • Thanks, I'll double check, but the size we're talking about is 129 bytes. This is way under standard MTUs. – bluejekyll Sep 15 '15 at 15:46
  • The MTU has nothing to do with anything. That just determines how datagrams are split into packets. I asked you to confirm it's sent in a single *datagram*. That will be determined by how the sending software works because datagrams are application-level things. Yes, how datagrams are mapped to packets is up to UDP, but UDP makes that invisible to the application. – David Schwartz Sep 15 '15 at 17:13
  • So, after TCPDumping a lot, I think I have a different problem going on, where my test is shutting down the socket early for some reason. It looks like my client is throwing back ICMP packets after receiving the packet response from the remote side. what this means is that I'm misusing the run_once() method, I thought that meant run for one event, but I'm guessing now it means run for one tick, which causes the client to return before the packet is received. I need to rethink the way that I'm managing my client event_loop(). – bluejekyll Sep 17 '15 at 04:18
  • Yes, that was it... the bug here was the abuse of run_once() not anything to do with partial packets. I misidentified the EOF error that was getting, which means that I also need to fix my logic around my buffer management to make sure that I can't have a question of it being filled, not-filled, or never received anything. Thanks for the help! – bluejekyll Sep 17 '15 at 04:24

1 Answers1

1

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().

bluejekyll
  • 155
  • 2
  • 8