第18章 - 实战项目:简单 HTTP 服务器
嗨,朋友!我是长安。
这一章我们要做一个简单的 HTTP 服务器,能够处理 HTTP 请求并返回网页!
这个项目会用到:TCP 连接、多线程、文件读取、HTTP 协议等知识。
🎯 项目目标
制作一个简单的 HTTP 服务器,支持:
- ✅ 处理 GET 请求
- ✅ 返回 HTML 页面
- ✅ 返回 404 错误页面
- ✅ 多线程处理请求
🚀 创建项目
cargo new http_server
cd http_server
💻 完整代码
src/main.rs:
use std::fs;
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use std::thread;
use std::time::Duration;
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
println!("🚀 服务器启动成功!");
println!("📍 访问: http://127.0.0.1:7878");
println!("按 Ctrl+C 停止服务器\n");
for stream in listener.incoming() {
let stream = stream.unwrap();
// 为每个连接创建一个新线程
thread::spawn(|| {
handle_connection(stream);
});
}
}
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
// 解析请求
let request = String::from_utf8_lossy(&buffer[..]);
let request_line = request.lines().next().unwrap_or("");
println!("📨 收到请求: {}", request_line);
let (status_line, filename) = if request_line.starts_with("GET / HTTP") {
("HTTP/1.1 200 OK", "index.html")
} else if request_line.starts_with("GET /about HTTP") {
("HTTP/1.1 200 OK", "about.html")
} else if request_line.starts_with("GET /slow HTTP") {
// 模拟慢速请求
thread::sleep(Duration::from_secs(5));
("HTTP/1.1 200 OK", "index.html")
} else {
("HTTP/1.1 404 NOT FOUND", "404.html")
};
// 读取 HTML 文件
let contents = fs::read_to_string(filename).unwrap_or_else(|_| {
String::from("<h1>文件未找到</h1>")
});
// 构建 HTTP 响应
let response = format!(
"{}\r\nContent-Length: {}\r\nContent-Type: text/html; charset=utf-8\r\n\r\n{}",
status_line,
contents.len(),
contents
);
stream.write_all(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
📄 创建 HTML 文件
index.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rust HTTP 服务器</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.container {
background: rgba(255, 255, 255, 0.1);
padding: 30px;
border-radius: 10px;
backdrop-filter: blur(10px);
}
h1 { text-align: center; }
a {
color: #ffd700;
text-decoration: none;
font-weight: bold;
}
a:hover { text-decoration: underline; }
.nav {
text-align: center;
margin: 20px 0;
}
.nav a {
margin: 0 15px;
}
</style>
</head>
<body>
<div class="container">
<h1>🦀 欢迎来到 Rust HTTP 服务器!</h1>
<p style="text-align: center; font-size: 1.2em;">
这是一个用 Rust 编写的简单 HTTP 服务器示例。
</p>
<div class="nav">
<a href="/">首页</a>
<a href="/about">关于</a>
<a href="/slow">慢速页面</a>
</div>
<h2>✨ 特性</h2>
<ul>
<li>✅ 处理 HTTP GET 请求</li>
<li>✅ 返回 HTML 页面</li>
<li>✅ 404 错误处理</li>
<li>✅ 多线程并发处理</li>
</ul>
<h2>📚 技术栈</h2>
<ul>
<li>Rust 标准库</li>
<li>TCP Socket</li>
<li>多线程</li>
<li>HTTP 协议</li>
</ul>
<p style="text-align: center; margin-top: 30px;">
<small>由长安的 Rust 教程提供 🚀</small>
</p>
</div>
</body>
</html>
about.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>关于 - Rust HTTP 服务器</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
}
.container {
background: rgba(255, 255, 255, 0.1);
padding: 30px;
border-radius: 10px;
backdrop-filter: blur(10px);
}
h1 { text-align: center; }
a {
color: #ffd700;
text-decoration: none;
font-weight: bold;
}
a:hover { text-decoration: underline; }
.back {
text-align: center;
margin-top: 30px;
}
</style>
</head>
<body>
<div class="container">
<h1>📖 关于这个项目</h1>
<h2>项目简介</h2>
<p>
这是一个使用 Rust 编写的简单 HTTP 服务器,用于演示 Rust 的网络编程能力。
</p>
<h2>实现功能</h2>
<ul>
<li>TCP Socket 监听</li>
<li>HTTP 请求解析</li>
<li>静态文件服务</li>
<li>多线程并发</li>
</ul>
<h2>技术亮点</h2>
<ul>
<li>🚀 零依赖,只用标准库</li>
<li>⚡ 多线程处理,支持并发</li>
<li>🛡️ 类型安全,编译时检查</li>
<li>💪 性能优异,内存安全</li>
</ul>
<div class="back">
<a href="/">← 返回首页</a>
</div>
</div>
</body>
</html>
404.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 - 页面未找到</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
color: white;
text-align: center;
}
.container {
background: rgba(255, 255, 255, 0.1);
padding: 30px;
border-radius: 10px;
backdrop-filter: blur(10px);
}
h1 { font-size: 5em; margin: 0; }
h2 { margin-top: 0; }
a {
color: #ffd700;
text-decoration: none;
font-weight: bold;
font-size: 1.2em;
}
a:hover { text-decoration: underline; }
</style>
</head>
<body>
<div class="container">
<h1>404</h1>
<h2>😱 页面未找到</h2>
<p>抱歉,你访问的页面不存在。</p>
<p style="margin-top: 30px;">
<a href="/">← 返回首页</a>
</p>
</div>
</body>
</html>
📖 代码详解
1. 启动服务器
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
- 在本地 7878 端口监听 TCP 连接
127.0.0.1是本地回环地址
2. 接受连接
for stream in listener.incoming() {
let stream = stream.unwrap();
thread::spawn(|| {
handle_connection(stream);
});
}
incoming()返回连接迭代器- 为每个连接创建新线程处理
3. 解析 HTTP 请求
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
let request = String::from_utf8_lossy(&buffer[..]);
let request_line = request.lines().next().unwrap_or("");
- 读取请求数据到缓冲区
- 转换为字符串并提取第一行
4. 路由处理
let (status_line, filename) = if request_line.starts_with("GET / HTTP") {
("HTTP/1.1 200 OK", "index.html")
} else if request_line.starts_with("GET /about HTTP") {
("HTTP/1.1 200 OK", "about.html")
} else {
("HTTP/1.1 404 NOT FOUND", "404.html")
};
- 根据请求路径选择对应的 HTML 文件
5. 返回 HTTP 响应
let response = format!(
"{}\r\nContent-Length: {}\r\nContent-Type: text/html; charset=utf-8\r\n\r\n{}",
status_line,
contents.len(),
contents
);
stream.write_all(response.as_bytes()).unwrap();
stream.flush().unwrap();
- 构建符合 HTTP 协议的响应
- 写入 TCP 流并发送
🏃 运行服务器
cargo run
输出:
🚀 服务器启动成功!
📍 访问: http://127.0.0.1:7878
按 Ctrl+C 停止服务器
📨 收到请求: GET / HTTP/1.1
📨 收到请求: GET /about HTTP/1.1
📨 收到请求: GET /test HTTP/1.1
然后在浏览器打开 http://127.0.0.1:7878
🎨 改进建议
1. 使用线程池
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
struct ThreadPool {
workers: Vec<Worker>,
sender: mpsc::Sender<Job>,
}
type Job = Box<dyn FnOnce() + Send + 'static>;
impl ThreadPool {
fn new(size: usize) -> ThreadPool {
let (sender, receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool { workers, sender }
}
fn execute<F>(&self, f: F)
where
F: FnOnce() + Send + 'static,
{
let job = Box::new(f);
self.sender.send(job).unwrap();
}
}
2. 添加静态文件服务
fn serve_file(path: &str) -> Option<Vec<u8>> {
fs::read(format!("static/{}", path)).ok()
}
3. 添加 MIME 类型支持
fn get_content_type(path: &str) -> &str {
if path.ends_with(".html") {
"text/html"
} else if path.ends_with(".css") {
"text/css"
} else if path.ends_with(".js") {
"application/javascript"
} else if path.ends_with(".jpg") || path.ends_with(".jpeg") {
"image/jpeg"
} else {
"text/plain"
}
}
💡 学到的知识点
通过这个项目,我们学会了:
- ✅ TCP Socket 编程
- ✅ HTTP 协议基础
- ✅ 多线程处理
- ✅ 文件 I/O
- ✅ 字符串处理
- ✅ 模式匹配
💪 挑战任务
- 实现 POST 请求处理
- 添加日志记录功能
- 支持静态文件目录
- 实现简单的路由系统
- 添加基本的身份验证
POST 请求处理示例
fn handle_post(stream: &mut TcpStream, body: &str) {
println!("收到 POST 数据: {}", body);
let response = "HTTP/1.1 200 OK\r\n\r\n收到数据";
stream.write_all(response.as_bytes()).unwrap();
}
// 在 handle_connection 中:
if request_line.starts_with("POST") {
// 提取请求体
let body_start = request.find("\r\n\r\n").unwrap_or(0) + 4;
let body = &request[body_start..];
handle_post(&mut stream, body);
}
🚀 进阶学习
想要更强大的 Web 框架?可以学习:
- Actix-web:高性能 Web 框架
- Rocket:易用的 Web 框架
- Axum:基于 Tokio 的异步框架
- Warp:组合式 Web 框架
🎉 总结
恭喜你完成了三个实战项目!
你已经学会了:
- 🎮 猜数字游戏 - 基础语法实践
- 📝 待办事项 CLI - 文件操作和数据结构
- 🌐 HTTP 服务器 - 网络编程和并发
现在你已经掌握了 Rust 的基础知识和实践经验!
准备好挑战更高级的内容了吗?进阶教程包含:泛型、Trait、生命周期、智能指针和并发编程!
继续加油,用 Rust 创造更多精彩的项目吧!🚀
