# 并发编程新手指南: 使用Tokio实现多线程聊天服务器
_2024年5月4日 · #rust · #async · #concurrency · #tokio_

目录
[导读](#introduction)
[01\) 最简单的回显服务器](#01-最简单的回显服务器)
[02\) 串行处理多个连接](#02-串行处理多个连接)
[03\) 修改消息](#03-修改消息)
[04\) 将字节流解析为行](#04-将字节流解析为行)
[05\) 服务器增加`/help`和`/quit`命令](#05-服务器增加help和quit命令)
[06\) 并发处理多个连接](#06-并发处理多个连接)
[07\) 让用户聊天](#07-让用户聊天)
[08\) 让用户真正的聊天](#08-让用户真正的聊天)
[09\) 为用户分配名称](#09-为用户分配名称)
[10\) 使用`/name`命令编辑自己的名字](#10-使用name命令编辑自己的名字)
[11\) 在客户端断开连接时释放用户名](#11-在客户端断开连接时释放用户名)
[12\) 增加`main`聊天室](#12-增加main聊天室)
[13\) 使用`/join`加入或创建聊天室](#13-使用join加入或创建聊天室)
[14\) 使用`/rooms`列出所有聊天室](#14-使用rooms列出所有聊天室)
[15\) 删除空聊天室](#15-删除空聊天室)
[16\) 使用`/users`命令列出当前聊天室的用户](#16-使用users命令列出当前聊天室的用户)
[17\) 性能优化](#17-性能优化)
[18\) 收尾工作](#18-收尾工作)
[结论](#结论)
[讨论](#讨论)
[进一步阅读](#进一步阅读)
## Introduction
我最近使用 `tokio` 编写了一个多线程聊天服务器,我对此很满意。我想通过这篇易于理解、循序渐进的教程分享我学到的东西,让我们开始吧。
> [!NOTE]
> 每一步的源代码都在 [这个仓库](https://github.com/pretzelhammer/chat-server) 的 [examples 目录](https://github.com/pretzelhammer/chat-server/tree/main/examples) 找到。
## 01\) 最简单的回显服务器
让我们开始写一个最简单的回显服务器。
```rust
use tokio::{io::{AsyncReadExt, AsyncWriteExt}, net::TcpListener};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let server = TcpListener::bind("127.0.0.1:42069").await?;
let (mut tcp, _) = server.accept().await?;
let mut buffer = [0u8; 16];
loop {
let n = tcp.read(&mut buffer).await?;
if n == 0 {
break;
}
let _ = tcp.write(&buffer[..n]).await?;
}
Ok(())
}
```
`#[tokio::main]` 是一个过程宏,它可以自动生成 `tokio` 运行时所需的重复性代码, 如下的代码:
```rust
#[tokio::main]
async fn my_async_fn() {
todo!()
}
```
大致等同于转换成这样:
```rust
fn main() {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(my_async_fn)
}
async fn my_async_fn() {
todo!()
}
```
快速回顾一下,如果我们有一个如下的异步函数:
```rust
async fn my_async_fn(t: T) -> T {
todo!()
}
```
可以转换为:
```rust
fn my_async_fn(t: T) -> impl Future