使用rust写一个Web服务器——单线程版本

文章目录

    • 监听TCP连接
    • 读取HTTP Reqeust
    • 返回HTTP Response
    • 返回HTML页面
    • 验证Request和选择性Response

使用rust编写一个基于HTTP协议的Web服务器。HTTP是更高层的通信协议,一般来说都基于TCP来构建的,除了HTTP/3,后者是基于UDP构建的协议

仓库地址: 1037827920/web-server: 使用rust编写的简单web服务器 (github.com)

下面分为五个步骤去完成这个单线程Web服务器:

  1. 监听TCP连接
  2. 读取HTTP Reqeust
  3. 返回HTTP Response
  4. 返回HTML页面
  5. 验证Request和选择性Response

监听TCP连接

use std::net::TcpListener;fn main() {// 监听端口let listener = TcpListener::bind("localhost:8080").unwrap();// incoming返回一个迭代器,它每一次迭代会返回一个新的连接stream(客户端发起的连接,Web服务器负责监听接收),因此,接下来做的就是从stream中读取数据,然后返回处理的结果for stream in listener.incoming() {let stream = stream.unwrap();println!("Connection established!");}
}

运行代码后访问localhost:8080,可以看到如下结果:

Connection established!
Connection established!
Connection established!

为啥浏览器访问依次,会在终端打印多次连接建立的信息?

原因在于stream超出作用域时,会触发drop的扫尾工作,其中包含了关闭连接。但是,浏览器可能会存在自动重试的情况,因此还会重新建立连接,最终打印了多次。

注意: 由于listener.incoming()会在当前阻塞式监听,所以main线程会被阻塞。

读取HTTP Reqeust

连接建立后,就可以开始读取客户端传来请求数据,先了解一下HTTP Reqeust

HTTP Request格式:

Method Request-URI HTTP-Version
headers CRLF
message-body
  • Method是请求的方法,例如GET、POST等,Reqeust-URI是该请求希望访问的目标资源路径,例如/、/sleep
  • 类似JSON格式的数据都是HTTP请求报头headers,例如“Host: localhost:8080”
  • message-body是消息体,它包含了用户请求携带的具体数据,例如更改用户名的请求,就要提交新的用户名数据,而GET请求是没有message-body的

代码:

use std::{// 帮助我们读取和写入数据// BufReader可以实现缓冲区读取,底层其实是基于std::io::Read实现,可以使用lines方法获取一个迭代器,可以对传输的内容流进行按行迭代读取,要使用该方法,需引入std::io::BufReadio::{prelude::*, BufReader},net::{TcpListener, TcpStream},
};fn main() {let listener = TcpListener::bind("192.168.218.128:8080").unwrap();for stream in listener.incoming() {let stream = stream.unwrap();handle_connection(stream);}
}/// # 函数作用
/// 处理连接:读取请求
fn handle_connection(mut stream: TcpStream) {let buf_reader = BufReader::new(&mut stream);let http_request: Vec<_> = buf_reader.lines().map(|result| result.unwrap()).take_while(|line| !line.is_empty()) // 从迭代器中获取元素,直到闭包返回false为止.collect(); // 使用collect消费掉迭代器println!("Request: {:#?}", http_request);
}

运行代码后访问localhost:8080,可以看到如下结果:

Request: ["GET / HTTP/1.1","Host: 192.168.218.128:8080","Connection: keep-alive","Cache-Control: max-age=0","Upgrade-Insecure-Requests: 1","User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0",...
]

如何判断客户端发来的HTTP数据是否读取完成:

客户端会在请求数据的结尾附上两个换行符,放我们检测某一行字符串为空时,就意味着请求数据已经传输完毕了,可以collect了。

返回HTTP Response

客户端请求后,服务端需要给予相应的请求应答

HTTP Response格式:

HTTP-Version Status-Code Reason-Phrase CRLF
headers CRLF
message-body

Status-Code用于告诉客户端,当前的请求是否成功,若失败,大概是什么原因

Response示例:

HTTP/1.1 200 OK\r\n\r\n

修改handle_conneciton,将Response发送回客户端:

/// # 函数作用
/// 处理连接:读取请求,回应请求
fn handle_connection(mut stream: TcpStream) {let buf_reader = BufReader::new(&mut stream);let http_request: Vec<_> = buf_reader.lines().map(|result| result.unwrap()).take_while(|line| !line.is_empty()) // 从迭代器中获取元素,直到闭包返回false为止.collect(); // 使用collect消费掉迭代器let response = "HTTP/1.1 200 OK\r\n\r\n";// write_all接收&[u8]类型作为参数,这里需要用as_bytes将字符串转换为字节数组stream.write_all(response.as_bytes()).unwrap();
}

运行代码后访问localhost:8080,浏览器已经不会再报错,已经收到了来自服务器的Response,虽然是空白页面

