Rust搭建webserver的底層原理與應(yīng)用實(shí)戰(zhàn)技巧
Rust http編程
Rust作為一門系統(tǒng)級編程語言,憑借其出色的性能、內(nèi)存安全性和并發(fā)特性,在網(wǎng)絡(luò)編程領(lǐng)域展現(xiàn)出強(qiáng)大的潛力。
本文將詳細(xì)介紹如何使用Rust進(jìn)行HTTP編程,涵蓋從基礎(chǔ)概念到實(shí)際應(yīng)用的各個方面。
1. HTTP基礎(chǔ)與Rust生態(tài)系統(tǒng)
1.1 HTTP協(xié)議回顧
HTTP(HyperText Transfer Protocol)是應(yīng)用層協(xié)議,基于請求-響應(yīng)模型工作。Rust提供了多種處理HTTP協(xié)議的方式:
標(biāo)準(zhǔn)庫:基礎(chǔ)但功能有限
第三方庫:功能豐富,如reqwest、hyper等
Web框架:如actix-web、rocket等
1.2 Rust HTTP生態(tài)系統(tǒng)概覽
Rust的HTTP生態(tài)系統(tǒng)包含多個層次的組件:
底層庫:hyper、h2、http等
客戶端庫:reqwest、ureq等
服務(wù)器框架:actix-web、rocket、warp等
工具庫:serde(序列化)、tokio(異步運(yùn)行時)等
2. 使用標(biāo)準(zhǔn)庫進(jìn)行HTTP編程
雖然不推薦在生產(chǎn)環(huán)境中使用標(biāo)準(zhǔn)庫進(jìn)行HTTP編程,但了解其基本用法有助于理解底層原理。
可以參考官方標(biāo)準(zhǔn)庫net庫 https://doc.rust-lang.org/stable/std/net/index.html
TcpListener可以創(chuàng)建http客戶端和服務(wù)端

HTTP簡單介紹
(1)http請求報(bào)文包含三個部分內(nèi)容 :請求行、請求頭 、請求體
Method Request-URI HTTP-Version CRLF //請求行:請求方式、協(xié)議版本等
headers CRLF //請求頭:包含若干個屬性,格式為“屬性名:屬性值”,格式為"屬性名:屬性值",服務(wù)端據(jù)此獲取客戶端的信息
message-body //請求體 :客戶端真正要傳送給服務(wù)端的內(nèi)容
(2)http響應(yīng)報(bào)文也有三部分內(nèi)容:響應(yīng)行、響應(yīng)頭、響應(yīng)體
HTTP-Version status-Code Reason-Phrase CRLF //響應(yīng)行:報(bào)文協(xié)議及版本,狀態(tài)碼及狀態(tài)描述
headers CRLF //響應(yīng)頭:由多個屬性組成
message-body //響應(yīng)體:真正響應(yīng)的內(nèi)容
2.1 基本HTTP服務(wù)端
主要使用標(biāo)準(zhǔn)庫中的net庫和io庫
use std::net::{ TcpListener, TcpStream }; //導(dǎo)入TcpListener和TcpStream
use std::io::{ Read, Write }; //導(dǎo)入Read和Write
fn handle_client(mut stream: TcpStream) {
//讀取客戶端請求,每次讀取1024個字節(jié)
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
//打印客戶端請求
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
//構(gòu)建http響應(yīng),向客戶端打招呼
//獲取客戶端地址
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}!");
//將響應(yīng)寫入到客戶端
stream.write_all(response.as_bytes()).unwrap();
//刷新緩沖區(qū)
stream.flush().unwrap();
}
fn main() -> std::io::Result<()> {
//創(chuàng)建監(jiān)聽器
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<()> {
//創(chuàng)建TCP連接
let mut stream = TcpStream::connect("localhost:8080")?;
//構(gòu)建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())?;
//創(chuàng)建個緩沖區(qū),用于讀取服務(wù)器的響應(yīng)
let mut buffer = Vec::new();
//讀取服務(wù)器的響應(yīng)
stream.read_to_end(&mut buffer)?;
//打印服務(wù)器的響應(yīng)
println!("{}", String::from_utf8_lossy(&buffer));
Ok(())
}服務(wù)端收到客戶端請求

客戶端收到服務(wù)端響應(yīng)

2.3 服務(wù)端響應(yīng)網(wǎng)頁
use std::net::{ TcpListener, TcpStream }; //導(dǎo)入TcpListener和TcpStream
use std::io::{ Read, Write }; //導(dǎo)入Read和Write
fn handle_client(mut stream: TcpStream) {
//讀取客戶端請求,每次讀取1024個字節(jié)
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
//打印客戶端請求
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
//構(gòu)建http響應(yīng),向客戶端打招呼
//獲取客戶端地址
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}!");
//從文件讀取內(nèi)容響應(yīng)給客戶端
let content = std::fs::read_to_string("index.html").unwrap();
let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", content);
//將響應(yīng)寫入到客戶端
stream.write_all(response.as_bytes()).unwrap();
//刷新緩沖區(qū)
stream.flush().unwrap();
}
fn main() -> std::io::Result<()> {
//創(chuàng)建監(jiān)聽器
let listener = TcpListener::bind("127.0.0.1:8080")?;
//處理客戶端請求
//listener.incoming()返回一個迭代器,用于接收客戶端的連接請求
for stream in listener.incoming() {
//處理客戶端請求的邏輯
//listener.incoming()返回的迭代器包含錯誤,需要使用?來處理
handle_client(stream?);
}
Ok(())
}直接瀏覽器訪問查看

