Rust实现的多线程Web服务器

基础部分是跟着《Rust权威指南》一步一步做的
在此基础上又增加了路径判断功能和二进制文件传输功能,总的来就是解决了原书代码无法使用超链接,无法加载图片、音乐等问题
简单的网页已经足以胜任,并发能力尚可(主要我这边测试能力有限)
另外还解决了write()的一个panic,这个问题会消耗线程,最终让程序挂掉,解决之后稳定性还不错

默认配置:线程池大小4,监听80端口,单次二进制传输65535Byte,index文件夹/var/www
如有需求可在main.rs中自行修改

本人纯Rust初学者,代码必然有大量不足,欢迎指点

实现效果 (云服务器5Mbps的带宽实在太低了)
源码Github地址 欢迎star

bin/main.rs
extern crate server;
use server::ThreadPool;

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

fn main() {
    let listener = TcpListener::bind("0.0.0.0:80").unwrap();
    let pool = ThreadPool::new(4);
    for stream in listener.incoming(){
        let stream = stream.unwrap();
        pool.execute(||{
            handle_connection(stream);
        });
    }
}

fn second_word(s: &String)->&str{
    let byte = s.as_bytes();
    let mut first:usize = 0;
    let second:usize;
    for (i,&elem) in byte.iter().enumerate(){
        if elem == b' '{
            if first == 0 {
                first = i;
            }else{
                second = i;
                return &s[(first + 1)..second];
            }
        }
    }
    &s[..]
}

fn get_ext(s: &str)->&str{
    let byte = s.as_bytes();
    for (i,&elem) in byte.iter().enumerate(){
        if elem == b'.'{
            return &s[(i + 1)..];
        }
    }
    &s[..]
}

fn handle_connection(mut stream: TcpStream){

    let root = "/var/www";
    let mut buffer = [0;512];
    stream.read(&mut buffer).unwrap();
    let buffer_to_s = String::from_utf8_lossy(&buffer[..]).to_string();
    let file_name = second_word(&buffer_to_s);

    let now = time::now();
    let f_now = time::strftime("%Y-%m-%dT%H:%M:%S", &now).unwrap();
    println!("{:?} GET {}",f_now, file_name);

    if file_name == "/"{
        let mut file = match File::open(format!("{}/index.html",root)){
            Ok(_f) => _f,
            Err(_) => {
                stream.write("HTTP/1.1 404 NOT FOUND\r\n\r\n".as_bytes()).unwrap();
                stream.flush().unwrap();
                return;
            }
        };

        let mut contents = String::new();
        file.read_to_string(&mut contents).unwrap();

        let response = format!("HTTP/1.1 200 OK\r\n\r\n{}",contents);
        stream.write(response.as_bytes()).unwrap();
        stream.flush().unwrap();
    }else{
        let ext = get_ext(&file_name);
        let mut file = match File::open(format!("{}{}",root,file_name)){
            Ok(_f) => _f,
            Err(_) => {
                stream.write("HTTP/1.1 404 NOT FOUND\r\n\r\n".as_bytes()).unwrap();
                stream.flush().unwrap();
                return;
            }
        };
        if ext == "html"{
            let mut contents = String::new();
            file.read_to_string(&mut contents).unwrap();

            let response = format!("HTTP/1.1 200 OK\r\n\r\n{}",contents);
            stream.write(response.as_bytes()).unwrap();
            stream.flush().unwrap();
        }else{
            let mut buffer = [0;65535];
            while let std::io::Result::Ok(len) = file.read(&mut buffer){
    			if len == 0 {
    				break;
    			}
    			else{
                    match stream.write(&buffer){
                        Ok(_) => {

                        },
                        Err(_) => {
                            break;
                        }
                    };
    			}
            }
            stream.flush().unwrap();
        }
    }
}
lib.rs
use std::thread;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;

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

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

type Job = Box;

pub struct ThreadPool{
    workers: Vec,
    sender: mpsc::Sender,
}

impl ThreadPool{
    pub fn new(size: usize)->ThreadPool{
        assert!(size > 0);

        let (sender, receiver) = mpsc::channel();

        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,
        }
    }
    pub fn execute(&self, f:F)
        where
            F: FnOnce() + Send + 'static
    {
        let job = Box::new(f);
        self.sender.send(job).unwrap();
    }
}

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

impl Worker{
    fn new(id:usize, receiver: Arc>>)->Worker{
        let thread = thread::spawn(move ||{
            loop{
                let job = match receiver.lock(){
                    Ok(res1) => match res1.recv(){
                        Ok(res2) => res2,
                        Err(_) =>{
                            println!("ErrRecv");
                            continue;
                        }
                    },
                    Err(_) => {
                        println!("ErrLock");
                        continue;
                    }
                };
                //println!("Worker {} got a job; excuting.",id);
                job.call_box();
            }
        });
        Worker{
            id,
            thread,
        }
    }
}

你可能感兴趣的:(Rust,多线程,rust,web,development,后端)