# 一个实践项目: Web服务器
知识学得再多,不实际应用也是纸上谈兵,不是忘掉就是废掉,对于技术学习尤为如此。在之前章节中,我们已经学习了 `Async Rust` 的方方面面,现在来将这些知识融会贯通,最终实现一个并发Web服务器。
## 多线程版本的Web服务器
在正式开始前,先来看一个单线程版本的 `Web` 服务器,该例子来源于 [`Rust Book`](https://doc.rust-lang.org/book/ch20-01-single-threaded.html) 一书。
`src/main.rs`:
```rust
use std::fs;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
fn main() {
// 监听本地端口 7878 ,等待 TCP 连接的建立
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
// 阻塞等待请求的进入
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream);
}
}
fn handle_connection(mut stream: TcpStream) {
// 从连接中顺序读取 1024 字节数据
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
let get = b"GET / HTTP/1.1\r\n";
// 处理HTTP协议头,若不符合则返回404和对应的`html`文件
let (status_line, filename) = if buffer.starts_with(get) {
("HTTP/1.1 200 OK\r\n\r\n", "hello.html")
} else {
("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html")
};
let contents = fs::read_to_string(filename).unwrap();
// 将回复内容写入连接缓存中
let response = format!("{status_line}{contents}");
stream.write_all(response.as_bytes()).unwrap();
// 使用flush将缓存中的内容发送到客户端
stream.flush().unwrap();
}
```
`hello.html`:
```rust
Hello!
Hello!
Hi from Rust
```
`404.html`:
```rust
Hello!
Oops!
Sorry, I don't know what you're asking for.
```
运行以上代码,并从浏览器访问 `127.0.0.1:7878` 你将看到一条来自 `Ferris` 的问候。
在回忆了单线程版本该如何实现后,我们也将进入正题,一起来实现一个基于 `async` 的异步Web服务器。
## 运行异步代码
一个 Web 服务器必须要能并发的处理大量来自用户的请求,也就是我们不能在处理完上一个用户的请求后,再处理下一个用户的请求。上面的单线程版本可以修改为多线程甚至于线程池来实现并发处理,但是线程还是太重了,使用 `async` 实现 `Web` 服务器才是最适合的。
首先将 `handle_connection` 修改为 `async` 实现:
```rust
async fn handle_connection(mut stream: TcpStream) {
//<-- snip -->
}
```
该修改会将函数的返回值从 `()` 变成 `Future