2.4 有條件地響應(yīng)網(wǎng)頁
有條件地響應(yīng)網(wǎng)頁,主要是對客戶端的請求進(jìn)行判斷,不同的請求路徑、請求方法等響應(yīng)不同內(nèi)容
use std::net::{ TcpListener, TcpStream }; //導(dǎo)入TcpListener和TcpStream
use std::io::{ Read, Write }; //導(dǎo)入Read和Write
fn handle_client(mut stream: TcpStream) {
//讀取客戶端請求,每次讀取1024個字節(jié)
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
//打印客戶端請求
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
//構(gòu)建http響應(yīng),向客戶端打招呼
//獲取客戶端地址
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<()> {
//創(chuàng)建監(jiān)聽器
let listener = TcpListener::bind("127.0.0.1:8080")?;
//處理客戶端請求
//listener.incoming()返回一個迭代器,用于接收客戶端的連接請求
for stream in listener.incoming() {
//處理客戶端請求的邏輯
//listener.incoming()返回的迭代器包含錯誤,需要使用?來處理
handle_client(stream?);
}
Ok(())
}get方法 /路徑

get方法其他路徑

代碼優(yōu)化,將一些重復(fù)的代碼封裝
use std::net::{ TcpListener, TcpStream }; //導(dǎo)入TcpListener和TcpStream
use std::io::{ Read, Write }; //導(dǎo)入Read和Write
fn handle_client(mut stream: TcpStream) {
//讀取客戶端請求,每次讀取1024個字節(jié)
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);
//封裝一個函數(shù),響應(yīng)客戶端
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<()> {
//創(chuàng)建監(jiān)聽器
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服務(wù)器
單線程的的webserver存在的問題:
請求只能串行處理,也就是說當(dāng)?shù)谝粋€連接處理完之前不會處理第二個連接。
這樣,當(dāng)有海量請求的時候,就會出問題
我們采用多線程
//多線程的http服務(wù)器
use std::thread;
use std::net::{ TcpListener, TcpStream };
use std::io::{ Read, Write };
fn handle_client(mut stream: TcpStream) {
//讀取客戶端請求,每次讀取1024個字節(jié)
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);
//封裝一個函數(shù),響應(yīng)客戶端
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<()> {
//創(chuàng)建監(jiān)聽器
let listener = TcpListener::bind("127.0.0.1:8080")?;
//創(chuàng)建線程句柄
let mut handles = Vec::new();
//處理客戶端請求
//listener.incoming()返回一個迭代器,用于接收客戶端的連接請求
for stream in listener.incoming() {
//處理客戶端請求的邏輯
//使用多線程
let handle = thread::spawn(move || {
handle_client(stream.unwrap());
});
handles.push(handle);
}
//等待所有線程結(jié)束
for handle in handles {
handle.join().unwrap();
}
Ok(())
}2.6 線程池webserver
上面通過多線程創(chuàng)建的webserver,當(dāng)請求不斷太多時,還是可以用一用。
但是當(dāng)請求比較海量時,系統(tǒng)也會跟著創(chuàng)建海量的線程,最終造成系統(tǒng)資源耗盡而崩潰
此時,我們采用線程池來處理

多線程,管道
從主線程將任務(wù)發(fā)送到管道,工作線程等待在管道的接收端,當(dāng)收到任務(wù)時,進(jìn)行處理。
? 創(chuàng)建文件結(jié)構(gòu):
.
├── main.rs
├── lib.rs // 線程池模塊
?? Cargo.toml(依賴可以不用加,使用標(biāo)準(zhǔn)庫)
[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中定義的依賴庫名稱,就是項(xiàng)目的名稱
fn main() {
//創(chuàng)建監(jiān)聽器,監(jiān)聽7878端口
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
//創(chuàng)建線程池,線程池大小為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個字節(jié)
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);
//封裝一個函數(shù),響應(yīng)客戶端
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;
//定義一個結(jié)構(gòu)體,表示線程池
#[allow(dead_code)]
pub struct ThreadPool {
workers: Vec<Worker>,
sender: mpsc::Sender<Job>,
}
//使用type關(guān)鍵字定義一個類型別名,表示任務(wù)。使用type起類型別名,用于簡化代碼
//這個類型是依照ThreadPool的excute()方法的參數(shù)類型來的
type Job = Box<dyn FnOnce() + Send + 'static>;
//為ThreadPool實(shí)現(xiàn)方法
impl ThreadPool {
// 創(chuàng)建新線程池
pub fn new(size: usize) -> ThreadPool {
//線程池的大小必須大于0
assert!(size > 0);
println!("Creating a thread pool of size {}", size);
//創(chuàng)建通道
let (sender, receiver) = mpsc::channel();
//將接收端放入互斥鎖中,再放入Arc中,實(shí)現(xiàn)共享
let receiver = Arc::new(Mutex::new(receiver));
//創(chuàng)建線程池
let mut workers = Vec::with_capacity(size);
//創(chuàng)建工作線程
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
//返回線程池
ThreadPool { workers, sender }
}
// 執(zhí)行任務(wù)。這里是參照標(biāo)準(zhǔn)庫 thread::spawn()的實(shí)現(xiàn)的
//對F有約束
pub fn execute<F>(&self, f: F) where F: FnOnce() + Send + 'static {
//將任務(wù)包裝成Box
let job = Box::new(f);
self.sender.send(job).unwrap();
}
}
//定義一個結(jié)構(gòu)體,表示工作線程
#[allow(dead_code)]
struct Worker {
id: usize, //工作線程的id
thread: thread::JoinHandle<()>, //線程句柄
}
//為Worker實(shí)現(xiàn)方法
impl Worker {
//接收端需要線程安全,所以需要Arc<Mutex<T>>
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
//創(chuàng)建工作線程
let thread = thread::spawn(move || {
//循環(huán)從通道中接收任務(wù),并執(zhí)行
loop {
//recv會阻塞線程,直到有數(shù)據(jù)可讀
let job = receiver.lock().unwrap().recv().unwrap();
println!("Worker {} got a job; executing.", id);
//執(zhí)行任務(wù)
job();
}
});
//返回工作線程
Worker { id, thread }
}
}?? index.html(放在項(xiàng)目根目錄)
<!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>運(yùn)行服務(wù)器
cargo run
然后在瀏覽器打開 http://127.0.0.1:7878/

