?? 歡迎大家來到景天科技苑??
🎈🎈 養成好習慣,先贊后看哦~🎈🎈
🏆 作者簡介:景天科技苑
🏆《頭銜》:大廠架構師,華為云開發者社區專家博主,阿里云開發者社區專家博主,CSDN全棧領域優質創作者,掘金優秀博主,51CTO博客專家等。
🏆《博客》:Rust開發,Python全棧,Golang開發,云原生開發,PyQt5和Tkinter桌面開發,小程序開發,人工智能,js逆向,App逆向,網絡系統安全,數據分析,Django,fastapi,flask等框架,云原生K8S,linux,shell腳本等實操經驗,網站搭建,數據庫等分享。所屬的專欄:Rust語言通關之路
景天的主頁:景天科技苑
文章目錄
- Rust http編程
- 1. HTTP基礎與Rust生態系統
- 1.1 HTTP協議回顧
- 1.2 Rust HTTP生態系統概覽
- 2. 使用標準庫進行HTTP編程
- 2.1 基本HTTP服務端
- 2.2 簡單HTTP客戶端
- 2.3 服務端響應網頁
- 2.4 有條件地響應網頁
- 2.5 多線程的http服務器
- 2.6 線程池webserver
- 2.7 實現線程池清除的webserver
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和Writefn 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和Writefn 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和Writefn 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);//判斷請求方法是否為GETif 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和Writefn 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();}//判斷請求方法是否為GETif 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();}//判斷請求方法是否為GETif 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();//創建線程池,線程池大小為4let 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();}//判斷請求方法是否為GETif 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 {//線程池的大小必須大于0assert!(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 {//將任務包裝成Boxlet job = Box::new(f);self.sender.send(job).unwrap();}
}//定義一個結構體,表示工作線程
#[allow(dead_code)]
struct Worker {id: usize, //工作線程的idthread: 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置為Nonethread: 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 {//線程池的大小必須大于0assert!(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 {//將任務包裝成Boxlet 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) {//發送結束消息給workerfor _ 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置為Nonethread: 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();//創建線程池,線程池大小為4let 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();}//判斷請求方法是否為GETif 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個請求后,服務器就關閉