rust学习-构建服务器
单线程server
服务器会依次处理每一个请求,在完成第一个连接的处理之前不会处理第二个连接
// cat main.rs
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;fn main() {let listener = TcpListener::bind("127.0.0.1:7878").unwrap();for stream in listener.incoming() {// stream 此时类型:core::result::Result<std::net::tcp::TcpStream, std::io::error::Error>// unwrap() 要么取出Result的Some中的值,要么Paniclet stream = stream.unwrap();// stream 此时类型:std::net::tcp::TcpStreamhandle_connection(stream);}
}fn handle_connection(mut stream: TcpStream) {let mut buffer = [0; 1024]; // [dataType;size],为每一个元素初始化为0stream.read(&mut buffer).unwrap();// 浏览器输入得到内容如下// Request: GET /favicon.ico HTTP/1.1// Host: 127.0.0.1:7878// Connection: keep-alive// sec-ch-ua: "Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"// sec-ch-ua-mobile: ?0// User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36// sec-ch-ua-platform: "macOS"// Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8// Sec-Fetch-Site: same-origin// Sec-Fetch-Mode: no-cors// Sec-Fetch-Dest: image// Referer: http://127.0.0.1:7878/// Accept-Encoding: gzip, deflate, br// Accept-Language: zh-CN,zh;q=0.9println!("Request: {}", String::from_utf8_lossy(&buffer[..]));// 一个微型的成功 HTTP 响应,格式如下// HTTP-Version Status-Code Reason-Phrase CRLF// headers CRLF// message-body// let response = "HTTP/1.1 200 OK\r\n\r\n";// 给前端传输htmllet contents = fs::read_to_string("hello.html").unwrap();let response = format!("HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",contents.len(),contents);// flush 会等待并阻塞程序执行直到所有字节都被写入连接中// TcpStream 包含一个内部缓冲区来最小化对底层操作系统的调用stream.write(response.as_bytes()).unwrap();stream.flush().unwrap();
}// 客户端的请求行
// Method Request-URI HTTP-Version CRLF
// headers CRLF
// message-body
// hello.html
<!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>
区分响应
// cat main.rs
use std::fs;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;fn main() {let listener = TcpListener::bind("127.0.0.1:7878").unwrap();for stream in listener.incoming() {let stream = stream.unwrap();handle_connection(stream);}
}fn handle_connection(mut stream: TcpStream) {let mut buffer = [0; 1024];stream.read(&mut buffer).unwrap();println!("Request: {}", String::from_utf8_lossy(&buffer[..]));// 将与 / 请求相关的数据硬编码进变量 get// 数据开头增加 b"" 字节字符串语法将其转换为字节字符串let get = b"GET / HTTP/1.1\r\n";// 检查 buffer 是否以 get 中的字节开头。如果是,这就是一个格式良好的 / 请求if buffer.starts_with(get) {let contents = fs::read_to_string("hello.html").unwrap();let response = format!("HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",contents.len(),contents);stream.write(response.as_bytes()).unwrap();stream.flush().unwrap();} else {// 如果 buffer 不 以 get 中的字节开头,返回404let status_line = "HTTP/1.1 404 NOT FOUND";let contents = fs::read_to_string("404.html").unwrap();let response = format!("{}\r\nContent-Length: {}\r\n\r\n{}",status_line,contents.len(),contents);stream.write(response.as_bytes()).unwrap();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>
重构 handle_connection
fn handle_connection(mut stream: TcpStream) {// --snip--let (status_line, filename) = if buffer.starts_with(get) {("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 response = format!("{}\r\nContent-Length: {}\r\n\r\n{}",status_line,contents.len(),contents);stream.write(response.as_bytes()).unwrap();stream.flush().unwrap();
}
单线程
use std::thread;
use std::time::Duration;
use std::fs;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;fn main() {let listener = TcpListener::bind("127.0.0.1:7878").unwrap();for stream in listener.incoming() {let stream = stream.unwrap();handle_connection(stream);}
}fn handle_connection(mut stream: TcpStream) {let mut buffer = [0; 1024];stream.read(&mut buffer).unwrap();println!("Request: {}", String::from_utf8_lossy(&buffer[..]));let get = b"GET / HTTP/1.1\r\n";let sleep = b"GET /sleep HTTP/1.1\r\n";let (status_line, filename) = if buffer.starts_with(get) {("HTTP/1.1 200 OK", "hello.html")} else if buffer.starts_with(sleep) {thread::sleep(Duration::from_secs(5));("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 response = format!("{}\r\nContent-Length: {}\r\n\r\n{}",status_line,contents.len(),contents);stream.write(response.as_bytes()).unwrap();stream.flush().unwrap();
}
多线程
奔溃版本1
fn main() {let listener = TcpListener::bind("127.0.0.1:7878").unwrap();for stream in listener.incoming() {let stream = stream.unwrap();thread::spawn(|| {handle_connection(stream);});}
}
// thread::spawn的类型
// F是参数类型,T是返回类型
// F 的 trait 有 (FnOnce() -> T)、Send,生命周期有 'static
// 处理请求的线程只会执行闭包一次,所以用FnOnce
// 需要 Send 来将闭包从一个线程转移到另一个线程
// 'static 是因为并不知道线程会执行多久
pub fn spawn<F, T>(f: F) -> JoinHandle<T>whereF: FnOnce() -> T + Send + 'static,T: Send + 'static
根据奔溃版本和thread::spawn构造假想线程池
fn main() {let listener = TcpListener::bind("127.0.0.1:7878").unwrap();let pool = ThreadPool::new(4);for stream in listener.incoming() {let stream = stream.unwrap();pool.execute(|| {handle_connection(stream);});}
}
impl ThreadPool {// --snip--pub fn execute<F>(&self, f: F)whereF: FnOnce() + Send + 'static{}
}
构造线程池-糟糕版本
thread::spawn,期望获取一些一旦创建线程就应该执行的代码
但是对于线程池不适用,线程池是当在需要时才执行代码执行
(1)定义 Worker 结构体存放 id 和 JoinHandle<()>
(2)修改 ThreadPool 存放一个 Worker 实例的 vector
(3)定义 Worker::new 函数,它获取一个 id 数字并返回一个带有 id 和用空闭包分配的线程的 Worker 实例
(4)在 ThreadPool::new 中,使用 for 循环计数生成 id,使用这个 id 新建 Worker,并储存进 vector 中
execute 将通过 ThreadPool ,向其中空闲的线程 Worker 实例发送任务。
(1)ThreadPool 会创建一个通道并充当发送端。
(2)每个 Worker 将会充当通道的接收端。
(3)新建一个 Job 结构体来存放用于向通道中发送的闭包。
(4)execute 方法会在通道发送端发出期望执行的任务。
(5)在线程中,Worker 会遍历通道的接收端并执行任何接收到的任务。
use std::thread;
use std::sync::mpsc;pub struct ThreadPool {workers: Vec<Worker>,// threads: Vec<thread::JoinHandle<()>>,sender: mpsc::Sender<Job>,
}struct Job;impl ThreadPool {/// 创建线程池。////// 线程池中线程的数量。////// # Panics////// `new` 函数在 size 为 0 时会 panic。pub fn new(size: usize) -> ThreadPool {// 创建一个没有任何线程的线程池应该是不可恢复的错误assert!(size > 0);/*// with_capacity, 与 Vec::new 做了同样的工作// 它为 vector 预先分配空间// 预分配比 Vec::new 要稍微有效率let mut threads = Vec::with_capacity(size);for _ in 0..size {// create some threads and store them in the vector}ThreadPool {threads}*//*let mut workers = Vec::with_capacity(size);for id in 0..size {workers.push(Worker::new(id));}ThreadPool {workers}*/// 创建通道并让 ThreadPool 实例充当发送端// Job是通道的待执行任务let (sender, receiver) = mpsc::channel();let mut workers = Vec::with_capacity(size);for id in 0..size {workers.push(Worker::new(id, receiver));}ThreadPool {workers,sender,}}// 在 execute 方法中获得期望执行的闭包pub fn execute<F>(&self, f: F)whereF: FnOnce() + Send + 'static{}
}struct Worker {id: usize,thread: thread::JoinHandle<()>,
}/*
impl Worker {fn new(id: usize) -> Worker {// 一个空闭包let thread = thread::spawn(|| {});Worker {id,thread,}}
}
*/impl Worker {// Rust 所提供的通道实现是多生产者,单消费者// 将通道的接收端传递给 workerfn new(id: usize, receiver: mpsc::Receiver<Job>) -> Worker {let thread = thread::spawn(|| {receiver;});Worker {id,thread,}}
}
终极版本
// cat lib.rs
use std::thread;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;pub struct ThreadPool {workers: Vec<Worker>,// threads: Vec<thread::JoinHandle<()>>,sender: mpsc::Sender<Job>,
}type Job = Box<dyn FnOnce() + Send + 'static>;impl ThreadPool {/// 创建线程池。////// 线程池中线程的数量。////// # Panics////// `new` 函数在 size 为 0 时会 panic。pub fn new(size: usize) -> ThreadPool {// 创建一个没有任何线程的线程池应该是不可恢复的错误assert!(size > 0);/*// with_capacity, 与 Vec::new 做了同样的工作// 它为 vector 预先分配空间// 预分配比 Vec::new 要稍微有效率let mut threads = Vec::with_capacity(size);for _ in 0..size {// create some threads and store them in the vector}ThreadPool {threads}*//*let mut workers = Vec::with_capacity(size);for id in 0..size {workers.push(Worker::new(id));}ThreadPool {workers}*/// 创建通道并让 ThreadPool 实例充当发送端// Job是通道的待执行任务/*let (sender, receiver) = mpsc::channel();let mut workers = Vec::with_capacity(size);for id in 0..size {workers.push(Worker::new(id));}ThreadPool {workers,sender,}*/let (sender, receiver) = mpsc::channel();let receiver = Arc::new(Mutex::new(receiver));let mut workers = Vec::with_capacity(size);for id in 0..size {workers.push(Worker::new(id, Arc::clone(&receiver)));}ThreadPool {workers,sender,}}// 在 execute 方法中获得期望执行的闭包pub fn execute<F>(&self, f: F)whereF: FnOnce() + 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) -> Worker {// 一个空闭包let thread = thread::spawn(|| {});Worker {id,thread,}}
}
*/// Rust 所提供的通道实现是多生产者,单消费者
// 将通道的接收端传递给 worker
impl Worker {fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {let thread = thread::spawn(move || {loop {let job = receiver.lock().unwrap().recv().unwrap();println!("Worker {} got a job; executing.", id);job();}});Worker {id,thread,}}
}
// cat main.rs
use std::thread;
use std::time::Duration;
use std::fs;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
use rust_demo::ThreadPool;fn main() {let listener = TcpListener::bind("127.0.0.1:7878").unwrap();let pool = ThreadPool::new(4);for stream in listener.incoming() {let stream = stream.unwrap();pool.execute(|| {handle_connection(stream);});}
}fn handle_connection(mut stream: TcpStream) {let mut buffer = [0; 1024];stream.read(&mut buffer).unwrap();println!("Request: {}", String::from_utf8_lossy(&buffer[..]));let get = b"GET / HTTP/1.1\r\n";let sleep = b"GET /sleep HTTP/1.1\r\n";let (status_line, filename) = if buffer.starts_with(get) {("HTTP/1.1 200 OK", "hello.html")} else if buffer.starts_with(sleep) {thread::sleep(Duration::from_secs(5));("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 response = format!("{}\r\nContent-Length: {}\r\n\r\n{}",status_line,contents.len(),contents);stream.write(response.as_bytes()).unwrap();stream.flush().unwrap();
}
相关文章:
rust学习-构建服务器
单线程server 服务器会依次处理每一个请求,在完成第一个连接的处理之前不会处理第二个连接 // cat main.rs use std::io::prelude::*; use std::net::TcpListener; use std::net::TcpStream;fn main() {let listener TcpListener::bind("127.0.0.1:7878&quo…...

Java并发----进程、线程、并行、并发
一、进程与线程 进程 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至 CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的 当一个程序被运行…...

【计算机网络】第 4 课 - 物理层
欢迎来到博主 Apeiron 的博客,祝您旅程愉快 ! 时止则止,时行则行。动静不失其时,其道光明。 目录 1、物理层的基本概念 2、物理层协议的主要任务 3、物理层任务 4、总结 1、物理层的基本概念 在计算机网络中,用来…...

深入理解MVVM架构模式
MVVM原理 MVVM是一种用于构建用户界面的软件架构模式,它的名称代表着三个组成部分:Model(模型)、View(视图)和ViewModel(视图模型)。MVVM的主要目标是将应用程序的UI与其底层数据模…...

配置IPv6 over IPv4手动隧道示例
组网需求 如图1所示,两台IPv6主机分别通过SwitchA和SwitchC与IPv4骨干网络连接,客户希望两台IPv6主机能通过IPv4骨干网互通。 图1 配置IPv6 over IPv4手动隧道组网图 配置思路 配置IPv6 over IPv4手动隧道的思路如下: 配置IPv4网络。配置接…...

Vue3--->组合式API与Pinia
目录 使用create-vue搭建 1、使用create-vue创建项目 2、项目目录和关键文件 组合式API 1、组合式API - setup选项 2、组合式API - reactive和ref函数 3、组合式API - computed 4、组合式API - watch 1、基础使用 - 侦听单个数据 2、基础使用 - 侦听多个数据 3、immediate&…...

三言两语说透柯里化和反柯里化
JavaScript中的柯里化(Currying)和反柯里化(Uncurrying)是两种很有用的技术,可以帮助我们写出更加优雅、泛用的函数。本文将首先介绍柯里化的概念、实现原理和应用场景,然后介绍反柯里化的概念、实现原理和应用场景,通过大量的代码示例帮助读…...
细讲TCP三次握手四次挥手(四)
常见面试题 为什么TCP连接的时候是3次?2次不可以吗? 因为需要考虑连接时丢包的问题,如果只握手2次,第二次握手时如果服务端发给客户端的确认报文段丢失,此时服务端已经准备好了收发数(可以理解服务端已经连接成功)据…...

HarmonyOS/OpenHarmony元服务开发-配置卡片的配置文件
卡片相关的配置文件主要包含FormExtensionAbility的配置和卡片的配置两部分: 1.卡片需要在module.json5配置文件中的extensionAbilities标签下,配置FormExtensionAbility相关信息。FormExtensionAbility需要填写metadata元信息标签,其中键名称…...

mac安装nacos,M1芯片
第一步,官网下载 》nacos官网 去github中下载对应的版本,本人下载的是1.4.1版本 在这儿选择其他的版本,下面这里选择 tar.gz 压缩包 解压后放到一个非中文的目录下,我选择在 user目录下面创建一个other目录,将使用的环…...

老板说把跳针改过去,什么是主板跳针
最近骑车拍了很多视频,把电脑磁盘堆满了,想着买一条固态SSD卡扩展一下。 一咬牙一跺脚,直接安排,毫不犹豫。顺带加装了无限网卡和蓝牙5.2。 收到后立马安装。安装完发现识别不到新磁盘 确认安装没问题。然后就去问固态硬盘的客服 …...

PyTorch代码实战入门
人这辈子千万不要马虎两件事 一是找对爱人、二是选对事业 因为太阳升起时要投身事业 太阳落山时要与爱人相拥 一、准备数据集 蚂蚁蜜蜂数据集 蚂蚁蜜蜂的图片,文件名就是数据的label 二、使用Dataset加载数据 打开pycharm,选择Anaconda创建的pytorch环…...

TSINGSEE青犀视频汇聚平台EasyCVR多种视频流播放协议介绍
众所周知,TSINGSEE青犀视频汇聚平台EasyCVR可支持多协议方式接入,包括主流标准协议GB28181、RTSP/Onvif、RTMP等,以及厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。今天我们来说一说,EasyCVR平台支持分…...

Vivado进行自定义IP封装
一. 简介 本篇文章将介绍如何使用Vivado来对上篇文章(FPGA驱动SPI屏幕)中的代码进行一个IP封装,Vivado自带的IP核应该都使用过,非常方便。 这里将其封装成IP核的目的主要是为了后续项目的调用,否则当我新建一个项目的时候,我需要将…...

开放自动化软件的硬件平台
自动化行业的产品主要以嵌入式系统为主,历来对产品硬件的可靠性和性能都提出很高的要求。最典型的产品要数PLC。PLC 要求满足体积小,实时性,可靠性,可扩展性强,环境要求高等特点。它们通常采用工业级高性能嵌入式SoC 实…...

AdvancedInstaller打包程序
文章目录 1. AdvancedInstaller 下载2. AdvancedInstaller 启动3. 新建工程4. 配置安装包详细信息5. 配置安装参数6. 添加要打包的文件7. 设置安装完成后启动程序8. 构建打包 1. AdvancedInstaller 下载 下载网址:https://www.advancedinstaller.com/ 2. AdvancedIn…...
无穷限积分习题
前置知识:无穷限积分 习题1 计算 ∫ 1 ∞ ln x x 2 d x \int_1^{\infty}\dfrac{\ln x}{x^2}dx ∫1∞x2lnxdx 解: \qquad 原式 ( − ln x x ) ∣ 1 ∞ ∫ 1 ∞ 1 x 2 d x ( − ln x x ) ∣ 1 ∞ ( − 1 x ) ∣ 1 ∞ (-\dfrac{\…...

AI 3D结构光技术加持,小米引领智能门锁新标准
一直以来,小米智能门锁系列产品让更多家庭走进了安全便捷的智能生活,安全至上的设计让很多家庭都轻松告别了随身钥匙。 7月27日,小米正式推出小米智能门锁M20 Pro,再一次引领智能门锁产品的发展潮流。该款门锁采用AI 3D结构光技术…...

管理类联考——逻辑——形式逻辑——汇总篇
简述 形式逻辑: 识别题型:逻辑符号表达及标志词:联假言符号化特殊命题“除非否则”;五大关系:矛盾、等价、包含、至少有一真、至少有一假;【通过“关系”,串联起“假联选”言】 识别题型&…...

架构的分类
目录 一、 RUP41 架构 1.1 RUP41架构方法概述 1.2 RUP41架构总体 1.3 RUP41架构方法内容 1.3.1 逻辑视图 1.3.2 开发视图 1.3.3 物理视图 1.3.4 处理视图 1.3.5 场景视图 二、 TOGAF9 架构 2.1 TOGAF9 架构概述 2.2 TOGAF9 架构分类 2.2.1 业务架构 2.2.2 数据架…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...