I/O概叙

Rust标准库提供对网络和I/O的支持,例如TCP连接,UDP套接字,读取和写入文件等。但是,这些操作都是同步或阻塞的,这意味着当它们被调用时,当前线程可能会停止执行并进入睡眠状态,直到它被解除阻塞。例如,std::io::Read中的read方法会阻塞当前线程,直到能读取到数据。在使用Future的时候,这种行为会有问题,因为我们希望在等待I/O完成时继续执行我们可能拥有的其他Future。

为了实现这一点,Tokio提供了许多标准库I/O资源的非阻塞版本,例如文件操作和TCP,UDP和 Unix套接字。这些操作为长时间运行的操作(如接受新的TCP连接)返回Future,并实现无阻塞 std:![:io:](https://www.emoji-cheat-sheet.com/graphics/emojis/io.png ":io:"):Read和std:![:io:](https://www.emoji-cheat-sheet.com/graphics/emojis/io.png ":io:"):Write的async版本,名为AsyncRead和 AsyncWrite。

例如,如果没有可用的数据,非阻塞读写不会阻塞当前线程。相反,它们会立即返回 WouldBlock错误,同时保证(Future::poll)已安排当前任务在以后可以取得进展时被唤醒,例如当网络数据包到达时。

通过使用非阻塞的Tokio I/O类型,如果一个执行I/O操作的Future不能立即执行,也不会阻止其他Future的执行,它只是返回 NotReady,并依赖于任务通知task notification,使poll方法再次被调用,那时该I/O应该会成功且不会阻塞。

在幕后,Tokio使用mio和tokio-fs跟踪不同futures等待的各种I/O资源的状态,只要其中任何一个的状态发生变化,操作系统就会通知它。

一个服务器例子

要了解它如何工作,请考虑以下echo服务器的实现:

1. use tokio::prelude::*;
2. use tokio::net::TcpListener;

5. let addr = "127.0.0.1:12345".parse().unwrap();
6. let listener = TcpListener::bind(&addr)
7. .expect("unable to bind TCP listener");

14. let incoming = listener.incoming();

19. let server = incoming
20. .map_err(|e| eprintln!("accept failed = {:?}", e))
21. .for_each(|socket| {

40. let (reader, writer) = socket.split();
41. let bytes_copied = tokio::io::copy(reader, writer);
42. let handle_conn = bytes_copied.map(|amt| {
43. println!("wrote {:?} bytes", amt)
44. }).map_err(|err| {
45. eprintln!("I/O error {:?}", err)
46. });

56. tokio::spawn(handle_conn)
57. });

62. tokio::run(server);

更多例子,请参考 这里.