【Rust http编程】Rust搭建webserver的底层原理与应用实战

发布于:2025-06-26 ⋅ 阅读:(20) ⋅ 点赞:(0)

在这里插入图片描述

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Rust开发,Python全栈,Golang开发,云原生开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏:Rust语言通关之路
景天的主页:景天科技苑

在这里插入图片描述

Rust http编程

Rust作为一门系统级编程语言,凭借其出色的性能、内存安全性和并发特性,在网络编程领域展现出强大的潜力。
本文将详细介绍如何使用Rust进行HTTP编程,涵盖从基础概念到实际应用的各个方面。

1. HTTP基础与Rust生态系统

1.1 HTTP协议回顾

HTTP(HyperText Transfer Protocol)是应用层协议,基于请求-响应模型工作。Rust提供了多种处理HTTP协议的方式:
标准库:基础但功能有限
第三方库:功能丰富,如reqwest、hyper等
Web框架:如actix-web、rocket等

1.2 Rust HTTP生态系统概览

Rust的HTTP生态系统包含多个层次的组件:
底层库:hyper、h2、http等
客户端库:reqwest、ureq等
服务器框架:actix-web、rocket、warp等
工具库:serde(序列化)、tokio(异步运行时)等

2. 使用标准库进行HTTP编程

虽然不推荐在生产环境中使用标准库进行HTTP编程,但了解其基本用法有助于理解底层原理。
可以参考官方标准库net库 https://doc.rust-lang.org/stable/std/net/index.html
TcpListener可以创建http客户端和服务端
在这里插入图片描述

HTTP简单介绍
(1)http请求报文包含三个部分内容 :请求行、请求头 、请求体
Method Request-URI HTTP-Version CRLF //请求行:请求方式、协议版本等
headers CRLF //请求头:包含若干个属性,格式为“属性名:属性值”,格式为"属性名:属性值",服务端据此获取客户端的信息
message-body //请求体 :客户端真正要传送给服务端的内容

(2)http响应报文也有三部分内容:响应行、响应头、响应体
HTTP-Version status-Code Reason-Phrase CRLF //响应行:报文协议及版本,状态码及状态描述
headers CRLF //响应头:由多个属性组成
message-body //响应体:真正响应的内容

2.1 基本HTTP服务端

主要使用标准库中的net库和io库

use std::net::{ TcpListener, TcpStream }; //导入TcpListener和TcpStream
use std::io::{ Read, Write }; //导入Read和Write

fn handle_client(mut stream: TcpStream) {
    //读取客户端请求,每次读取1024个字节
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).unwrap();

    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&buffer[..]));

    //构建http响应,向客户端打招呼
    //获取客户端地址
    let client_addr = stream.peer_addr().unwrap();
    println!("New connection: {}", client_addr);
    let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");
    //将响应写入到客户端
    stream.write_all(response.as_bytes()).unwrap();
    //刷新缓冲区
    stream.flush().unwrap();
}

fn main() -> std::io::Result<()> {
    //创建监听器
    let listener = TcpListener::bind("127.0.0.1:8080")?;

    //处理客户端请求
    //listener.incoming()返回一个迭代器,用于接收客户端的连接请求
    for stream in listener.incoming() {
        //处理客户端请求的逻辑
        //listener.incoming()返回的迭代器包含错误,需要使用?来处理
        handle_client(stream?);
    }
    Ok(())
}

2.2 简单HTTP客户端

use std::io::{ Read, Write };
use std::net::TcpStream;

fn main() -> std::io::Result<()> {
    //创建TCP连接
    let mut stream = TcpStream::connect("localhost:8080")?;

    //构建HTTP请求
    let request =
        "GET / HTTP/1.1\r\n\
                   Host: localhost:8080\r\n\
                   Connection: close\r\n\
                   \r\n";

    stream.write_all(request.as_bytes())?;

    //创建个缓冲区,用于读取服务器的响应
    let mut buffer = Vec::new();
    //读取服务器的响应
    stream.read_to_end(&mut buffer)?;

    //打印服务器的响应
    println!("{}", String::from_utf8_lossy(&buffer));
    Ok(())
}

