一、搭建http服务器
在8080端口创建tcp监听,引入handle_client请求处理模块,ThreadPool模块创建多线程。
use handle::handle_client;
use std::net::TcpListener;
use thread::ThreadPool;
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080")?;
let pool = ThreadPool::new(4);
for stream in listener.incoming() {
let stream = stream.unwrap();
pool.execute(|| handle_client(stream));
}
Ok(())
}
二、创建多线程
在ThreadPool用于创建Worker,发送要执行job的函数給Worker。Worker创建后用于监听ThreadPool发送过来job函数并执行。
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
pub struct Worker {
pub id: usize,
pub thread: thread::JoinHandle<()>,
}
impl Worker {
fn new(id: usize, receiver: Arc>>) -> Worker {
// fn new(ide: usize, receiver: mpsc::Receiver) -> Worker {
let thread = thread::spawn(move || {
loop {
let job = receiver.lock().unwrap().recv().unwrap();
// println!("Worker {} got a job!", id);
job();
}
});
Worker { id, thread }
}
}
pub struct ThreadPool {
// workers: Vec,
sender: mpsc::Sender,
}
type Job = Box;
impl ThreadPool {
pub fn new(size: usize) -> ThreadPool {
let num = if size > 0 { size } else { 1 };
let mut workers = Vec::with_capacity(num);
let (sender, receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver)); //线程安全
for id in 0..size {
// workers.push(Worker::new(id));
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool {
// threads
// workers,
sender,
}
}
pub fn execute(&self, f: F)
where
F: FnOnce() + Send + 'static,
{
let job = Box::new(f);
self.sender.send(job).unwrap();
}
}
三、请求处理模块
在请求处理模块中引入MySQL模块,serde模块用于将数据库查询结果转换成json返回给前端。在tools中封装params_parse、send_file、send、json,分别用于获取请求类型、返回文件、返回字符串、返回json格式字符串。
use std::io::{BufReader, Read};
use std::net::TcpStream;
// use mysql::prelude::*;
mod tools;
use database::{mysql_connect, User};
use mysql::prelude::Queryable;
use serde::{Deserialize, Serialize};
use serde_json;
#[derive(Serialize, Deserialize, Debug)]
struct Json
where
T: Serialize,
{
arr: Vec,
}
pub fn handle_client(mut stream: TcpStream) {
let (method, path, _protocol) = tools::params_parse(&mut stream);
if method == "GET" {
// 处理 GET 请求
if path == "/" {
tools::send_file(&mut stream, String::from("view/index.html"));
} else if path == "/home" {
tools::send_file(&mut stream, String::from("view/home.html"));
} else if path == "/user" {
let mut conn = if let Ok(conn) = mysql_connect() {
conn
} else {
tools::send(&mut stream, String::from("mysql connect error!"));
return;
};
// 执行查询
let res: Result, _> = conn.query_map(
"SELECT * from users",
|(id, nick_name, avatar_url, job, introduce)| User {
id,
nick_name,
avatar_url,
job,
introduce,
},
);
let json = match res {
Ok(value) => serde_json::to_string(&value).unwrap(),
Err(_) => {
let value = String::from("{arr:[]}");
serde_json::to_string(&value).unwrap()
}
};
tools::json(&mut stream, json);
return;
} else {
tools::send(&mut stream, String::from("hello world!"));
return;
}
} else if method == "POST" {
// 处理 POST 请求
let mut reader = BufReader::new(&mut stream);
let mut buffer = vec![0; 1024];
if let Err(_) = reader.read_to_end(&mut buffer) {
tools::send(&mut stream, String::from("params parse error!"));
return;
};
let request_body = String::from_utf8_lossy(&buffer).to_string();
tools::send(&mut stream, request_body);
return;
} else {
// 处理其他请求
tools::send(&mut stream, String::from("Not Implemented"));
return;
}
}
四、tools.rs
在写返回数据时,如果返回的内容在页面中没有正确显示,并且服务器已响应,那可能是没有正确的实现http协议,可能是Content-Type、Content-Length、charset等参数设置错误所导致的。
use std::fs;
use std::io::{BufRead, BufReader};
use std::io::{Read, Write};
use std::net::TcpStream;
// 解析请求类型,请求路径
pub fn params_parse(mut stream: &mut TcpStream) -> (String, String, String) {
let mut reader = BufReader::new(&mut stream);
let mut request_line = String::new();
if let Err(_) = reader.read_line(&mut request_line) {
send(&mut stream, String::from("params parse error!"));
return (
String::from(""),
String::from(""),
String::from(""),
);
};
let tokens: Vec<&str> = request_line.split_whitespace().collect();
let method = String::from(tokens[0]);
let path = String::from(tokens[1]);
let protocol = String::from(tokens[2]);
println!(
"Method: {}, Path: {}, Protocol: {}, token: {:?}",
method, path, protocol, tokens
);
(method, path, protocol)
}
pub fn send_file(stream: &mut TcpStream, file_path: String) {
// 如果打开文件失败
let mut file = match fs::File::open(file_path) {
Ok(file) => file,
Err(_) => {
let response = "HTTP/1.1 404 Not Found\r\n\r\n";
stream.write(response.as_bytes()).unwrap();
return;
}
};
// thread::sleep(time::Duration::from_millis(1000));
let mut contents = String::new();
// 如果读取内容失败
if let Err(_) = file.read_to_string(&mut contents) {
let response = "HTTP/1.1 500 Internal Server Error\r\n\r\n";
stream.write(response.as_bytes()).unwrap();
return;
}
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: {}\r\n\r\n{}",
contents.len(),
contents
);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
pub fn send(stream: &mut TcpStream, content: String) {
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: {}\r\n\r\n{}",
content.len(),
content
);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
pub fn json(stream: &mut TcpStream, json: String) {
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: {}\r\n\r\n{}",
json.len(),
json
);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
五、数据库连接
use mysql::*;
use serde::Serialize;
#[derive(Serialize, Debug, PartialEq, Eq)]
pub struct User {
pub id: i32,
pub nick_name: String,
pub avatar_url: String,
pub job: String,
pub introduce: String,
}
pub fn mysql_connect() -> std::result::Result> {
let url = "mysql://root:root@localhost:3306/test";
let pool = Pool::new(url)?;
let conn = pool.get_conn()?;
return Ok(conn);
}
使用到的第三方库mysql、serde、serde_json。