返回HTML页面

hello.html:

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>This is title</title></head><body><h1>Hello!</h1><p>Hi from Web Server</p></body>
</html>

添加导包:

use std::fs;

修改handle_connection函数:

/// # 函数作用
/// 处理连接:读取请求,回应请求
fn handle_connection(mut stream: TcpStream) {let buf_reader = BufReader::new(&mut stream);let _http_request: Vec<_> = buf_reader.lines().map(|result| result.unwrap()).take_while(|line| !line.is_empty()) // 从迭代器中获取元素,直到闭包返回false为止.collect(); // 使用collect消费掉迭代器let status_line = "HTTP/1.1 200 OK"; // 状态行let contents = fs::read_to_string("hello.html").unwrap(); // 读取文件内容let length = contents.len();let response = format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}");// write_all接收&[u8]类型作为参数,这里需要用as_bytes将字符串转换为字节数组stream.write_all(response.as_bytes()).unwrap();
}

运行代码后访问localhost:8080,浏览器会显示hello.html页面

验证Request和选择性Response

404.html内容:

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>This is 404 Page</title></head><body><h1>Sorry!</h1><p>404</p></body>
</html>

继续修改handle_connection,针对客户端不同的Request给出相应的Response

fn handle_connection(mut stream: TcpStream) {let buf_reader = BufReader::new(&mut stream);// 使用next而不是lines,因为我们只需要读取第一行,判断具体的request方法let request_line = buf_reader.lines().next().unwrap().unwrap();let (status_line, filename) = if request_line == "GET / HTTP/1.1" {("HTTP/1.1 200 OK", "hello.html")} else {("HTTP/1.1 404 NOT FOUND", "404.html")};let contents = fs::read_to_string(filename).unwrap();let length = contents.len();let response =format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}");stream.write_all(response.as_bytes()).unwrap();
}

运行代码后访问localhost:8080,浏览器会显示hello.html页面,范围localhost:8080/sleep,会显示404.html页面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/880849.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第一弹:C++ 的基本知识概述

文章目录 知识点 1&#xff1a;C 的概述1. C的特征2. C 程序的编辑、编译和执行3. 第一个 C 源程序4. 面向对象程序设计思想4.1 面向对象程序设计思想初始4.2 面向对象程序设计思想的核心 知识点 2&#xff1a;C 对 C 的扩展1. 作用域访问运算符 ::1.1 全局变量和局部变量1.2 作…

若无向图G(V,E)中含7个顶点,为保证图G在任何情况下都是连通的,则需要的边数最少是多少?

这乍一看是不是可抽象&#xff08;迷糊&#xff09;了&#xff0c;butttt待我小翻译一下。 先举少一点的例子&#xff0c;假如我们有三个点&#xff0c;我给你两条边&#xff0c;那是不是不管咋连都一定一定是连通的。 那我们再进一步&#xff0c;假如四个点呢&#xff1f;我给…

RabbitMQ 界面管理说明

1.RabbitMQ界面访问端口和后端代码连接端口不一样 界面端口是15672 http://localhost:15672/ 后端端口是 5672 默认账户密码登录 guest 2.总览图 3.RabbitMq数据存储位置 4.队列 4.客户端消费者连接状态 5.队列运行状态 6.整体运行状态

在Linux中将设备驱动的地址映射到用户空间

本期主题&#xff1a; MMU的简单介绍&#xff0c;以及如何实现设备地址映射到用户空间 往期链接&#xff1a; Linux内核链表零长度数组的使用inline的作用嵌入式C基础——ARRAY_SIZE使用以及踩坑分析Linux下如何操作寄存器&#xff08;用户空间、内核空间方法讲解&#xff09;…

Redis篇(最佳实践)(持续更新迭代)

介绍一&#xff1a;键值设计 一、优雅的key结构 Redis 的 Key 虽然可以自定义&#xff0c;但最好遵循下面的几个最佳实践约定&#xff1a; 遵循基本格式&#xff1a;[业务名称]:[数据名]:[id]长度不超过 44 字节不包含特殊字符 例如&#xff1a; 我们的登录业务&#xff0…

『功能项目』宠物的攻击巨型化【80】

本章项目成果展示 我们打开上一篇79宠物的召唤跟随的项目&#xff0c; 本章要做的事情是实现在战斗中有几率触发宠物巨型化攻击将怪物击飞的效果 首先在主角预制体中增加隐藏的宠物巨型化 制作巨型化宠物的攻击效果 将该动画控制器放置在隐藏的巨型化宠物的动画控制器上 首先查…

c# object和dynamic的区别

在 C# 编程中&#xff0c;object 和 dynamic 是两个非常有用的关键字&#xff0c;但它们的使用场景和性能影响各不相同。本文将详细探讨这两者的用法、区别以及如何优化它们的使用。 1. object 关键字 1.1 什么是 object object 是 C# 中所有类型的基类。每个类型&#xff0…

