谭春华 發表於 2025-6-26 11:02:32

Rust搭建webserver的底层原理与应用实战技巧

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>Rust http编程</li><ul class="second_class_ul"><li>1. HTTP基础与Rust生态系统</li><ul class="third_class_ul"><li>1.1 HTTP协议回顾</li><li>1.2 Rust HTTP生态系统概览</li></ul><li>2. 使用标准库进行HTTP编程</li><ul class="third_class_ul"><li>2.1 基本HTTP服务端</li><li>2.2 简单HTTP客户端</li><li>2.3 服务端响应网页</li><li>2.4 有条件地响应网页</li><li>2.5 多线程的http服务器</li><li>2.6 线程池webserver</li><li>2.7 实现线程池清除的webserver</li></ul></ul></ul></div><p class="maodian"></p><h2>Rust http编程</h2>
<p>Rust作为一门系统级编程语言,凭借其出色的性能、内存安全性和并发特性,在网络编程领域展现出强大的潜力。<br />本文将详细介绍如何使用Rust进行HTTP编程,涵盖从基础概念到实际应用的各个方面。</p>
<p class="maodian"></p><h3>1. HTTP基础与Rust生态系统</h3>
<p class="maodian"></p><h4>1.1 HTTP协议回顾</h4>
<p>HTTP(HyperText Transfer Protocol)是应用层协议,基于请求-响应模型工作。Rust提供了多种处理HTTP协议的方式:<br />标准库:基础但功能有限<br />第三方库:功能丰富,如reqwest、hyper等<br />Web框架:如actix-web、rocket等</p>
<p class="maodian"></p><h4>1.2 Rust HTTP生态系统概览</h4>
<p>Rust的HTTP生态系统包含多个层次的组件:<br />底层库:hyper、h2、http等<br />客户端库:reqwest、ureq等<br />服务器框架:actix-web、rocket、warp等<br />工具库:serde(序列化)、tokio(异步运行时)等</p>
<p class="maodian"></p><h3>2. 使用标准库进行HTTP编程</h3>
<p>虽然不推荐在生产环境中使用标准库进行HTTP编程,但了解其基本用法有助于理解底层原理。<br />可以参考官方标准库net库 https://doc.rust-lang.org/stable/std/net/index.html<br />TcpListener可以创建http客户端和服务端</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202506/2025062610565144.png" /></p>
<p><strong>HTTP简单介绍</strong><br />(1)http请求报文包含三个部分内容 :请求行、请求头 、请求体<br />Method Request-URI HTTP-Version CRLF //请求行:请求方式、协议版本等<br />headers CRLF //请求头:包含若干个属性,格式为&ldquo;属性名:属性值&rdquo;,格式为&quot;属性名:属性值&quot;,服务端据此获取客户端的信息<br />message-body //请求体 :客户端真正要传送给服务端的内容</p>
<p>(2)http响应报文也有三部分内容:响应行、响应头、响应体<br />HTTP-Version status-Code Reason-Phrase CRLF //响应行:报文协议及版本,状态码及状态描述<br />headers CRLF //响应头:由多个属性组成<br />message-body //响应体:真正响应的内容</p>
<p class="maodian"></p><h4>2.1 基本HTTP服务端</h4>
<p>主要使用标准库中的net库和io库</p>
<div class="jb51code"><pre class="brush:plain;">use std::net::{ TcpListener, TcpStream }; //导入TcpListener和TcpStream
use std::io::{ Read, Write }; //导入Read和Write
fn handle_client(mut stream: TcpStream) {
    //读取客户端请求,每次读取1024个字节
    let mut buffer = ;
    stream.read(&amp;mut buffer).unwrap();
    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&amp;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() -&gt; std::io::Result&lt;()&gt; {
    //创建监听器
    let listener = TcpListener::bind("127.0.0.1:8080")?;
    //处理客户端请求
    //listener.incoming()返回一个迭代器,用于接收客户端的连接请求
    for stream in listener.incoming() {
      //处理客户端请求的逻辑
      //listener.incoming()返回的迭代器包含错误,需要使用?来处理
      handle_client(stream?);
    }
    Ok(())
}</pre></div>
<p class="maodian"></p><h4>2.2 简单HTTP客户端</h4>
<div class="jb51code"><pre class="brush:plain;">use std::io::{ Read, Write };
use std::net::TcpStream;
fn main() -&gt; std::io::Result&lt;()&gt; {
    //创建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(&amp;mut buffer)?;
    //打印服务器的响应
    println!("{}", String::from_utf8_lossy(&amp;buffer));
    Ok(())
}</pre></div>
<p>服务端收到客户端请求</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202506/2025062610565068.png" /></p>
<p>客户端收到服务端响应</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202506/2025062610565036.png" /></p>
<p class="maodian"></p><h4>2.3 服务端响应网页</h4>
<div class="jb51code"><pre class="brush:plain;">use std::net::{ TcpListener, TcpStream }; //导入TcpListener和TcpStream
use std::io::{ Read, Write }; //导入Read和Write
fn handle_client(mut stream: TcpStream) {
    //读取客户端请求,每次读取1024个字节
    let mut buffer = ;
    stream.read(&amp;mut buffer).unwrap();
    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&amp;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() -&gt; std::io::Result&lt;()&gt; {
    //创建监听器
    let listener = TcpListener::bind("127.0.0.1:8080")?;
    //处理客户端请求
    //listener.incoming()返回一个迭代器,用于接收客户端的连接请求
    for stream in listener.incoming() {
      //处理客户端请求的逻辑
      //listener.incoming()返回的迭代器包含错误,需要使用?来处理
      handle_client(stream?);
    }
    Ok(())
}</pre></div>
<p>直接浏览器访问查看</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202506/2025062610565017.png" /></p>
<p class="maodian"></p><h4>2.4 有条件地响应网页</h4>
<p>有条件地响应网页,主要是对客户端的请求进行判断,不同的请求路径、请求方法等响应不同内容</p>
<div class="jb51code"><pre class="brush:plain;">use std::net::{ TcpListener, TcpStream }; //导入TcpListener和TcpStream
use std::io::{ Read, Write }; //导入Read和Write
fn handle_client(mut stream: TcpStream) {
    //读取客户端请求,每次读取1024个字节
    let mut buffer = ;
    stream.read(&amp;mut buffer).unwrap();
    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&amp;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(&amp;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(&amp;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() -&gt; std::io::Result&lt;()&gt; {
    //创建监听器
    let listener = TcpListener::bind("127.0.0.1:8080")?;
    //处理客户端请求
    //listener.incoming()返回一个迭代器,用于接收客户端的连接请求
    for stream in listener.incoming() {
      //处理客户端请求的逻辑
      //listener.incoming()返回的迭代器包含错误,需要使用?来处理
      handle_client(stream?);
    }
    Ok(())
}</pre></div>
<p>get方法 /路径</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202506/2025062610565057.png" /></p>
<p>get方法其他路径</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202506/2025062610565068.png" /></p>
<p><strong>代码优化,将一些重复的代码封装</strong></p>
<div class="jb51code"><pre class="brush:plain;">use std::net::{ TcpListener, TcpStream }; //导入TcpListener和TcpStream
use std::io::{ Read, Write }; //导入Read和Write
fn handle_client(mut stream: TcpStream) {
    //读取客户端请求,每次读取1024个字节
    let mut buffer = ;
    stream.read(&amp;mut buffer).unwrap();
    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&amp;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(&amp;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(&amp;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() -&gt; std::io::Result&lt;()&gt; {
    //创建监听器
    let listener = TcpListener::bind("127.0.0.1:8080")?;
    //处理客户端请求
    //listener.incoming()返回一个迭代器,用于接收客户端的连接请求
    for stream in listener.incoming() {
      //处理客户端请求的逻辑
      //listener.incoming()返回的迭代器包含错误,需要使用?来处理
      handle_client(stream?);
    }
    Ok(())
}</pre></div>
<p class="maodian"></p><h4>2.5 多线程的http服务器</h4>
<p>单线程的的webserver存在的问题:<br />请求只能串行处理,也就是说当第一个连接处理完之前不会处理第二个连接。<br />这样,当有海量请求的时候,就会出问题<br />我们采用多线程</p>
<div class="jb51code"><pre class="brush:plain;">//多线程的http服务器
use std::thread;
use std::net::{ TcpListener, TcpStream };
use std::io::{ Read, Write };
fn handle_client(mut stream: TcpStream) {
    //读取客户端请求,每次读取1024个字节
    let mut buffer = ;
    stream.read(&amp;mut buffer).unwrap();
    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&amp;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(&amp;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(&amp;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() -&gt; std::io::Result&lt;()&gt; {
    //创建监听器
    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(())
}</pre></div>
<p class="maodian"></p><h4>2.6 线程池webserver</h4>
<p>上面通过多线程创建的webserver,当请求不断太多时,还是可以用一用。<br />但是当请求比较海量时,系统也会跟着创建海量的线程,最终造成系统资源耗尽而崩溃<br />此时,我们采用线程池来处理</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202506/2025062610565039.png" /></p>
<p>多线程,管道<br />从主线程将任务发送到管道,工作线程等待在管道的接收端,当收到任务时,进行处理。</p>
<p>✅ 创建文件结构:<br />.<br />├── main.rs<br />├── lib.rs // 线程池模块</p>
<p>🔧 Cargo.toml(依赖可以不用加,使用标准库)</p>
<div class="jb51code"><pre class="brush:plain;">
name = "myhttpserver3"
version = "0.1.0"
edition = "2024"
</pre></div>
<p>📄 src/main.rs</p>
<div class="jb51code"><pre class="brush:plain;">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 = ;
    stream.read(&amp;mut buffer).unwrap();
    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&amp;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(&amp;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(&amp;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);
      }
    }
}</pre></div>
<p>📄 src/lib.rs</p>
<div class="jb51code"><pre class="brush:plain;">//线程池
use std::sync::{ Arc, Mutex };
use std::sync::mpsc;
use std::thread;
//定义一个结构体,表示线程池
#
pub struct ThreadPool {
    workers: Vec&lt;Worker&gt;,
    sender: mpsc::Sender&lt;Job&gt;,
}
//使用type关键字定义一个类型别名,表示任务。使用type起类型别名,用于简化代码
//这个类型是依照ThreadPool的excute()方法的参数类型来的
type Job = Box&lt;dyn FnOnce() + Send + 'static&gt;;
//为ThreadPool实现方法
impl ThreadPool {
    // 创建新线程池
    pub fn new(size: usize) -&gt; ThreadPool {
      //线程池的大小必须大于0
      assert!(size &gt; 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(&amp;receiver)));
      }
      //返回线程池
      ThreadPool { workers, sender }
    }
    // 执行任务。这里是参照标准库 thread::spawn()的实现的
    //对F有约束
    pub fn execute&lt;F&gt;(&amp;self, f: F) where F: FnOnce() + Send + 'static {
      //将任务包装成Box
      let job = Box::new(f);
      self.sender.send(job).unwrap();
    }
}
//定义一个结构体,表示工作线程
#
struct Worker {
    id: usize, //工作线程的id
    thread: thread::JoinHandle&lt;()&gt;, //线程句柄
}
//为Worker实现方法
impl Worker {
    //接收端需要线程安全,所以需要Arc&lt;Mutex&lt;T&gt;&gt;
    fn new(id: usize, receiver: Arc&lt;Mutex&lt;mpsc::Receiver&lt;Job&gt;&gt;&gt;) -&gt; 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 }
    }
}</pre></div>
<p>📄 index.html(放在项目根目录)</p>
<div class="jb51code"><pre class="brush:xhtml;">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8" /&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;
    &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Hello, Jingtian!&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;</pre></div>
<p>📄 404.html</p>
<div class="jb51code"><pre class="brush:xhtml;">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8" /&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;
    &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Oops!&lt;/h1&gt;
    &lt;p&gt;The page you are looking for does not exist.&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</pre></div>
<p>运行服务器<br />cargo run</p>
<p>然后在浏览器打开 http://127.0.0.1:7878/</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202506/2025062610565064.png" /></p>
<p>如果是其他路径</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202506/2025062610565093.png" /></p>
<p class="maodian"></p><h4>2.7 实现线程池清除的webserver</h4>
<p>在之前的用线程池实现的webserver中,每个工作线程中通过loop进行循环,从channel的接收端等待任务,然后执行。<br />但是在代码中,work采用的是loop循环,没有跳出循环的条件,没有提供一种机制,来通知工作线程结束。<br />现在我们就来实现线程池对象的正确清除。<br />通过为ThreadPool实现Drop trait来实现线程池对象清除</p>
<p>修改Worker如下:</p>
<div class="jb51code"><pre class="brush:plain;">struct Worker {
    id: usize, //工作线程的id
    //线程句柄,将thread::JoinHandle&lt;()&gt;包装成Option,用于在drop()方法中调用take()方法
    //Option中有take()方法,可以将Some中的值取出来,同时将Some置为None
    thread: Option&lt;thread::JoinHandle&lt;()&gt;&gt;,
}</pre></div>
<p>Option中有take方法</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202506/2025062610565084.png" /></p>
<p>完成的代码:<br />src/lib.rs</p>
<div class="jb51code"><pre class="brush:plain;">//线程池
use std::sync::{ Arc, Mutex };
use std::sync::mpsc;
use std::thread;
//定义一个结构体,表示线程池
#
pub struct ThreadPool {
    workers: Vec&lt;Worker&gt;,
    // sender: mpsc::Sender&lt;Job&gt;,
    sender: mpsc::Sender&lt;Message&gt;,
}
//使用type关键字定义一个类型别名,表示任务。使用type起类型别名,用于简化代码
//这个类型是依照ThreadPool的excute()方法的参数类型来的
type Job = Box&lt;dyn FnOnce() + Send + 'static&gt;;
//发送结束消息给worker,所有发送job的地方都要修改
enum Message {
    //两种情况
    NewJob(Job),
    Terminate,
}
//为ThreadPool实现方法
impl ThreadPool {
    // 创建新线程池
    pub fn new(size: usize) -&gt; ThreadPool {
      //线程池的大小必须大于0
      assert!(size &gt; 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(&amp;receiver)));
      }
      //返回线程池
      ThreadPool { workers, sender }
    }
    // 执行任务。这里是参照标准库 thread::spawn()的实现的
    //对F有约束
    pub fn execute&lt;F&gt;(&amp;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(&amp;mut self) {
      //发送结束消息给worker
      for _ in &amp;self.workers {
            self.sender.send(Message::Terminate).unwrap();
      }
      //等待所有工作线程结束
      for worker in &amp;mut self.workers {
            println!("Shutting down worker {}", worker.id);
            //等待工作线程结束
            if let Some(thread) = worker.thread.take() {
                thread.join().unwrap();
            }
      }
    }
}
//定义一个结构体,表示工作线程
#
struct Worker {
    id: usize, //工作线程的id
    //线程句柄,将thread::JoinHandle&lt;()&gt;包装成Option,用于在drop()方法中调用take()方法
    //Option中有take()方法,可以将Some中的值取出来,同时将Some置为None
    thread: Option&lt;thread::JoinHandle&lt;()&gt;&gt;,
}
//为Worker实现方法
impl Worker {
    //接收端需要线程安全,所以需要Arc&lt;Mutex&lt;T&gt;&gt;
    fn new(id: usize, receiver: Arc&lt;Mutex&lt;mpsc::Receiver&lt;Message&gt;&gt;&gt;) -&gt; 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) =&gt; {
                        println!("Worker {} got a job; executing.", id);
                        job();
                  }
                  Message::Terminate =&gt; {
                        println!("Worker {} was told to terminate.", id);
                        //收到结束消息,退出循环
                        break;
                  }
                }
            }
      });
      //返回工作线程
      Worker { id, thread: Some(thread) }
    }
}</pre></div>
<p>src/main.rs</p>
<div class="jb51code"><pre class="brush:plain;">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 = ;
    stream.read(&amp;mut buffer).unwrap();
    //打印客户端请求
    println!("Request: {}", String::from_utf8_lossy(&amp;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(&amp;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(&amp;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);
      }
    }
}</pre></div>
<p>接收4个请求后,服务器就关闭</p>
<p>到此这篇关于Rust搭建webserver的底层原理与应用实战的文章就介绍到这了,更多相关Rust webserver底层原理内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: Rust搭建webserver的底层原理与应用实战技巧