Rust 的简单的webserever服务。
编写参考于令狐一冲的视频。
一、单线程服务
use std::net::TcpListener;
use std::net::TcpStream;
use std::io::{Read, Write};
use std::fs;
fn handle_client(mut stream:TcpStream){
let mut buffer = [1;512];
stream.read(&mut buffer).unwrap();
println!("收到了请求!{:#?}",String::from_utf8_lossy(&buffer));
// ===直接返回响应==
// let response = "HTTP/1.1 200 OK\r\n\r\n";
// stream.write(response.as_bytes()).unwrap();
// stream.flush().unwrap();
// ===返回网页===
// let content = fs::read_to_string("./main.html").unwrap();
// let response = format!("HTTP/1.1 200 OK\r\n\r\n {}",content);
// stream.write(response.as_bytes()).unwrap();
// stream.flush().unwrap();
// ===有条件的返回网页===
// let get = b"GET / HTTP/1.1\r\n";
// if buffer.starts_with(get){
// let content = fs::read_to_string("./main.html").unwrap();
// let response = format!("HTTP/1.1 200 OK\r\n\r\n {}",content);
// stream.write(response.as_bytes()).unwrap();
// stream.flush().unwrap();
// }else {
// let content = fs::read_to_string("./404.html").unwrap();
// let response = format!("HTTP/1.1 404 Not Found\r\n\r\n {}",content);
// stream.write(response.as_bytes()).unwrap();
// stream.flush().unwrap();
// }
// ===重构有条件的返回网页===
let get = b"GET / HTTP/1.1\r\n";
let (status_line ,filename) = if buffer.starts_with(get){
("HTTP/1.1 200 OK\r\n\r\n","main.html")
}else {
("HTTP/1.1 404 Not Found\r\n\r\n","404.html")
};
let contents = fs::read_to_string(filename).unwrap();
let response = format!("{}{}",status_line,contents);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
fn main() -> std::io::Result<()>{
let listener = TcpListener::bind("127.0.0.1:8080")?;
for stream in listener.incoming(){
handle_client(stream?);
}
println!("Hello, world!");
Ok(())
}
二、使用线程池
main.rs
use std::net::TcpListener;
use std::net::TcpStream;
use std::io::{Read, Write};
use std::fs;
use std::thread;
use std::time;
use webserver_multi::ThreadPool;
fn handle_client(mut stream:TcpStream){
let mut buffer = [1;512];
stream.read(&mut buffer).unwrap();
let get = b"GET / HTTP/1.1\r\n";
let (status_line ,filename) = if buffer.starts_with(get){
("HTTP/1.1 200 OK\r\n\r\n","main.html")
}else {
("HTTP/1.1 404 Not Found\r\n\r\n","404.html")
};
let contents = fs::read_to_string(filename).unwrap();
let response = format!("{}{}",status_line,contents);
let t0 = time::Duration::from_micros(500);
thread::sleep(t0); //休息一下,模拟工作
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
fn main() -> std::io::Result<()>{
let listener = TcpListener::bind("127.0.0.1:8080")?;
let pool = ThreadPool::new(4);
for stream in listener.incoming().take(4){
let stream_ = stream.unwrap();
pool.execute(||{handle_client(stream_)});
}
Ok(())
}
lib.rs
use std::thread;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use core::option::Option::Some;
struct Worker{
id:usize,
thread:Option<thread::JoinHandle<()>>,
}
impl Worker{
pub fn new(id:usize, receiver:Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker{
let thread = thread::spawn(move||{
loop{
let message = receiver.lock().unwrap().recv().unwrap();
match message{
Message::NewJob(job) =>{
println!("Worker {} got a job !",id);
job();
}
Message::Teiminate => {
println!("Worker {} break !",id);
break;
}
}
}
});
Worker {
id:id,
thread:Some(thread)
}
}
}
type Job = Box<dyn FnOnce() + Send +'static>;
enum Message{
NewJob(Job),
Teiminate,
}
pub struct ThreadPool{
workers:Vec<Worker>,
sender:mpsc::Sender<Message>
}
impl ThreadPool{
pub fn new(size:usize) ->Self{
assert!(size>0);
let mut workers = Vec::with_capacity(size);
let (sender,receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
for id in 0..size{
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool{
workers,
sender,
}
}
pub fn execute<F>(&self, f: F)
where F: FnOnce() + Send + 'static {
let job = Box::new(f);
self.sender.send(Message::NewJob(job)).unwrap();
}
}
impl Drop for ThreadPool{
fn drop(&mut self){
// 发送信息给终端,等待停止服务。
for _ in &mut self.workers{
self.sender.send(Message::Teiminate).unwrap();
}
for worker in &mut self.workers{
if let Some(thread) = worker.thread.take(){
thread.join().unwrap();
}
}
}
}