目标
使用rust构建一个单线程处理http请求的web服务器。
- 学习有关TCP和HTTP两个协议的知识。
- 侦听套接字上的TCP连接。
- 掌握http协议
动手
cargo 构建项目
我们不希望只构建一个小玩具的rs代码,而是采用生产方式来构建我们的任何rs项目,这需要我们用到之前讲述过的cargo工具。执行一下指令构建本次实践的项目
cargo new webBean
回顾之前文章讲过的内容:
1、Cargo.toml是项目描述信息
2、src是源代码放置的目录
使用net网路库监听tcp链接
- 补充知识:
Web服务器中涉及的两个主要协议是超文本传输协议 (HTTP)和传输控制协议 (TCP)。两种协议都是请求-响应协议,这意味着客户端发起请求,服务器监听请求并向客户端提供响应。
- 概述:
- tcp是传输层协议,用来实现端到端的数据传输。
- http是应用层协议,当服务器端接收到请求端的数据时,用户应用层通过解析数据识别是什么应用请求(http,icmp,ftp)等再进行业务处理,应用层协议实际上也是一种逻辑业务:比如nginx解析http进行转发,或者通常使用的http 框架(比如go的beego、java的spring mvc等)都进行了封装,用户只需要处理“真正的业务”请求。
创建服务器,监听tcp链接:
use std::net::TcpListener;
fn main() {
let listener = TcpListener::bind("localhost:9999").unwrap();
for stream in listener.incoming(){
let stream = stream.unwrap();
println!("connection established!");
}
}
核心知识点:
- 使用use 引入rust提供的net包,使用TcpListener的bind函数来请求分配一个监听会话,该bind函数返回一个Result<T, E>,指示绑定可能失败。正常来说,返回失败时,我们服务器应该是无效的,需要退出。那么常见的失败原因有端口被占用、或者非管理员端口申请(比如80等)。
- unwrap是rust语言中,主要用于Option或Result的打开其包装的结果,在生产中通常处理Result而不是直接使用unwrap,在这里只是演示,不做深入解析。
- 使用let 将 TcpListener监听的返回值绑定到"变量" listener,这个绑定是不可变的,所以实际上又是一个常量。注意第7行的 let stream,这里的stream虽然与循坏的stream重名了,但实际上是通过let绑定了一个新值,是一个新的"变量"。
- 服务器建立监听后,可以通过Incoming函数来接收客户端链接的请求流,for循环将依次处理每个连接并产生一系列流供我们处理。
- 我们对每个流进行unwrap处理,如果有错误即终止程序,生产环境中我们还是通过对Result进行错误处理,而不是直接终止程序。
读取请求
- 我们编写函数handle_connection来处理服务器接收到请求流:
重写后
use std::net::TcpStream;
use std::net::TcpListener;
use std::io::prelude::*;
fn main() {
let listener = TcpListener::bind("127.0.0.1:9999").unwrap();
for stream in listener.incoming(){
let stream = stream.unwrap();
// println!("connection established!");
handle_connection(stream);
}
}
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0;