Writing an Accept Loop

让我们实现服务器的支架:一个循环,它将 TCP socket 绑定到地址,并开始接受连接。

首先,让我们添加所需的导入样板:

  1. # extern crate async_std;
  2. use async_std::{
  3. prelude::*, // 1
  4. task, // 2
  5. net::{TcpListener, ToSocketAddrs}, // 3
  6. };
  7. type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; // 4
  1. prelude重新导出与 futures 和 streams 一起使用,所需的某些 trait。
  2. task模块大致对应std::thread模块,但 task 轻得多。一个线程(thread)可以运行许多 tasks。
  3. 对于 socket 类型,我们使用TcpListener,来自async_std,它就像std::net::TcpListener,但非-阻塞,并且使用asyncAPI。
  4. 在此示例中,我们会跳过实现全面的错误处理。为了传播错误,我们将使用装箱的(boxed) error trait 对象。你知道 stdlib 中,有个From<&'_ str> for Box<dyn Error>实现吗,它允许您将字符串?操作符?

现在我们可以编写服务器的 接收 循环:

  1. # extern crate async_std;
  2. # use async_std::{
  3. # net::{TcpListener, ToSocketAddrs},
  4. # prelude::*,
  5. # };
  6. #
  7. # type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
  8. #
  9. async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { // 1
  10. let listener = TcpListener::bind(addr).await?; // 2
  11. let mut incoming = listener.incoming();
  12. while let Some(stream) = incoming.next().await { // 3
  13. // TODO
  14. }
  15. Ok(())
  16. }
  1. 我们将accept_loop函数标记上了async,这使我们可以在内部使用.await语法。
  2. TcpListener::bind call 返回了一个 Future,此处,我们会.await(等待),提取出Result,然后?得到一个TcpListener。 注意.await?的良好合作。这与std::net::TcpListener的工作如此相同,但多个.await而已。 对std API 施行镜像魔法,是async_std的一个明确的设计目标。
  3. 在这里,我们想迭代传入的 socket,就像在常规std中的那样:
  1. let listener: std::net::TcpListener = unimplemented!();
  2. for stream in listener.incoming() {
  3. }

不幸的是,目前这还不适用于async,因为在语言中,还不支持async for 循环。因此,我们必须手动实现循环,通过使用while let Some(item) = iter.next().await模式。

最后,让我们添加 main:

  1. # extern crate async_std;
  2. # use async_std::{
  3. # net::{TcpListener, ToSocketAddrs},
  4. # prelude::*,
  5. # task,
  6. # };
  7. #
  8. # type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
  9. #
  10. # async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { // 1
  11. # let listener = TcpListener::bind(addr).await?; // 2
  12. # let mut incoming = listener.incoming();
  13. # while let Some(stream) = incoming.next().await { // 3
  14. # // TODO
  15. # }
  16. # Ok(())
  17. # }
  18. #
  19. // main
  20. fn run() -> Result<()> {
  21. let fut = accept_loop("127.0.0.1:8080");
  22. task::block_on(fut)
  23. }

意识到这一点的关键是,Rust 与其他语言不同,调用异步函数确实是运行任何代码的。异步函数仅构造 Future,它们是惰性状态机。要在一个异步函数中,开始逐步遍历 Future 状态机,您应该使用.await。在非异步函数中,执行 Future 的一种方法是将其交给 executor。在这种情况下,我们会使用task::block_on,在当前线程上对 Future 执行并阻塞,直到它完成。