MIO EventLoop 没有为 TcpStream 运行

Posted

技术标签:

【中文标题】MIO EventLoop 没有为 TcpStream 运行【英文标题】:MIO EventLoop is not running for TcpStream 【发布时间】:2016-02-18 16:22:28 【问题描述】:

我是一名正在努力解决异步 IO 问题的 Rust 初学者。我决定使用mio。

我已经阅读了一些源代码 + 教程,但仍有一些我不明白的基本部分。我正在使用netcat -k -l 127.0.0.1 9999 设置服务器。然后我用cargo(下面的代码)运行简单的测试。我期待看到“准备好”或“滴答”的恐慌。但它永远不会发生,测试会永远运行。

extern crate mio;
use mio::*;
#[allow(unused_imports)]
use mio::tcp::TcpStream;

#[allow(dead_code)]
struct MyHandler;

impl Handler for MyHandler 
    type Timeout = ();
    type Message = ();

    fn ready(&mut self, _event_loop: &mut EventLoop<Self>, _token: Token, _event_set: EventSet) 
        panic!("ready");
    

    fn tick(&mut self, _event_loop: &mut EventLoop<Self>) 
        panic!("tick");
    


#[test]
fn mio_test1() 
    let addr = "127.0.0.1:9999".parse().unwrap();
    let mut event_loop = EventLoop::<MyHandler>::new().unwrap();
    event_loop.register(&TcpStream::connect(&addr).unwrap(), Token(0), EventSet::readable(), PollOpt::level()).unwrap();
    event_loop.run(&mut MyHandler).unwrap();

【问题讨论】:

在这种情况下使用#[allow(...)] 是多余的。最好直接prepend the unused variables with an underscore。相关question. 感谢@Shepmaster 的帮助。我已经看到您使用重新注册进行的编辑,并且我已经将其修复为注册,它导致了操作系统错误。现在我又带着原来的问题回来了。我换了println!带着恐慌!确保不执行处理程序。 【参考方案1】:

您的问题源于您打开的套接字在您的事件循环有机会运行之前已关闭。

你当前的代码大致相当于这个:

let addr = "127.0.0.1:9999".parse().unwrap();
let mut event_loop = EventLoop::<MyHandler>::new().unwrap();

    let sock = TcpStream::connect(&addr).unwrap();
    event_loop.register(&sock, Token(0), EventSet::readable(), PollOpt::level()).unwrap();
   // The socket is closed here, before the event loop runs
event_loop.run(&mut MyHandler).unwrap();

所以解决方法就是将套接字绑定到一个变量,这样当你调用事件循环时它会保持打开状态。

let addr = "127.0.0.1:9999".parse().unwrap();
let mut event_loop = EventLoop::<MyHandler>::new().unwrap();
let sock = TcpStream::connect(&addr).unwrap();
event_loop.register(&sock, Token(0), EventSet::readable(), PollOpt::level()).unwrap();
event_loop.run(&mut MyHandler).unwrap();

然后,您的代码会按照您的预期运行,并在套接字上有要读取的内容时立即发生恐慌。

【讨论】:

这是一个很好的答案。我是否正确理解我借用套接字来注册方法并且当它返回时它被丢弃?如果 event_loop 实例将套接字设置为成员,它不会被删除,这是真的吗? 您的代码有效地创建了一个套接字,将对它的引用传递给 register 函数,然后在语句末尾删除套接字,因为它没有绑定到任何变量。如果通过“将套接字设置为成员”是指如果 register 获得套接字的所有权并存储它,那么是的,套接字将保持打开状态并且您的代码将正常工作。 这对我来说没有任何意义。生命的全部意义就是防止这种事情发生!如果事件流保持对套接字的引用,那么编译器应该抱怨它持续的时间不够长! 从技术上讲,事件循环不会保留对套接字的引用。它实际上只是获取文件描述符,将其与 Token 一起传递给内核 API,然后将其丢弃。所以最终是内核完成了所有的簿记工作。

以上是关于MIO EventLoop 没有为 TcpStream 运行的主要内容,如果未能解决你的问题,请参考以下文章

Mio 在零持续时间超时的轮询中的行为是啥?

向 mio 注册频道

SDSoc学习:使用MIO驱动LED

ZYNQ学习之——MIO

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

如何处理mio中的错误?