Rust Web小项目

Rust 第26节 Web小项目

监听TCP链接

use std::net::TcpListener;fn main() {let listener = TcpListener::bind("127.0.0.1:7887").unwrap(); //监听7887端口,成功后,就创建一个linstenerfor stream in listener.incoming() {   // listener.incoming 会返回产生一流序列的迭代器;每一个链接,都会产生一个流序列println!("connectiond!!");}
}

获取请求数据

use std::{io::Read, net::{TcpListener, TcpStream}};fn main() {let listener = TcpListener::bind("127.0.0.1:7887").unwrap(); //监听7887端口,成功后,就创建一个linstenerfor stream in listener.incoming() {   // listener.incoming 会返回产生一流序列的迭代器;每一个链接,都会产生一个流序列let stream = stream.unwrap();handle_connection(stream);}
}fn handle_connection(mut stream : TcpStream) {let mut buffer = [0; 512]; //定义一个数组存放数据stream.read(&mut buffer).unwrap(); //使用read方法读取数据println!("Request : {}",String::from_utf8_lossy(&buffer[..])) //将数组数据转换为字符串}

响应Http请求

fn handle_connection(mut stream : TcpStream) {let mut buffer = [0; 512]; //定义一个数组存放数据stream.read(&mut buffer).unwrap(); //使用read方法读取数据//println!("Request : {}",String::from_utf8_lossy(&buffer[..])) //将数组数据转换为字符串//响应let response = "HTTP/1.1 200 0k\r\n\r\n"; //协议,状态码 数据stream.write(response.as_bytes()).unwrap(); // write()接收的是数组,所以需要转换下stream.flush().unwrap(); //阻塞程序运行,直到数据都写入完成}

返回一个http数据

fn handle_connection(mut stream : TcpStream) {let mut buffer = [0; 2048]; //定义一个数组存放数据stream.read(&mut buffer).unwrap(); //使用read方法读取数据//println!("Request : {}",String::from_utf8_lossy(&buffer[..])) //将数组数据转换为字符串let contents = fs::read_to_string("hello.html").unwrap();//响应let response = "HTTP/1.1 200 0k\r\n\r\n"; //协议,状态码 数据let response = format!("{}{}",response,contents);stream.write(response.as_bytes()).unwrap(); // write()接收的是数组,所以需要转换下stream.flush().unwrap(); //阻塞程序运行,直到数据都写入完成}
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>hello!</title>
</head><body><h1>hello</h1><p>Hi from Rust</p>
</body></html>

hello.html如上所示

访问其他路径报故障

fn handle_connection(mut stream : TcpStream) {let mut buffer = [0; 2048]; //定义一个数组存放数据stream.read(&mut buffer).unwrap(); //使用read方法读取数据//println!("Request : {}",String::from_utf8_lossy(&buffer[..])); //将数组数据转换为字符串let get = b"GET / HTTP/1.1\r\n"; //加个b 转换成字节字符串,可进行比较if buffer.starts_with(get) { //缓冲区是否以get开头let contents = fs::read_to_string("hello.html").unwrap();//响应let response = "HTTP/1.1 200 0k\r\n\r\n"; //协议,状态码 数据let response = format!("{}{}",response,contents);stream.write(response.as_bytes()).unwrap(); // write()接收的是数组,所以需要转换下stream.flush().unwrap(); //阻塞程序运行,直到数据都写入完成} else {let status_line = "HTTP/1.1 404 NOT FOUND\r\n\r\n";let contents = fs::read_to_string("404.html").unwrap();let response = format!("{}{}",status_line,contents);stream.write(response.as_bytes()).unwrap(); // write()接收的是数组,所以需要转换下stream.flush().unwrap(); //阻塞程序运行,直到数据都写入完成}

404.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>Hello!</title></head><body><h1>Oops!</h1><p>Sorry,I don't Know what you're asking for.</p></body>
</html>

代码重构

fn handle_connection(mut stream : TcpStream) {let mut buffer = [0; 2048]; //定义一个数组存放数据stream.read(&mut buffer).unwrap(); //使用read方法读取数据//println!("Request : {}",String::from_utf8_lossy(&buffer[..])); //将数组数据转换为字符串let get = b"GET / HTTP/1.1\r\n"; //加个b 转换成字节字符串,可进行比较let (status_line , filename) = if buffer.starts_with(get) {("HTTP/1.1 200 0k\r\n\r\n" , "hello.html")} else {("HTTP/1.1 404 NOT FOUND\r\n\r\n" , "404.html")};let contents = fs::read_to_string(filename).unwrap();let response = format!("{}{}",status_line,contents);stream.write(response.as_bytes()).unwrap(); // write()接收的是数组,所以需要转换下stream.flush().unwrap(); //阻塞程序运行,直到数据都写入完成}

多线程

最简单的

fn main() {let listener = TcpListener::bind("127.0.0.1:7887").unwrap(); //监听7887端口,成功后,就创建一个linstenerfor stream in listener.incoming() {   // listener.incoming 会返回产生一流序列的迭代器;每一个链接,都会产生一个流序列let stream = stream.unwrap();thread::spawn(||{handle_connection(stream);});}
}

这样来一个请求就会创建一个线程

但是如果被攻击,就会一直创建很多线程

加入线程池

main.rs

use std::{fs, io::{Read, Write}, net::{TcpListener, TcpStream}, thread, time::Duration};
use project_web::ThreadPool;
fn main() {let listener = TcpListener::bind("127.0.0.1:7887").unwrap(); //监听7887端口,成功后,就创建一个linstenerlet pool = ThreadPool::new(4);for stream in listener.incoming() {   // listener.incoming 会返回产生一流序列的迭代器;每一个链接,都会产生一个流序列let stream = stream.unwrap();pool.execute(||{handle_connection(stream);});}
}fn handle_connection(mut stream : TcpStream) {let mut buffer = [0; 2048]; //定义一个数组存放数据stream.read(&mut buffer).unwrap(); //使用read方法读取数据//println!("Request : {}",String::from_utf8_lossy(&buffer[..])); //将数组数据转换为字符串let get = b"GET / HTTP/1.1\r\n"; //加个b 转换成字节字符串,可进行比较let sleep = b"GET /sleep HTTP/1.1\r\n";let (status_line , filename) = if buffer.starts_with(get) {("HTTP/1.1 200 0k\r\n\r\n" , "hello.html")} else if buffer.starts_with(sleep) {thread::sleep(Duration::from_secs(5));("HTTP/1.1 200 0k\r\n\r\n" , "hello.html")}else {("HTTP/1.1 404 NOT FOUND\r\n\r\n" , "404.html")};let contents = fs::read_to_string(filename).unwrap();let response = format!("{}{}",status_line,contents);stream.write(response.as_bytes()).unwrap(); // write()接收的是数组,所以需要转换下stream.flush().unwrap(); //阻塞程序运行,直到数据都写入完成}

lib.rs

use std::sync::mpsc;
use std::thread;
use std::sync::Arc;
use std::sync::Mutex;type Job = Box<dyn FnBox + Send + 'static>;pub struct ThreadPool {workers : Vec<Worker>,  //这个类型是模仿 thread::spawn() 的函数返回sender : mpsc::Sender<Job>,
}impl ThreadPool {/// Create a new ThreadPool////// The size is the number of threads in the Pool////// # Panics////// The `new` function will panic if the size is zeropub fn new(size : usize) -> ThreadPool {assert!(size > 0);let (sender,receive) = mpsc::channel();let receive = Arc::new(Mutex::new(receive));let mut workers = Vec::with_capacity(size); //创建一个预分配好空间的vecfor id in 0..size {//创建线程并保存到vec中//这里只想创建线程,而并不想立即执行函数,需要等下一步传入函数后再进行执行workers.push(Worker::new(id ,  Arc::clone(&receive)));}ThreadPool { workers , sender }}pub fn execute<F>(&self,f : F)where F : FnBox + 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,receive : Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {let thread = thread::spawn(move || {loop {while let Ok(job) = receive.lock().unwrap().recv() {println!("worker {} get a job,execute",id);job.call_box();}//let job = receive.lock().unwrap().recv().unwrap(); //这里接收传递过来的闭包,但是这种类型不能直接解引用//*(job)(); 这样解构有问题}} );Worker{ id , thread }}
}trait  FnBox {fn call_box(self: Box<Self>);
}impl<F> FnBox for F
where F : FnOnce()
{fn call_box(self: Box<F>) {(*self)()}
}

优雅地停机

use std::{fs, io::{Read, Write}, net::{TcpListener, TcpStream}, thread, time::Duration};
use project_web::ThreadPool;
fn main() {let listener = TcpListener::bind("127.0.0.1:7887").unwrap(); //监听7887端口,成功后,就创建一个linstenerlet pool = ThreadPool::new(4);for stream in listener.incoming().take(2) {   // listener.incoming 会返回产生一流序列的迭代器;每一个链接,都会产生一个流序列let stream = stream.unwrap();pool.execute(||{handle_connection(stream);});}thread::sleep(Duration::from_secs(1));println!("ShutDown!! Bye!!");
}fn handle_connection(mut stream : TcpStream) {let mut buffer = [0; 2048]; //定义一个数组存放数据stream.read(&mut buffer).unwrap(); //使用read方法读取数据//println!("Request : {}",String::from_utf8_lossy(&buffer[..])); //将数组数据转换为字符串let get = b"GET / HTTP/1.1\r\n"; //加个b 转换成字节字符串,可进行比较let sleep = b"GET /sleep HTTP/1.1\r\n";let (status_line , filename) = if buffer.starts_with(get) {("HTTP/1.1 200 0k\r\n\r\n" , "hello.html")} else if buffer.starts_with(sleep) {thread::sleep(Duration::from_secs(5));("HTTP/1.1 200 0k\r\n\r\n" , "hello.html")}else {("HTTP/1.1 404 NOT FOUND\r\n\r\n" , "404.html")};let contents = fs::read_to_string(filename).unwrap();let response = format!("{}{}",status_line,contents);stream.write(response.as_bytes()).unwrap(); // write()接收的是数组,所以需要转换下stream.flush().unwrap(); //阻塞程序运行,直到数据都写入完成}
use std::sync::mpsc;
use std::thread;
use std::sync::Arc;
use std::sync::Mutex;type Job = Box<dyn FnBox + Send + 'static>;pub struct ThreadPool {workers : Vec<Worker>,  //这个类型是模仿 thread::spawn() 的函数返回sender : mpsc::Sender<Message>,
}enum Message {NewJob(Job),Terminate,
}impl ThreadPool {/// Create a new ThreadPool////// The size is the number of threads in the Pool////// # Panics////// The `new` function will panic if the size is zeropub fn new(size : usize) -> ThreadPool {assert!(size > 0);let (sender,receive) = mpsc::channel();let receive = Arc::new(Mutex::new(receive));let mut workers = Vec::with_capacity(size); //创建一个预分配好空间的vecfor id in 0..size {//创建线程并保存到vec中//这里只想创建线程,而并不想立即执行函数,需要等下一步传入函数后再进行执行workers.push(Worker::new(id ,  Arc::clone(&receive)));}ThreadPool { workers ,  sender }}pub fn execute<F>(&self,f : F)where F : FnBox + Send + 'static  //这是线程创建时传入的闭包类型{let job = Box::new(f);let job = Message::NewJob(job);self.sender.send(job).unwrap();}
}impl Drop for ThreadPool {fn drop(&mut self){for _ in &mut self.workers {self.sender.send(Message::Terminate).unwrap();}for wroker in &mut self.workers {println!("shutting down worker {}",wroker.id);//wroker.thread.join().unwrap();if let Some(thread) = wroker.thread.take() {thread.join().unwrap();}}}
}struct Worker {id : usize,thread : Option<thread::JoinHandle<()>>,
}impl Worker {fn new(id : usize,receive : Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker {let thread = thread::spawn(move || {loop {let message = receive.lock().unwrap().recv().unwrap();match message {Message::NewJob(job) => {println!("worker {} get a job,execute",id);job.call_box();},Message::Terminate => {println!("shutdown!!!");break;}}//let job = receive.lock().unwrap().recv().unwrap(); //这里接收传递过来的闭包,但是这种类型不能直接解引用//*(job)(); 这样解构有问题}} );Worker{ id : id , thread: Some(thread) }}
}trait  FnBox {fn call_box(self: Box<Self>);
}impl<F> FnBox for F
where F : FnOnce()
{fn call_box(self: Box<F>) {(*self)()}
}

这只是一个简单的小工程,目的是为了复习之前的知识;工程中还是有很多问题的。

杂项

类型别名

type xx = 原有类型

type Myi32 = i32;
fn main() {let x = 5;let y : Myi32 = 6;println!("{}",x + y);}

主要用于复杂类型,减少代码量,让程序易读

空类型

!类型;也叫nerve 类型

代表函数返回是什么也没有

函数指针

接收一个类型是函数的参数

 fn main() {let num = 5;let ret = do_twice(add_one, num);println!("{}",ret);
}fn add_one(x : i32) -> i32 {x + 1
}fn do_twice(f : fn(i32) -> i32,arg : i32) -> i32 {f(arg) + f(arg)
}

函数指针与闭包的不同

闭包是trait

fn 是一个类型,不是一个trait

可以直接指定fn为参数类型,不用声明一个以Fn trait为约束的泛型参数

而且,函数指针可以作为参数传递给一个接收闭包的函数

返回一个闭包

不能直接返回闭包

fn return_closure() -> Box<dyn Fn(i32) -> i32 > {Box::new(|x| x + 1)
}fn main() {let y = return_closure();println!("{}",y(2));
}

Box<dyn Trait> 是一种用于动态多态性的类型。它表示一个在运行时指向实现了特定 trait 的类型的指针。dyn Trait 是一种trait对象形式,可以运行时处理不同类型的对象。

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

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

相关文章

2024年mongodb自建三节点副本集详细教程

环境说明 系统centos7.9 自建服务器或云服务器&#xff0c;硬件要求不低于2核2G内存&#xff0c;20G硬盘&#xff0c;文件系统默认是ext4即可。 生产环境最好单独一个磁盘存放数据库&#xff0c;方便数据备份和还原&#xff0c;避免干扰到其他磁盘的运作。 mongodb 4.4.27 …

HTML-表格

表格 1.基本结构 一个完整的表格由&#xff1a;表格标题、表格头部、表格主体、表格脚注&#xff0c;四部分组成 表格涉及到的标签&#xff1a; table&#xff1a;表格 caption&#xff1a;标题 thead&#xff1a;表格头部 tbody&#xff1a;表格主体 tfoot&#xff1a;表格注…

android:persistent和android:priority的区别,对进程优先级有什么影响?

前言&#xff1a;写的apk因为系统busy给我kill了&#xff0c;(adj 900): kill all background&#xff0c;在AndroidManifest.xml添加android:persistent"true"后&#xff0c;被甲方要求不能这样做&#xff0c;还是得从adj改&#xff0c;把 priority改成1000 android…

可Pin to Pin兼容DRV8837的国产H桥电机驱动芯片,具大电流,短gnd,短电源保护功能

在国产牙刷&#xff0c;电子锁设计中&#xff0c;以前方案很多采用TI的DRV8837做直流电机驱动&#xff0c;随着中美贸易战和牙刷&#xff0c;电子锁等产品价格平民化普及&#xff0c;很多大厂在做国产化替代设计方案&#xff0c;GLOBALCHIP 的电机驱动芯片GC8837&#xff0c;价…

解读Android进程优先级ADJ算法

本文基于原生Android 9.0源码来解读进程优先级原理,基于篇幅考虑会精炼部分代码 一、概述 1.1 进程 Android框架对进程创建与管理进行了封装,对于APP开发者只需知道Android四大组件的使用。当Activity, Service, ContentProvider, BroadcastReceiver任一组件启动时,当其所…

YOLOv8改进 | Conv篇 | 2024.1月最新成果可变形卷积DCNv4(适用检测、Seg、分类、Pose、OBB)

一、本文介绍 本文给大家带来的改进机制是2024-1月的最新成果DCNv4,其是DCNv3的升级版本,效果可以说是在目前的卷积中名列前茅了,同时该卷积具有轻量化的效果!一个DCNv4参数量下降越15Wparameters左右,。它主要通过两个方面对前一版本DCNv3进行改进:首先,它移除了空间聚…

Python 流静态文件过滤、端口过滤、同域过滤(host过滤)、代理拦截

目录 静态文件过滤 需求 代码 端口过滤 需求 代码 同域过滤&#xff08;host过滤&#xff09; 需求 代码 静态文件过滤 需求 流量中的url包含大量静态文件请求信息&#xff0c;过滤掉 代码 def __is_static(self, flow: http.HTTPFlow) -> bool:static_ext [.j…

探讨Go语言中的HTTP代理模式:看Go如何玩转网络中转站

在互联网的海洋中&#xff0c;HTTP代理服务器像一座灯塔&#xff0c;为我们的网络冲浪提供了指引。而当Go语言遇上HTTP代理&#xff0c;会碰撞出怎样的火花呢&#xff1f;今天&#xff0c;让我们一起探讨Go语言中的HTTP代理模式&#xff0c;看看它如何玩转这个网络中转站&#…

三:C语言-输入与输出

三&#xff1a;输入与输出 一&#xff1a;输出 1.printf()&#xff1a; ​ 将参数文本输出到屏幕上&#xff0c;它名字里的 f 代表 format&#xff08;格式化&#xff09;&#xff0c;表示可以定制输出文本的格式 ​ printf()不会在行尾自动添加换行符&#xff0c;待运行结…

IDEA(十)2022版本 Services中服务窗口不显示端口号解决

目录 一、问题描述二、问题分析三、解决方案3.1 设置启动参数【生效】3.2 方法二&#xff1a;设置环境变量【不生效】3.3 方法三&#xff1a;删除缓存【不生效】 四、补充&#xff1a;如何手动控制端口显示 一、问题描述 我们在使用 IDEA 的过程中&#xff0c;会发现在 Servic…

Hive之set参数大全-11

设置 Map Join 操作中优化哈希表的工作集大小&#xff08;working set size&#xff09; hive.mapjoin.optimized.hashtable.wbsize 是 Apache Hive 中的一个配置属性&#xff0c;用于设置 Map Join 操作中优化哈希表的工作集大小&#xff08;working set size&#xff09;。 …

Dockerfile:如何写一个Dockerfile文件?

如何写一个Dockerfile文件&#xff1f; &#x1f6a8;推荐参考&#xff1a;Dockerfile&#xff1a;如何写一个Dockerfile文件&#xff1f; 现在的项目肯定都离不开docker&#xff0c;只要是流水线部署就会涉及Dockerfile文件&#xff0c;那么如何写一个正确的编写一个Dockerfil…

dpdk网络转发环境的搭建

文章目录 前言ip命令的使用配置dpdk-basicfwd需要的网络结构测试dpdk-basicfwddpdk-basicfwd代码分析附录basicfwd在tcp转发时的失败抓包信息DPDK的相关设置 前言 上手dpdk有两难。其一为环境搭建。被绑定之后的网卡没有IP&#xff0c;我如何给它发送数据呢&#xff1f;当然&a…

[leetcode] 18. 四数之和

文章目录 题目描述解题方法排序 双指针java代码 相似题目 题目描述 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&a…

二、Kotlin 内置类型

1. 基本类型 1.1 Kotlin 和 Java 的基本类型对比 KotlinJava字节Bytebyte/Byte整型Int & Longint/Integer & long/Long浮点型Float & Doublefloat/Float & double/Double字符Charchar/Chararcter字符串StringString 1.2 定义变量 1.2.1 val 只读变量 &…

qt初入门5:字体设置和元对象系统的练习

空闲时间&#xff0c;参考课本demo&#xff0c;做一下练习。 字体的颜色主要用QPalette类&#xff0c;调色板的作用&#xff0c;控制着窗口部件的颜色和外观&#xff0c;包括背景色、前景色、文本颜色、边框颜色等。 字体的显示样式主要用Font类&#xff0c;用于管理字体。 元…

python三数之和

给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组。 示例 1…

无限学模式-“重塑科研学习路径:ChatGPT应用实战课,开启高效率、高创新的科研之旅!“

ChatGPT 在论文写作与编程方面也具备强大的能力。无论是进行代码生成、错误调试还是解决编程难题&#xff0c;ChatGPT都能为您提供实用且高质量的建议和指导&#xff0c;提高编程效率和准确性。此外&#xff0c;ChatGPT是一位出色的合作伙伴&#xff0c;可以为您提供论文写作的…

【研0日记】24.01.25

回家倒数第6天 受不了了&#xff0c;不想写了&#xff0c;这群b怎么这么能写 用latex写了个伪代码&#xff0c;有点好玩 \usepackage[ruled,linesnumbered]{algorithm2e} \begin{algorithm}[ht] \caption{Pipeline of Kernel Iteration in K-Net.} \label{alg:alg1} …

2024年软考报名时间及条件,小白必看

不少考生开始准备报名2024年软件水平考试&#xff0c;那么报名软考有没有学历、专业以及工作经验等方面的限制呢?今天就给大家梳理下2024年软考考试&#xff0c;若有变更&#xff0c;也会及时更新内容。 免费送备考资料。联系我 2024年软考考试时间 2024年软考有两次考试&a…