RustBook——WebServer学习示例+注释

3 篇文章 0 订阅

Rust——WebServer示例的学习+注释

项目结构

- hello_web
	- src
		- main.rs
		- lib.rs
	- Cargo.lock
	- Cargo.toml
	- hello.html
	- 404.html 

Main入口——main.rs

extern crate hello_web;

use hello_web::ThreadPool;

use std::fs::File;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
use std::thread;
use std::time::Duration;

fn main() {
    // 绑定本地IP:Port监听
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();

    // 创建线程池
    let pool = ThreadPool::new(8);

    // 遍历监听的listener
    // take(10) 表示只接受10个请求
    for stream in listener.incoming().take(10) {
        let stream = stream.unwrap();

        // 使用线程池处理请求过来的stream
        pool.execute(|| {
            handle_connection(stream);
        });
    }

    println!("Shutting down.");
}

/// 处理tcp连接请求,进行不同的响应
fn handle_connection(mut stream: TcpStream) {
    // 将stream存入buffer
    let mut buffer = [0; 512];
    stream.read(&mut buffer).unwrap();

    let get = b"GET / HTTP/1.1\r\n";
    let sleep = b"GET /sleep HTTP/1.1\r\n";

    // 判断请求以什么开头,决定响应不同的内容
    let (status_line, filename) = if buffer.starts_with(get) {
        ("HTTP/1.1 200 OK\r\n\r\n", "hello.html")
    } else if buffer.starts_with(sleep) {
        // 线程睡5秒
        thread::sleep(Duration::from_secs(5));
        ("HTTP/1.1 200 OK\r\n\r\n", "hello.html")
    } else {
        ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html")
    };

    // 读取本地文件,并转换为String
    let mut contents = String::new();
    let mut file = File::open(filename).unwrap();
    file.read_to_string(&mut contents).unwrap();

    // 封装响应体
    let response = format!("{}{}", status_line, contents);

    // 返回数据
    stream.write(response.as_bytes()).unwrap();
    stream.flush().unwrap();
}

Lib库——lib.rs

use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;

/// sender发送的信息的类型的枚举
///
/// NewJob - 表示新的任务
///
/// Terminate - 表示结束
enum Message {
    NewJob(Job),
    Terminate,
}

/// 线程池的结构体
/// # Arguments
///
/// * workers - 是实际的任务运行者
/// * sender - 用于发送任务
pub struct ThreadPool {
    workers: Vec<Worker>,
    sender: mpsc::Sender<Message>,
}

trait FnBox {
    fn call_box(self: Box<Self>);
}

impl<F: FnOnce()> FnBox for F {
    fn call_box(self: Box<F>) {
        (*self)()
    }
}

type Job = Box<dyn FnBox + Send + 'static>;

impl ThreadPool {
    /// 创建线程池。
    ///
    /// 线程池中线程的数量。
    ///
    /// # Panics
    ///
    /// `new` 函数在 size 为 0 时会 panic。
    pub fn new(size: usize) -> ThreadPool {
        assert!(size > 0);

        // 一个sender 对应 一个 receiver
        let (sender, receiver) = mpsc::channel();

		// Mutex为receiver加锁
		// Arc使receiver能在多个线程中调用
        let receiver = Arc::new(Mutex::new(receiver));

        // 构建Worker的容器Vector,初始化容量为size
        let mut workers = Vec::with_capacity(size);

        for id in 0..size {
            workers.push(Worker::new(id, Arc::clone(&receiver)));
        }

        ThreadPool { workers, sender }
    }

    /// 执行任务
    ///
    /// 由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 {
    /// 当sender发送终止的消息时,停掉所有的worker
    fn drop(&mut self) {
        println!("Sending terminate message to all workers.");

        for _ in &mut self.workers {
            self.sender.send(Message::Terminate).unwrap();
        }

        println!("Shutting down all workers.");

        for worker in &mut self.workers {
            println!("Shutting down worker {}", worker.id);

            if let Some(thread) = worker.thread.take() {
                thread.join().unwrap();
            }
        }
    }
}

struct Worker {
    id: usize,
    thread: Option<thread::JoinHandle<()>>,
}

impl Worker {
    /// 构建Worker
    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; executing.", id);

                    job.call_box();
                }
                Message::Terminate => {
                    println!("Worker {} was told to terminate.", id);

                    break;
                }
            }
        });

        Worker {
            id,
            thread: Some(thread),
        }
    }
}

Html文件——hello.html和404.html

  • hello.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hello!</title>
  </head>
  <body>
    <h1>Hello!</h1>
    <p>Hi from Rust</p>
  </body>
</html>
  • 404.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hello!</title>
  </head>
  <body>
    <h1>Oops!</h1>
    <p>Sorry, I don't know what you're asking for.</p>
  </body>
</html>

Cargo配置——Cargo.toml和Cargo.lock

  • Cargo.toml
[package]
name = "hello_web"
version = "0.1.0"
authors = ["***"]
edition = "2018"

[dependencies]

  • Cargo.lock
[[package]]
name = "hello_web"
version = "0.1.0"


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值