如果是其他路徑

2.7 實(shí)現(xiàn)線程池清除的webserver
在之前的用線程池實(shí)現(xiàn)的webserver中,每個工作線程中通過loop進(jìn)行循環(huán),從channel的接收端等待任務(wù),然后執(zhí)行。
但是在代碼中,work采用的是loop循環(huán),沒有跳出循環(huán)的條件,沒有提供一種機(jī)制,來通知工作線程結(jié)束。
現(xiàn)在我們就來實(shí)現(xiàn)線程池對象的正確清除。
通過為ThreadPool實(shí)現(xiàn)Drop trait來實(shí)現(xiàn)線程池對象清除
修改Worker如下:
struct Worker {
id: usize, //工作線程的id
//線程句柄,將thread::JoinHandle<()>包裝成Option,用于在drop()方法中調(diào)用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;
//定義一個結(jié)構(gòu)體,表示線程池
#[allow(dead_code)]
pub struct ThreadPool {
workers: Vec<Worker>,
// sender: mpsc::Sender<Job>,
sender: mpsc::Sender<Message>,
}
//使用type關(guān)鍵字定義一個類型別名,表示任務(wù)。使用type起類型別名,用于簡化代碼
//這個類型是依照ThreadPool的excute()方法的參數(shù)類型來的
type Job = Box<dyn FnOnce() + Send + 'static>;
//發(fā)送結(jié)束消息給worker,所有發(fā)送job的地方都要修改
enum Message {
//兩種情況
NewJob(Job),
Terminate,
}
//為ThreadPool實(shí)現(xiàn)方法
impl ThreadPool {
// 創(chuàng)建新線程池
pub fn new(size: usize) -> ThreadPool {
//線程池的大小必須大于0
assert!(size > 0);
println!("Creating a thread pool of size {}", size);
//創(chuàng)建通道
let (sender, receiver) = mpsc::channel();
//將接收端放入互斥鎖中,再放入Arc中,實(shí)現(xiàn)共享
let receiver = Arc::new(Mutex::new(receiver));
//創(chuàng)建線程池
let mut workers = Vec::with_capacity(size);
//創(chuàng)建工作線程
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
//返回線程池
ThreadPool { workers, sender }
}
// 執(zhí)行任務(wù)。這里是參照標(biāo)準(zhǔn)庫 thread::spawn()的實(shí)現(xiàn)的
//對F有約束
pub fn execute<F>(&self, f: F) where F: FnOnce() + Send + 'static {
//將任務(wù)包裝成Box
let job = Box::new(f);
// self.sender.send(job).unwrap();
self.sender.send(Message::NewJob(job)).unwrap();
}
}
//為ThreadPool實(shí)現(xiàn)Drop trait
impl Drop for ThreadPool {
//當(dāng)線程池被銷毀時,關(guān)閉所有工作線程
//實(shí)現(xiàn)Drop trait,只需要實(shí)現(xiàn)drop()方法即可
fn drop(&mut self) {
//發(fā)送結(jié)束消息給worker
for _ in &self.workers {
self.sender.send(Message::Terminate).unwrap();
}
//等待所有工作線程結(jié)束
for worker in &mut self.workers {
println!("Shutting down worker {}", worker.id);
//等待工作線程結(jié)束
if let Some(thread) = worker.thread.take() {
thread.join().unwrap();
}
}
}
}
//定義一個結(jié)構(gòu)體,表示工作線程
#[allow(dead_code)]
struct Worker {
id: usize, //工作線程的id
//線程句柄,將thread::JoinHandle<()>包裝成Option,用于在drop()方法中調(diào)用take()方法
//Option中有take()方法,可以將Some中的值取出來,同時將Some置為None
thread: Option<thread::JoinHandle<()>>,
}
//為Worker實(shí)現(xiàn)方法
impl Worker {
//接收端需要線程安全,所以需要Arc<Mutex<T>>
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker {
//創(chuàng)建工作線程
let thread = thread::spawn(move || {
//循環(huán)從通道中接收任務(wù),并執(zhí)行
loop {
//recv會阻塞線程,直到有數(shù)據(jù)可讀
// 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);
//收到結(jié)束消息,退出循環(huán)
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中定義的依賴庫名稱,就是項(xiàng)目的名稱
fn main() {
//創(chuàng)建監(jiān)聽器,監(jiān)聽7878端口
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
//創(chuàng)建線程池,線程池大小為4
let pool = ThreadPool::new(4);
println!("Server running on 127.0.0.1:7878");
//使用線程池處理請求
//listener.incoming()返回一個迭代器,用于接收客戶端的連接請求
//take(4)表示只接收4個連接請求,可以根據(jù)實(shí)際情況調(diào)整
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個字節(jié)
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);
//封裝一個函數(shù),響應(yīng)客戶端
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個請求后,服務(wù)器就關(guān)閉
到此這篇關(guān)于Rust搭建webserver的底層原理與應(yīng)用實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)Rust webserver底層原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust搭建webserver的底層原理與應(yīng)用實(shí)戰(zhàn)技巧
本文介紹Rust在HTTP編程中的應(yīng)用,涵蓋協(xié)議基礎(chǔ)、標(biāo)準(zhǔn)庫與第三方庫(如hyper、reqwest)的使用,以及多線程服務(wù)器和線程池的實(shí)現(xiàn)與關(guān)閉機(jī)制,展示Rust在性能、安全和并發(fā)方面的優(yōu)勢,感興趣的朋友跟隨小編一起看看吧2025-06-06
rust標(biāo)準(zhǔn)庫std::env環(huán)境相關(guān)的常量
在本章節(jié)中, 我們探討了Rust處理命令行參數(shù)的常見的兩種方式和處理環(huán)境變量的兩種常見方式, 拋開Rust的語法, 實(shí)際上在命令行參數(shù)的處理方式上, 與其它語言大同小異, 可能影響我們習(xí)慣的也就只剩下語法,本文介紹rust標(biāo)準(zhǔn)庫std::env的相關(guān)知識,感興趣的朋友一起看看吧2024-03-03
詳解Rust調(diào)用tree-sitter支持自定義語言解析
使用Rust語言結(jié)合tree-sitter庫解析自定義語言需要定義語法、生成C解析器,并在Rust項(xiàng)目中集成,具體步驟包括創(chuàng)建grammar.js定義語法,使用tree-sitter-cli工具生成C解析器,以及在Rust項(xiàng)目中編寫代碼調(diào)用解析器,這一過程涉及到對tree-sitter的深入理解和Rust語言的應(yīng)用技巧2024-09-09
Rust 中單線程 Web 服務(wù)器的實(shí)現(xiàn)
本文用Rust構(gòu)建單線程Web服務(wù)器,通過HTTP/TCP處理請求,返回hello.html或404.html,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-06-06
教你使用RustDesk?搭建一個自己的遠(yuǎn)程桌面中繼服務(wù)器
這篇文章主要介紹了RustDesk?搭建一個自己的遠(yuǎn)程桌面中繼服務(wù)器,主要包括服務(wù)端安裝和客戶端配置方法,配置好相關(guān)操作輸入控制碼即可發(fā)起遠(yuǎn)程或文件傳輸,本文通過圖文給大家講解的非常詳細(xì),需要的朋友可以參考下2022-08-08
如何使用VSCode配置Rust開發(fā)環(huán)境(Rust新手教程)
這篇文章主要介紹了如何使用VSCode配置Rust開發(fā)環(huán)境(Rust新手教程),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07

