从 mio::udp::UdpSocket.recv 接收部分 UDP 数据包

Posted

技术标签:

【中文标题】从 mio::udp::UdpSocket.recv 接收部分 UDP 数据包【英文标题】:receiving partial UDP packets from mio::udp::UdpSocket.recv 【发布时间】:2015-09-15 07:32:27 【问题描述】:

我正在使用 mio::udp::UdpSocket 接收对来自客户端的请求的响应。看起来我在触发事件上收到了部分 UDP 数据包。我不确定这是否是 mio 库中的错误。

我已经尝试过 PollOpt::level()、all()、empty()、edge() 等。我想我通常想要基于 poll() 文档的 level(),但它们都不起作用。通过添加 20 毫秒的睡眠,我得到了完整的数据包。

作为参考,当使用阻塞 std::net::UdpSocket 时,我没有发现任何问题。老实说,如果 std::net::SocketOpts 稳定,我会使用它。使用 mio 的目的是在 socket 上获得超时,看起来 net2 将替换 std::net,但即使 net2 在 recv 上也没有超时。

这是事件循环的代码:

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

这是处理程序的代码:

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),
  

【问题讨论】:

我建议通过分析网络上的 UDP 数据包来 100% 确认您尝试获取的所有数据都在单个数据报中发送。 谢谢,我会仔细检查,但我们所说的大小是 129 字节。这在标准 MTU 之下。 MTU 与任何东西都无关。这只是决定如何将数据报拆分为数据包。我要求您确认它是在单个数据报中发送的。这将取决于发送软件的工作方式,因为数据报是应用程序级的东西。是的,数据报如何映射到数据包取决于 UDP,但 UDP 使应用程序不可见。 所以,在 TCPDumping 很多之后,我认为我遇到了另一个问题,我的测试由于某种原因提前关闭了套接字。看起来我的客户端在接收到来自远程端的数据包响应后正在扔回 ICMP 数据包。这意味着我滥用了 run_once() 方法,我认为这意味着运行一个事件,但我现在猜测它意味着运行一个滴答声,这会导致客户端在收到数据包之前返回。我需要重新考虑管理客户端 event_loop() 的方式。 是的,就是这样......这里的错误是滥用 run_once() 与部分数据包无关。我错误地识别了正在发生的 EOF 错误,这意味着我还需要围绕缓冲区管理修复我的逻辑,以确保我不会遇到它被填充、未填充或从未收到任何东西的问题。感谢您的帮助! 【参考方案1】:

只是跟进,上述逻辑中的错误是 run_once() 运行一个滴答声,而不是一个“事件”,这是一个错误的假设(但公平地说,该接口目前没有很好地记录)。

无论如何,这不是部分数据包问题,而是在 run_once() 逻辑运行之前未交付数据包的问题,​​在套接字上没有看到任何内容并立即返回。

我已将处理程序更改为在接收数据包后执行 event_loop.shutdown() 并使用 run() 而不是 run_once()。

【讨论】:

以上是关于从 mio::udp::UdpSocket.recv 接收部分 UDP 数据包的主要内容,如果未能解决你的问题,请参考以下文章

从PRISM开始学WPFMVVMViewModel?

在 python 中,为啥从数组读取比从列表读取慢?

从图库中挑选或从相机捕获的高质量图像

从PRISM开始学WPFMVVMCommand?

从PRISM开始学WPFPrism?

mysql 主-主-从-从