服务端收到客户端请求
在这里插入图片描述

客户端收到服务端响应
在这里插入图片描述

2.3 服务端响应网页

use std::net::{ TcpListener, TcpStream }; //导入TcpListener和TcpStream
use std::io::{ Read, Write }; //导入Read和Write

fn handle_client(mut stream: TcpStream) {
    //读取客户端请求,每次读取1024个字节
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).unwrap();

    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&buffer[..]));

    //构建http响应,向客户端打招呼
    //获取客户端地址
    let client_addr = stream.peer_addr().unwrap();
    println!("New connection: {}", client_addr);
    // let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");

    //从文件读取内容响应给客户端
    let content = std::fs::read_to_string("index.html").unwrap();

    let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", content);

    //将响应写入到客户端
    stream.write_all(response.as_bytes()).unwrap();
    //刷新缓冲区
    stream.flush().unwrap();
}

fn main() -> std::io::Result<()> {
    //创建监听器
    let listener = TcpListener::bind("127.0.0.1:8080")?;

    //处理客户端请求
    //listener.incoming()返回一个迭代器,用于接收客户端的连接请求
    for stream in listener.incoming() {
        //处理客户端请求的逻辑
        //listener.incoming()返回的迭代器包含错误,需要使用?来处理
        handle_client(stream?);
    }
    Ok(())
}

直接浏览器访问查看
在这里插入图片描述

2.4 有条件地响应网页

有条件地响应网页,主要是对客户端的请求进行判断,不同的请求路径、请求方法等响应不同内容

use std::net::{ TcpListener, TcpStream }; //导入TcpListener和TcpStream
use std::io::{ Read, Write }; //导入Read和Write

fn handle_client(mut stream: TcpStream) {
    //读取客户端请求,每次读取1024个字节
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).unwrap();

    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&buffer[..]));

    //构建http响应,向客户端打招呼
    //获取客户端地址
    let client_addr = stream.peer_addr().unwrap();
    println!("New connection: {}", client_addr);
    // let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");

    //获取客户端的请求方法
    let request_method = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
    let request_method = request_method.split(" ").nth(0).unwrap();
    println!("Request method: {}", request_method);

    //判断请求方法是否为GET
    if request_method != "GET" {
        let content = std::fs::read_to_string("404.html").unwrap();
        let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
        stream.write_all(response.as_bytes()).unwrap();
        stream.flush().unwrap();
        return;
    } else {
        //获取客户端的请求路径
        let request_path = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
        let request_path = request_path.split(" ").nth(1).unwrap();
        println!("Request path: {}", request_path);

        //判断请求路径是否为/
        if request_path == "/" {
            let content = std::fs::read_to_string("index.html").unwrap();
            let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", content);
            stream.write_all(response.as_bytes()).unwrap();
            stream.flush().unwrap();
            return;
        } else {
            let content = std::fs::read_to_string("404.html").unwrap();
            let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
            stream.write_all(response.as_bytes()).unwrap();
            stream.flush().unwrap();
            return;
        }
    }
}

fn main() -> std::io::Result<()> {
    //创建监听器
    let listener = TcpListener::bind("127.0.0.1:8080")?;

    //处理客户端请求
    //listener.incoming()返回一个迭代器,用于接收客户端的连接请求
    for stream in listener.incoming() {
        //处理客户端请求的逻辑
        //listener.incoming()返回的迭代器包含错误,需要使用?来处理
        handle_client(stream?);
    }
    Ok(())
}

get方法 /路径
在这里插入图片描述

get方法其他路径
在这里插入图片描述

代码优化,将一些重复的代码封装

use std::net::{ TcpListener, TcpStream }; //导入TcpListener和TcpStream
use std::io::{ Read, Write }; //导入Read和Write

