基础部分是跟着《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<Self>);
}
impl<F: FnOnce()> FnBox for F{
fn call_box(self: Box<F>){
(*self)()
}
}
type Job = Box<dyn FnBox + Send +'static>;
pub struct ThreadPool{
workers: Vec<Worker>,
sender: mpsc::Sender<Job>,
}
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<F>(&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<Mutex<mpsc::Receiver<Job>>>)->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,
}
}
}