Rust下载与配置
依照rust的官方下载指南,操作很简单。
下载rustup:curl https://sh.rustup.rs -sSf | sh
配置环境:export PATH=”$HOME/.cargo/bin:$PATH”
此时已经可以正常使用了,创建一个rust项目比较重要的指令如下,只要掌握这些指令我们就可创建编译和运行rust程序了。
新建一个rust项目:cargo new project_name
编译rust :cargo build
编译并运行rust :cargo run
Rust实现多线程服务器
由于已经实现过Python socket搭建多线程服务器,发现用rust进行多线程服务器和用Python创建多线程服务器没有太大的差别,大体上逻辑和结构都是一样的,区别在于实现细节和两种语言的特点。由于才接触rust而且只有这一天,所以对rust相关语法都是现查现用,原理细节和它的高级特性等等没有进行深究,也看不太懂,所以并没有按照官方文档进行操作。所以只是依样画葫芦用rust实现了一下python搭建的HTTP服务器,两者还是很相似的,有些实现细节有差别。
首先捋一捋rust搭建多线程服务器的逻辑
绑定端口——>监听请求——>循环接受请求多线程将请求发给handel_connect——>处理请求——>关闭socket连接
所以函数控制主体如图,跟Python用socket搭建没有什么区别。
然后主要是请求处理部分,都先接收请求的信息,然后对信息进行解析。Python我是用正则解析来设置路由区分请求信息的,而rust则是用starts_with()这个方法来解析的。我们将我们的每一个页面对应一个请求信息。
通过if-else if-else 结构根据不同请求头信息返回不同的界面。
我们总共设置了6个页面和一个文件下载功能。与Python搭建服务器相同,都是通过读取不同的文件对请求进行响应。这一次我们多设置了一个404页面,同时通过start_with()函数来进行控制,使程序更加的合理了。
文件下载页面和其他页面不同,需要设置header,所以用了一个bool变量来控制header的添加。这一段代码实现的是读取相应的文件完成响应并关闭连接。所以接下来我们对每一个页面进行验证。
1.登录页
我们设置的默认页面就是登陆页,直接copy了python搭建的HTML页面。页面如下,根据设置的路由,访问/和/login页面都会返回登录页。
2.登录校验
在python上我们可以通过正则从请求信息中提取出用户名和密码,而我们不知道rust是否有正则,所以就没用正则。而是取巧的使用设置路由的方式进行验证,我们表单提交请求为/index?user=&password=,所以我们直接将路由设置为:
let get_admin = b”GET /index?user=admin&password=123456 HTTP/1.1\r\n”;这样我们就返回我们的后台页面,如图。
3.失败页面
对于失败页面,我们怎么判断呢?因为start_with()函数使用起来不如正则灵活,所以如何确定返回登录失败页面你是个问题。我们可以将路由设置为:
let get_error = b”GET /index?user=”;
同时利用else if 的优先级顺序,将我们的成功页面放在失败校验之前,这样我们就实现了失败页面的返回。同时为了让我们的网址不那么奇怪,我们失败页会跳转到登录页,使用js控制。失败页js如图。
4.两个email页
这两个页面没有什么特别的配置。
5.404页面
404页面其实也没有什么特别的,只是他是放在else里的,所以很好的控制路由,让网页看起来更加合理。
6.文件下载页
要实现文件下载,主要修改响应包的header,添加如下字段浏览器就会将返回包当做文件进行下载了。
Content-Type:application/octet-stream\r\nContent-Disposition: attachment;filename = 贪玩蓝月.zip\r\n\r\n
下载之后查看文件内容,文件正常,信息与之前一致。
所以使用rust搭建多线程服务器功能基本完成,不过只是照猫画虎,对于rust语言依旧还是处于很粗浅的状态,对于rust多线程处理机制还是很陌生的。不过通过这次实验,让我了解了rust这门逐渐崛起的语言。
code
main.rs
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
use std::fs::File;
use std::thread;
fn main() {
//在8080端口进行监听
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
//接受连接并多线程交给handle_connection进行处理
for stream in listener.incoming() {
let stream = stream.unwrap();
thread::spawn(||{
handle_connection(stream);
});
}
}
//连接响应函数
fn handle_connection(mut stream: TcpStream) {
//读取请求头信息
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
//println!("{0:?}",&buffer[0..512]);
//设置静态路由
let get_index = b"GET / HTTP/1.1\r\n";
let get_login = b"GET /login HTTP/1.1\r\n";
let get_admin = b"GET /index?user=admin&password=123456 HTTP/1.1\r\n";
let get_error = b"GET /index?user=";
let get_email1 = b"GET /email1 HTTP/1.1\r\n";
let get_email2 = b"GET /email2 HTTP/1.1\r\n";
let get_paper = b"GET /paper HTTP/1.1\r\n";
//路由实现
let mut status_line="HTTP/1.1 200 OK\r\n\r\n";
let response_file;
let mut if_file=false;
if buffer.starts_with(get_index) || buffer.starts_with(get_login){
response_file="login.html";
}
else if buffer.starts_with(get_admin){
response_file="admin.html";
}
else if buffer.starts_with(get_error){
response_file="error.html";
}
else if buffer.starts_with(get_email1){
response_file="email1.html";
}
else if buffer.starts_with(get_email2){
response_file="email2.html";
}
else if buffer.starts_with(get_paper)
{
if_file=true;
status_line="HTTP/1.1 200 OK\r\n";
response_file="kid.txt"
}
else {
status_line="HTTP/1.1 404 NOT FOUND\r\n\r\n";
response_file="404.html";
};
//读取文件返回响应信息
let mut file = File::open(response_file).unwrap();
let mut contents = String::new();
let headers="Content-Type:application/octet-stream\r\nContent-Disposition: attachment;filename = 贪玩蓝月.zip\r\n\r\n";
file.read_to_string(&mut contents).unwrap();
let response = if if_file{
format!("{}{}{}", status_line,headers, contents)
} else
{
format!("{}{}", status_line, contents)
};
//流写入body
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}