fn handle_client(mut stream: TcpStream) {
    //读取客户端请求,每次读取1024个字节
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).unwrap();

    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&buffer[..]));

    //获取客户端地址
    let client_addr = stream.peer_addr().unwrap();
    println!("New connection: {}", client_addr);
    // let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");

    //获取客户端的请求方法
    let request_method = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
    let request_method = request_method.split(" ").nth(0).unwrap();
    println!("Request method: {}", request_method);

    //封装一个函数,响应客户端
    fn response_client(mut stream: TcpStream, response: String) {
        stream.write_all(response.as_bytes()).unwrap();
        stream.flush().unwrap();
    }

    //判断请求方法是否为GET
    if request_method != "GET" {
        let content = std::fs::read_to_string("404.html").unwrap();
        let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
        // stream.write_all(response.as_bytes()).unwrap();
        // stream.flush().unwrap();
        response_client(stream, response);
    } else {
        //获取客户端的请求路径
        let request_path = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
        let request_path = request_path.split(" ").nth(1).unwrap();
        println!("Request path: {}", request_path);

        //判断请求路径是否为/
        if request_path == "/" {
            let content = std::fs::read_to_string("index.html").unwrap();
            let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", content);
            // stream.write_all(response.as_bytes()).unwrap();
            // stream.flush().unwrap();
            response_client(stream, response);
        } else {
            let content = std::fs::read_to_string("404.html").unwrap();
            let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
            // stream.write_all(response.as_bytes()).unwrap();
            // stream.flush().unwrap();
            response_client(stream, response);
        }
    }
}

fn main() -> std::io::Result<()> {
    //创建监听器
    let listener = TcpListener::bind("127.0.0.1:8080")?;

    //处理客户端请求
    //listener.incoming()返回一个迭代器,用于接收客户端的连接请求
    for stream in listener.incoming() {
        //处理客户端请求的逻辑
        //listener.incoming()返回的迭代器包含错误,需要使用?来处理
        handle_client(stream?);
    }
    Ok(())
}

2.5 多线程的http服务器

单线程的的webserver存在的问题:
请求只能串行处理,也就是说当第一个连接处理完之前不会处理第二个连接。
这样,当有海量请求的时候,就会出问题
我们采用多线程

//多线程的http服务器
use std::thread;
use std::net::{ TcpListener, TcpStream };
use std::io::{ Read, Write };

fn handle_client(mut stream: TcpStream) {
    //读取客户端请求,每次读取1024个字节
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).unwrap();

    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&buffer[..]));

    //获取客户端地址
    let client_addr = stream.peer_addr().unwrap();
    println!("New connection: {}", client_addr);
    // let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");

    //获取客户端的请求方法
    let request_method = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
    let request_method = request_method.split(" ").nth(0).unwrap();
    println!("Request method: {}", request_method);

    //封装一个函数,响应客户端
    fn response_client(mut stream: TcpStream, response: String) {
        stream.write_all(response.as_bytes()).unwrap();
        stream.flush().unwrap();
    }

    //判断请求方法是否为GET
    if request_method != "GET" {
        let content = std::fs::read_to_string("404.html").unwrap();
        let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
        // stream.write_all(response.as_bytes()).unwrap();
        // stream.flush().unwrap();
        response_client(stream, response);
    } else {
        //获取客户端的请求路径
        let request_path = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
        let request_path = request_path.split(" ").nth(1).unwrap();
        println!("Request path: {}", request_path);

        //判断请求路径是否为/
        if request_path == "/" {
            let content = std::fs::read_to_string("index.html").unwrap();
            let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", content);
            // stream.write_all(response.as_bytes()).unwrap();
            // stream.flush().unwrap();
            response_client(stream, response);
        } else {
            let content = std::fs::read_to_string("404.html").unwrap();
            let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
            // stream.write_all(response.as_bytes()).unwrap();
            // stream.flush().unwrap();
            response_client(stream, response);
        }
    }
}

fn main() -> std::io::Result<()> {
    //创建监听器
    let listener = TcpListener::bind("127.0.0.1:8080")?;

    //创建线程句柄
    let mut handles = Vec::new();

    //处理客户端请求
    //listener.incoming()返回一个迭代器,用于接收客户端的连接请求
    for stream in listener.incoming() {
        //处理客户端请求的逻辑
        //使用多线程
        let handle = thread::spawn(move || {
            handle_client(stream.unwrap());
        });
        handles.push(handle);
    }
    //等待所有线程结束
    for handle in handles {
        handle.join().unwrap();
    }
    Ok(())
}