mysql学习教程,从入门到精通,SQL CROSS JOIN 语句(26)

1、SQL CROSS JOIN 语句 CROSS JOIN在 SQL 中用于将两个或多个表的每一行进行组合。这意味着如果表 A 有 M 行&#xff0c;表 B 有 N 行&#xff0c;那么CROSS JOIN 的结果将包含 M * N 行。这种连接不依赖于任何连接条件&#xff0c;因此它会生成笛卡尔积。 下面是一个简单的…

Linux下的基本指令/命令(一)

目录 基本命令 1. Is命令/指令: 罗列当前目录下指定的文件或者目录. 2. pwd命令&#xff1a; 查看当前工作的路径 3. cd命令&#xff1a; 切换到指定路径下。 只能切换到目录中 4. tree命令: 树状显式目录 使用前要输入命令 yum install -y tree &#xff0c;用来安装一个…

基于Hive和Hadoop的哔哩哔哩网站分析系统

本项目是一个基于大数据技术的哔哩哔哩平台分析系统&#xff0c;旨在为用户提供全面的哔哩哔哩视频数据和深入的用户行为分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xf…

数据流处理技术与Flink框架

一数据流 数据流定义&#xff1a; 数据流&#xff08;Data Stream&#xff09;是指数据以连续不断的方式到达和处理的序列。在现实世界中&#xff0c;许多数据来源都是以流的形式存在&#xff0c;比如&#xff1a; 1. 用户行为&#xff1a;用户在网站上的点击流、移动应用中…

【Linux】几种常见配置文件介绍

配置文件目录 linux 系统中有很多配置文件目录/etc/systemd/system、/lib/systemd/system 以及/usr/lib/systemd/system 等&#xff0c;这三者有什么样的关系呢&#xff1f; 以下是网络上找的资料汇总&#xff0c;并加了一些操作验证。方便后期使用 介绍 目录/lib/systemd/s…

使用C#,MSSQL开发的钢结构加工系统

很久以前的项目&#xff0c;上位机使用C#开发。数据库使用mssql。控制系统选用了三菱PLC&#xff0c;上位机和PLC之间走ModbusTCP通讯协议。 主要功能&#xff1a;读取加工文件&#xff08;csv格式&#xff09;&#xff0c;导入到数据库&#xff0c;并根据机床刀具规则&#x…

Python编码系列—Python命令模式:将请求封装为对象

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

个人文章合集 - 前端相关

前端&#xff1a;简述表单提交前如何进行数据验证 前端&#xff1a;项目一个html中如何引入另一个html&#xff1f; 前端&#xff1a;一张图快速记忆CSS所有属性 前端&#xff1a;三个CSS预处理器(框架)-Sass、LESS 和 Stylus的比较 前端&#xff1a;基于Java角度理解nodejs/np…

Vant WeApp 开启 NPM 遇到的问题总结

新建小程序工程&#xff0c;默认未启用 NPM 管理组件 使用 Vant WeApp UI 组件库&#xff0c;需使用 NPM 安装组件&#xff0c;报错&#xff0c;因为初始工程未启用 NPM 工具 -> 构建 NPM 在控制台工程路径下初始化 NPM 构建工具环境 npm init -y 使用 NPM 安装 Vant Weap…

nvm安装 出现 Error retrieving “http://xxxx/SHASUMS256.txt“: HTTP Status 404 解决方法

目录 前言1. 问题所示2. 原理分析3. 解决方法前言 nvm的基本知识,推荐阅读:nvm安装教程,实现node的多版本管理(图文界面) 1. 问题所示 nvm安装node版本的时候出现如下问题: F:\vue_project\block_canvas>nvm install 18.19.1 Error retrieving "http://npm.t…

【软件测试】详解软件测试中的测试级别

目录 一、测试级别二、组件测试三、开发者测试3.1测试与调试3.2 组件测试目标3.3 测试功能 四、稳健性测试4.1 效率的测试4.2 测试可维护性4.3 测试策略4.4 白盒测试 一、测试级别 软件系统通常是由许多子系统组成的&#xff0c;而这些子系统又是由多个组件组成的&#xff0c;…

VMware Aria Operations for Logs 8.18 发布,新增功能概览

VMware Aria Operations for Logs 8.18 - 集中式日志管理 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-aria-operations-for-logs/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 集中式日志管理 VMware Aria …

自闭症儿童寄宿学校:打造良好的学习和生活环境

在探讨自闭症儿童的教育与康复之路时&#xff0c;星贝育园无疑是一个值得深入了解的典范。这所全国知名的广泛性发育障碍全托寄宿制儿童康复训练机构&#xff0c;不仅以其独特的CBM干预法引领着行业前沿&#xff0c;更以其对每一个孩子的深切关怀与承诺&#xff0c;构建了一个充…