异步与网络

第24课:TCP服务端

🎯 本课目标

掌握Rust异步TCP编程:服务端、客户端、协议解析和连接管理。

1. TCP服务端

use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let listener = TcpListener::bind("127.0.0.1:8080").await?;
    println!("服务端监听 127.0.0.1:8080");

    loop {
        let (mut socket, addr) = listener.accept().await?;
        println!("新连接: {}", addr);

        tokio::spawn(async move {
            let mut buf = [0; 1024];
            loop {
                let n = match socket.read(&mut buf).await {
                    Ok(0) => return,
                    Ok(n) => n,
                    Err(e) => { println!("读错误: {}", e); return; }
                };
                if let Err(e) = socket.write_all(&buf[..n]).await {
                    println!("写错误: {}", e); return;
                }
            }
        });
    }
}

✅ 编译验证通过

2. TCP客户端

use tokio::net::TcpStream;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
    stream.write_all(b"Hello!").await?;

    let mut buf = [0; 1024];
    let n = stream.read(&mut buf).await?;
    println!("收到: {:?}", &buf[..n]);
    Ok(())
}

✅ 编译验证通过

3. 协议解析

use tokio::io::{AsyncBufReadExt, BufReader};

// 行协议:每行一个命令
async fn handle_connection(stream: tokio::net::TcpStream) {
    let (reader, mut writer) = stream.into_split();
    let mut reader = BufReader::new(reader);
    let mut line = String::new();

    loop {
        line.clear();
        match reader.read_line(&mut line).await {
            Ok(0) => break,  // EOF
            Ok(_) => {
                let cmd = line.trim();
                let response = match cmd {
                    "PING" => "PONG",
                    "INFO" => "Rust TCP Server v1.0",
                    s if s.starts_with("ECHO ") => &s[5..],
                    _ => "ERROR: 未知命令",
                };
                writer.write_all(format!("{}\n", response).as_bytes()).await.unwrap();
            }
            Err(e) => { println!("错误: {}", e); break; }
        }
    }
}

4. 连接管理

use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

#[tokio::main]
async fn main() {
    let listener = TcpListener::bind("0.0.0.0:8080").await.unwrap();
    let connections = Arc::new(AtomicUsize::new(0));

    loop {
        let (socket, addr) = listener.accept().await.unwrap();
        let conn_count = Arc::clone(&connections);
        conn_count.fetch_add(1, Ordering::Relaxed);

        tokio::spawn(async move {
            handle_connection(socket).await;
            conn_count.fetch_sub(1, Ordering::Relaxed);
        });
    }
}
TCP连接的异步模型: ┌─────────┐ ┌─────────┐ │ Client │ │ Server │ └────┬────┘ └────┬────┘ │ SYN │ │─────────────────→│ │ SYN+ACK │ │←─────────────────│ │ ACK │ │─────────────────→│ │ │ │ Data │ ← AsyncReadExt::read().await │─────────────────→│ │ Response │ ← AsyncWriteExt::write().await │←─────────────────│ │ │ 每个连接一个tokio::spawn任务 不需要为每个连接分配线程!

📝 练习题

练习1:聊天服务器

用broadcast通道实现多人聊天室。

练习2:长度前缀协议

实现4字节长度前缀的二进制协议。

练习3:代理服务器

实现简单的TCP代理转发。

🏆

成就解锁:网络工匠

掌握TCP异步编程——构建网络服务的基础

🔬 深度理解:异步编程模型

Rust的异步模型与其他语言有本质区别:

1. 编译器生成状态机

async函数不是语法糖生成闭包或Future对象,而是编译器生成一个状态机enum,每个.await点是一个状态。这比Green Thread更高效——无堆分配、无运行时开销。

2. 零成本异步

不使用的异步特性不产生代码。不调用async函数?没有状态机代码。这与其他语言的全局运行时(Go的goroutine调度器、Java的Fiber)不同。

3. 显式运行时

Rust没有内置异步运行时。你选择Tokio、async-std或其他——运行时是库,不是语言特性。这意味着灵活但需要选择。

📊 异步模型对比

模型语言开销并发数
OS线程C/Java1-8MB/线程~1000
Green ThreadGo~2KB/goroutine~100K
async/awaitRust~几百字节~1M+
回调Node.js

🌐 网络编程最佳实践


📚 扩展阅读

以下资源帮助你深入学习本课主题:

🔑 关键术语回顾

本课涉及的核心概念,确保你理解每一个:

术语说明
所有权Rust内存管理的核心机制,每个值有唯一所有者
借用通过引用访问数据,不获取所有权
生命周期引用有效的范围,编译期分析工具
traitRust的接口/抽象机制,类似其他语言的接口
泛型参数化类型,零成本抽象
模式匹配强大的数据解构和分支机制
零成本抽象高层抽象不引入运行时开销
fearless concurrency编译器保证线程安全

💬 学习建议

  1. 动手编码 — 每个代码示例都在本地运行一遍
  2. 修改实验 — 故意改错代码,看编译器报什么错
  3. 完成练习 — 每课的练习题是巩固知识的关键
  4. 阅读源码 — 看标准库和优秀开源项目的实现
  5. 写项目 — 真正掌握需要构建真实项目

🤔 常见问题

Q: Rust学习曲线真的很陡吗?

A: 前期确实需要适应所有权和借用检查器,但一旦理解了,这些概念会让你的代码更可靠。大多数人2-4周就能上手。

Q: Rust适合什么项目?

A: 系统编程、Web服务、CLI工具、嵌入式、WASM、网络服务、数据库等。基本上需要性能和安全的地方都适合。

Q: 遇到编译错误怎么办?

A: Rust编译器的错误信息非常友好!仔细阅读,通常会指出问题所在和修复建议。也可以用cargo clippy获取更多提示。