2.6 线程池webserver

上面通过多线程创建的webserver,当请求不断太多时,还是可以用一用。
但是当请求比较海量时,系统也会跟着创建海量的线程,最终造成系统资源耗尽而崩溃
此时,我们采用线程池来处理
在这里插入图片描述

多线程,管道
从主线程将任务发送到管道,工作线程等待在管道的接收端,当收到任务时,进行处理。

✅ 创建文件结构:
.
├── main.rs
├── lib.rs // 线程池模块

🔧 Cargo.toml(依赖可以不用加,使用标准库)

[package]
name = "myhttpserver3"
version = "0.1.0"
edition = "2024"

[dependencies]

📄 src/main.rs

use std::net::TcpListener;
use std::io::prelude::*;
use std::net::TcpStream;
use myhttpserver3::ThreadPool; //这里myhttpserver3是Cargo.toml中定义的依赖库名称,就是项目的名称

fn main() {
    //创建监听器,监听7878端口
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
    //创建线程池,线程池大小为4
    let pool = ThreadPool::new(4);

    println!("Server running on 127.0.0.1:7878");

    //使用线程池处理请求
    for stream in listener.incoming().take(10) {
        let stream = stream.unwrap();

        pool.execute(|| {
            handle_connection(stream);
        });
    }

    println!("Shutting down.");
}

fn handle_connection(mut stream: TcpStream) {
    //读取客户端请求,每次读取1024个字节
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).unwrap();

    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&buffer[..]));

    //获取客户端地址
    let client_addr = stream.peer_addr().unwrap();
    println!("New connection: {}", client_addr);
    // let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");

    //获取客户端的请求方法
    let request_method = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
    let request_method = request_method.split(" ").nth(0).unwrap();
    println!("Request method: {}", request_method);

    //封装一个函数,响应客户端
    fn response_client(mut stream: TcpStream, response: String) {
        stream.write_all(response.as_bytes()).unwrap();
        stream.flush().unwrap();
    }

    //判断请求方法是否为GET
    if request_method != "GET" {
        let content = std::fs::read_to_string("404.html").unwrap();
        let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
        // stream.write_all(response.as_bytes()).unwrap();
        // stream.flush().unwrap();
        response_client(stream, response);
    } else {
        //获取客户端的请求路径
        let request_path = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
        let request_path = request_path.split(" ").nth(1).unwrap();
        println!("Request path: {}", request_path);

        //判断请求路径是否为/
        if request_path == "/" {
            let content = std::fs::read_to_string("index.html").unwrap();
            let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", content);
            // stream.write_all(response.as_bytes()).unwrap();
            // stream.flush().unwrap();
            response_client(stream, response);
        } else {
            let content = std::fs::read_to_string("404.html").unwrap();
            let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
            // stream.write_all(response.as_bytes()).unwrap();
            // stream.flush().unwrap();
            response_client(stream, response);
        }
    }
}

📄 src/lib.rs

//线程池
use std::sync::{ Arc, Mutex };
use std::sync::mpsc;
use std::thread;

//定义一个结构体,表示线程池
#[allow(dead_code)]
pub struct ThreadPool {
    workers: Vec<Worker>,
    sender: mpsc::Sender<Job>,
}

//使用type关键字定义一个类型别名,表示任务。使用type起类型别名,用于简化代码
//这个类型是依照ThreadPool的excute()方法的参数类型来的
type Job = Box<dyn FnOnce() + Send + 'static>;

