Implementing a client

现在让我们实现聊天的客户端。因为该协议是基于行(line-based)的,所以实现非常简单:

  • 从 stdin 读取的行,应通过 socket 发送。
  • 从 socket 读取的行,应回显到 stdout。

与服务器不同,客户端仅需要有限的并发性,因为它仅与单个用户进行交互。因此,在这种情况下,异步不会带来很多性能优势。

但是,异步对于管理并发仍然很有用!具体来说,客户应同时从 stdin 和 socket 中读取(信息)。使用线程对此进行编程非常麻烦,尤其是要干净关机时。使用异步,我们可以只用select!宏就可以了。

  1. # extern crate async_std;
  2. # extern crate futures;
  3. use async_std::{
  4. io::{stdin, BufReader},
  5. net::{TcpStream, ToSocketAddrs},
  6. prelude::*,
  7. task,
  8. };
  9. use futures::{select, FutureExt};
  10. type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
  11. // main
  12. fn run() -> Result<()> {
  13. task::block_on(try_run("127.0.0.1:8080"))
  14. }
  15. async fn try_run(addr: impl ToSocketAddrs) -> Result<()> {
  16. let stream = TcpStream::connect(addr).await?;
  17. let (reader, mut writer) = (&stream, &stream); // 1
  18. let mut lines_from_server = BufReader::new(reader).lines().fuse(); // 2
  19. let mut lines_from_stdin = BufReader::new(stdin()).lines().fuse(); // 2
  20. loop {
  21. select! { // 3
  22. line = lines_from_server.next().fuse() => match line {
  23. Some(line) => {
  24. let line = line?;
  25. println!("{}", line);
  26. },
  27. None => break,
  28. },
  29. line = lines_from_stdin.next().fuse() => match line {
  30. Some(line) => {
  31. let line = line?;
  32. writer.write_all(line.as_bytes()).await?;
  33. writer.write_all(b"\n").await?;
  34. }
  35. None => break,
  36. }
  37. }
  38. }
  39. Ok(())
  40. }
  1. 在这里,我们将TcpStream分为了,一半 read 和一半 write :这里的impl AsyncRead for &'_ TcpStream,就像 std 中的那个一样。
  2. 我们为 socket 和 stdin 创建了行的数据流(a stream of lines)。
  3. 在 main select 循环中,我们打印从服务器接收的 lines,并发送从控制台读取到的 lines。