diff --git a/book/contents/tokio/io.md b/book/contents/tokio/io.md index c800dbf9..d7362186 100644 --- a/book/contents/tokio/io.md +++ b/book/contents/tokio/io.md @@ -7,10 +7,10 @@ Tokio 中的 I/O 操作和 `std` 在使用方式上几无区别,最大的区 - 还有数据结构也实现了它们:`Vec`、`&[u8]`,这样就可以直接使用这些数据结构作为读写器( reader / writer) ## AsyncRead 和 AsyncWrite -这两个特征为字节流的异步读写提供了便利的使用方法,这些方法都使用 `async` 声明,且需要通过 `.await` 进行调用, +这两个特征为字节流的异步读写提供了便利,通常我们会使用 `AsyncReadExt` 和 `AsyncWriteExt` 提供的工具方法,这些方法都使用 `async` 声明,且需要通过 `.await` 进行调用, #### async fn read - `read` 是一个异步方法可以将数据读入缓冲区( `buffer` )中,然后返回读取的字节数。 + `AsyncReadExt::read` 是一个异步方法可以将数据读入缓冲区( `buffer` )中,然后返回读取的字节数。 ```rust use tokio::fs::File; use tokio::io::{self, AsyncReadExt}; @@ -31,7 +31,7 @@ async fn main() -> io::Result<()> { 需要注意的是:当 `read` 返回 `Ok(0)` 时,意味着字节流( stream )已经关闭,在这之后继续调用 `read` 会立刻完成,依然获取到返回值 `Ok(0)`。 例如,字节流如果是 `TcpStream` 类型,那 `Ok(0)` 说明该**连接的读取端已经被关闭**(写入端关闭,会报其它的错误)。 #### async fn read_to_end -该方法会从字节流中读取所有的字节,直到遇到 `EOF` : +`AsyncReadExt::read_to_end` 方法会从字节流中读取所有的字节,直到遇到 `EOF` : ```rust use tokio::io::{self, AsyncReadExt}; use tokio::fs::File; @@ -48,7 +48,7 @@ async fn main() -> io::Result<()> { ``` #### async fn write -`write` 异步方法会尝试将缓冲区的内容写入到写入器( `writer` )中,同时返回写入的字节数: +`AsyncWriteExt::write` 异步方法会尝试将缓冲区的内容写入到写入器( `writer` )中,同时返回写入的字节数: ```rust use tokio::io::{self, AsyncWriteExt}; use tokio::fs::File; @@ -67,7 +67,7 @@ async fn main() -> io::Result<()> { 上面代码很清晰,但是大家可能会疑惑 `b"some bytes"` 是什么意思。这种写法可以将一个 `&str` 字符串转变成一个字节数组:`&[u8;10]`,然后 `write` 方法又会将这个 `&[u8;10]` 的数组类型隐式强转为数组切片: `&[u8]`。 #### async fn write_all -将缓冲区的内容全部写入到写入器中: +`AsyncWriteExt::write_all` 将缓冲区的内容全部写入到写入器中: ```rust use tokio::io::{self, AsyncWriteExt}; use tokio::fs::File; @@ -81,12 +81,12 @@ async fn main() -> io::Result<()> { } ``` -以上只是部分方法,实际上还有一些实用的方法由于篇幅有限无法列出,大家可以通过 API 文档查看完整的列表。 +以上只是部分方法,实际上还有一些实用的方法由于篇幅有限无法列出,大家可以通过 [API 文档](https://docs.rs/tokio/latest/tokio/io/index.html) 查看完整的列表。 ## 实用函数 另外,和标准库一样, `tokio::io` 模块包含了多个实用的函数或API,可以用于处理标准输入/输出/错误等。 -例如,`tokio::io::copy` 异步的将读取器( `reader` )中的内容拷贝到写入器中。 +例如,`tokio::io::copy` 异步的将读取器( `reader` )中的内容拷贝到写入器( `writer` )中。 ```rust use tokio::fs::File; use tokio::io; @@ -288,6 +288,8 @@ struct Task { 但是再怎么优化,任务的结构体至少也会跟其中的栈数组一样大,因此通常情况下,使用堆上的缓冲区会高效实用的多。 +> 当任务因为调度在线程间移动时,存储在栈上的数据需要进行保存和恢复,过大的栈上变量会带来不小的数据拷贝开销 +> 因此,存储大量数据的变量最好放到堆上 ##### 处理EOF 当 TCP 连接的读取端关闭后,再调用 `read` 方法会返回 `Ok(0)`。此时,再继续下去已经没有意义,因此我们需要退出循环。忘记在 EOF 时退出读取循环,是网络编程中一个常见的 bug : ```rust