//为ThreadPool实现方法
impl ThreadPool {
    // 创建新线程池
    pub fn new(size: usize) -> ThreadPool {
        //线程池的大小必须大于0
        assert!(size > 0);
        println!("Creating a thread pool of size {}", size);

        //创建通道
        let (sender, receiver) = mpsc::channel();
        //将接收端放入互斥锁中,再放入Arc中,实现共享
        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 }
    }

    // 执行任务。这里是参照标准库 thread::spawn()的实现的
    //对F有约束
    pub fn execute<F>(&self, f: F) where F: FnOnce() + Send + 'static {
        //将任务包装成Box
        let job = Box::new(f);
        self.sender.send(job).unwrap();
    }
}

//定义一个结构体,表示工作线程
#[allow(dead_code)]
struct Worker {
    id: usize, //工作线程的id
    thread: thread::JoinHandle<()>, //线程句柄
}

//为Worker实现方法
impl Worker {
    //接收端需要线程安全,所以需要Arc<Mutex<T>>
    fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
        //创建工作线程
        let thread = thread::spawn(move || {
            //循环从通道中接收任务,并执行
            loop {
                //recv会阻塞线程,直到有数据可读
                let job = receiver.lock().unwrap().recv().unwrap();
                println!("Worker {} got a job; executing.", id);
                //执行任务
                job();
            }
        });

        //返回工作线程
        Worker { id, thread }
    }
}

📄 index.html(放在项目根目录)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>Hello, Jingtian!</h1>
  </body>
</html>

📄 404.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>Oops!</h1>
    <p>The page you are looking for does not exist.</p>
  </body>
</html>

运行服务器
cargo run

然后在浏览器打开 http://127.0.0.1:7878/
在这里插入图片描述

如果是其他路径
在这里插入图片描述

2.7 实现线程池清除的webserver

在之前的用线程池实现的webserver中,每个工作线程中通过loop进行循环,从channel的接收端等待任务,然后执行。
但是在代码中,work采用的是loop循环,没有跳出循环的条件,没有提供一种机制,来通知工作线程结束。
现在我们就来实现线程池对象的正确清除。
通过为ThreadPool实现Drop trait来实现线程池对象清除

修改Worker如下:

struct Worker {
    id: usize, //工作线程的id
    //线程句柄,将thread::JoinHandle<()>包装成Option,用于在drop()方法中调用take()方法
    //Option中有take()方法,可以将Some中的值取出来,同时将Some置为None
    thread: Option<thread::JoinHandle<()>>,
}

Option中有take方法
在这里插入图片描述

完成的代码:
src/lib.rs

//线程池
use std::sync::{ Arc, Mutex };
use std::sync::mpsc;
use std::thread;

//定义一个结构体,表示线程池
#[allow(dead_code)]
pub struct ThreadPool {
    workers: Vec<Worker>,
    // sender: mpsc::Sender<Job>,
    sender: mpsc::Sender<Message>,
}

//使用type关键字定义一个类型别名,表示任务。使用type起类型别名,用于简化代码
//这个类型是依照ThreadPool的excute()方法的参数类型来的
type Job = Box<dyn FnOnce() + Send + 'static>;

//发送结束消息给worker,所有发送job的地方都要修改
enum Message {
    //两种情况
    NewJob(Job),
    Terminate,
}

//为ThreadPool实现方法
impl ThreadPool {
    // 创建新线程池
    pub fn new(size: usize) -> ThreadPool {
        //线程池的大小必须大于0
        assert!(size > 0);
        println!("Creating a thread pool of size {}", size);

        //创建通道
        let (sender, receiver) = mpsc::channel();
        //将接收端放入互斥锁中,再放入Arc中,实现共享
        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 }
    }

    // 执行任务。这里是参照标准库 thread::spawn()的实现的
    //对F有约束
    pub fn execute<F>(&self, f: F) where F: FnOnce() + Send + 'static {
        //将任务包装成Box
        let job = Box::new(f);
        // self.sender.send(job).unwrap();
        self.sender.send(Message::NewJob(job)).unwrap();
    }
}

//为ThreadPool实现Drop trait
impl Drop for ThreadPool {
    //当线程池被销毁时,关闭所有工作线程
    //实现Drop trait,只需要实现drop()方法即可
    fn drop(&mut self) {
        //发送结束消息给worker
        for _ in &self.workers {
            self.sender.send(Message::Terminate).unwrap();
        }
        //等待所有工作线程结束
        for worker in &mut self.workers {
            println!("Shutting down worker {}", worker.id);
            //等待工作线程结束
            if let Some(thread) = worker.thread.take() {
                thread.join().unwrap();
            }
        }
    }
}

//定义一个结构体,表示工作线程
#[allow(dead_code)]
struct Worker {
    id: usize, //工作线程的id
    //线程句柄,将thread::JoinHandle<()>包装成Option,用于在drop()方法中调用take()方法
    //Option中有take()方法,可以将Some中的值取出来,同时将Some置为None
    thread: Option<thread::JoinHandle<()>>,
}

//为Worker实现方法
impl Worker {
    //接收端需要线程安全,所以需要Arc<Mutex<T>>
    fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker {
        //创建工作线程
        let thread = thread::spawn(move || {
            //循环从通道中接收任务,并执行
            loop {
                //recv会阻塞线程,直到有数据可读
                // let job = receiver.lock().unwrap().recv().unwrap();
                let message = receiver.lock().unwrap().recv().unwrap();
                // println!("Worker {} got a job; executing.", id);
                //判断消息类型
                match message {
                    Message::NewJob(job) => {
                        println!("Worker {} got a job; executing.", id);
                        job();
                    }
                    Message::Terminate => {
                        println!("Worker {} was told to terminate.", id);
                        //收到结束消息,退出循环
                        break;
                    }
                }
            }
        });

        //返回工作线程
        Worker { id, thread: Some(thread) }
    }
}

src/main.rs

use std::net::TcpListener;
use std::io::prelude::*;
use std::net::TcpStream;
use myhttpserver4::ThreadPool; //这里myhttpserver3是Cargo.toml中定义的依赖库名称,就是项目的名称

fn main() {
    //创建监听器,监听7878端口
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
    //创建线程池,线程池大小为4
    let pool = ThreadPool::new(4);

    println!("Server running on 127.0.0.1:7878");

    //使用线程池处理请求
    //listener.incoming()返回一个迭代器,用于接收客户端的连接请求
    //take(4)表示只接收4个连接请求,可以根据实际情况调整
    for stream in listener.incoming().take(4) {
        let stream = stream.unwrap();

        pool.execute(|| {
            handle_connection(stream);
        });
    }

    println!("Shutting down.");
}

fn handle_connection(mut stream: TcpStream) {
    //读取客户端请求,每次读取1024个字节
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).unwrap();

    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&buffer[..]));

    //获取客户端地址
    let client_addr = stream.peer_addr().unwrap();
    println!("New connection: {}", client_addr);
    // let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");

    //获取客户端的请求方法
    let request_method = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
    let request_method = request_method.split(" ").nth(0).unwrap();
    println!("Request method: {}", request_method);

    //封装一个函数,响应客户端
    fn response_client(mut stream: TcpStream, response: String) {
        stream.write_all(response.as_bytes()).unwrap();
        stream.flush().unwrap();
    }

    //判断请求方法是否为GET
    if request_method != "GET" {
        let content = std::fs::read_to_string("404.html").unwrap();
        let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
        // stream.write_all(response.as_bytes()).unwrap();
        // stream.flush().unwrap();
        response_client(stream, response);
    } else {
        //获取客户端的请求路径
        let request_path = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
        let request_path = request_path.split(" ").nth(1).unwrap();
        println!("Request path: {}", request_path);

        //判断请求路径是否为/
        if request_path == "/" {
            let content = std::fs::read_to_string("index.html").unwrap();
            let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", content);
            // stream.write_all(response.as_bytes()).unwrap();
            // stream.flush().unwrap();
            response_client(stream, response);
        } else {
            let content = std::fs::read_to_string("404.html").unwrap();
            let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
            // stream.write_all(response.as_bytes()).unwrap();
            // stream.flush().unwrap();
            response_client(stream, response);
        }
    }
}

接收4个请求后,服务器就关闭


网站公告

今日签到

点亮在社区的每一天
去签到