基于Rust的多线程 Web 服务器

构建多线程 Web 服务器

  • 在 socket 上监听 TCP 连接
  • 解析少量的 HTTP 请求
  • 创建一个合适的 HTTP 响应
  • 使用线程池改进服务器的吞吐量
  • 优雅的停机和清理
  • 注意:并不是最佳实践

创建项目

~/rust
➜ cargo new helloCreated binary (application) `hello` package~/rust
➜

main.rs 文件

use std::net::TcpListener;fn main() {let listener = TcpListener::bind("127.0.0.1:7878").unwrap();for stream in listener.incoming() {let stream = stream.unwrap();println!("Connection established!");}
}

修改一:

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; 512];stream.read(&mut buffer).unwrap();println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
}

修改二:

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; 512];stream.read(&mut buffer).unwrap();// 请求// Method Request-URI HTTP-Version CRLF// headers CRLF// message-body// 响应// HTTP-Version Status-Code Reason-Phrase CRLF// headers CRLF// message-bodylet response = "HTTP/1.1 200 OK\r\n\r\n";stream.write(response.as_bytes()).unwrap();stream.flush().unwrap();println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
}

修改三:

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; 512];stream.read(&mut buffer).unwrap();// 请求// Method Request-URI HTTP-Version CRLF// headers CRLF// message-body// 响应// HTTP-Version Status-Code Reason-Phrase CRLF// headers CRLF// message-bodylet contents = fs::read_to_string("hello.html").unwrap();let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", contents);stream.write(response.as_bytes()).unwrap();stream.flush().unwrap();
}

修改四:

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; 512];stream.read(&mut buffer).unwrap();let get = b"GET / HTTP/1.1\r\n";if buffer.starts_with(get) {let contents = fs::read_to_string("hello.html").unwrap();let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", contents);stream.write(response.as_bytes()).unwrap();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();stream.flush().unwrap();}
}

修改五:

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; 512];stream.read(&mut buffer).unwrap();let get = b"GET / HTTP/1.1\r\n";let (status_line, filename) = if buffer.starts_with(get) {("HTTP/1.1 200 OK\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();stream.flush().unwrap();
}

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>

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>

单线程Web服务器

use std::fs;
use std::thread;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
use std::time::Duration;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; 512];stream.read(&mut buffer).unwrap();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\r\n\r\n", "hello.html")} else if buffer.starts_with(sleep) {thread::sleep(Duration::from_secs(5));("HTTP/1.1 200 OK\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();stream.flush().unwrap();
}

开启线程

use std::fs;
use std::thread;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
use std::time::Duration;fn main() {let listener = TcpListener::bind("localhost:7878").unwrap();for stream in listener.incoming() {let stream = stream.unwrap();thread::spawn(|| {handle_connection(stream);});}
}fn handle_connection(mut stream: TcpStream) {let mut buffer = [0; 512];stream.read(&mut buffer).unwrap();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\r\n\r\n", "hello.html")} else if buffer.starts_with(sleep) {thread::sleep(Duration::from_secs(5));("HTTP/1.1 200 OK\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();stream.flush().unwrap();
}

lib.rs 文件

use std::thread;pub struct ThreadPool {threads: Vec<thread::JoinHandle<()>>,
}impl ThreadPool {/// Creates a new ThreadPool./// /// The size is the number of threads in the pool./// /// # Panics/// /// The `new` function will panic if the size is zero.pub fn new(size: usize) -> ThreadPool {assert!(size > 0);let mut threads = Vec::with_capacity(size);for _ in 0..size {// create some threads and store them in the vector}ThreadPool { threads }}pub fn execute<F>(&self, f: F)whereF: FnOnce() + Send + 'static,{}
}

lib.rs 修改一

use std::thread;pub struct ThreadPool {workers: Vec<Worker>,
}impl ThreadPool {/// Creates a new ThreadPool.////// The size is the number of threads in the pool.////// # Panics////// The `new` function will panic if the size is zero.pub fn new(size: usize) -> ThreadPool {assert!(size > 0);let mut workers = Vec::with_capacity(size);for id in 0..size {workers.push(Worker::new(id));}ThreadPool { workers }}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 }}
}

lib.rs 修改二

use std::thread;
use std::sync::mpsc;pub struct ThreadPool {workers: Vec<Worker>,sender: mpsc::Sender<Job>,
}struct Job;
impl ThreadPool {/// Creates a new ThreadPool.////// The size is the number of threads in the pool.////// # Panics////// The `new` function will panic if the size is zero.pub fn new(size: usize) -> ThreadPool {assert!(size > 0);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 }}pub fn execute<F>(&self, f: F)whereF: FnOnce() + Send + 'static,{}
}struct Worker {id: usize,thread: thread::JoinHandle<()>,
}impl Worker {fn new(id: usize, receiver: mpsc::Receiver<Job>) -> Worker {let thread = thread::spawn(|| {receiver;});Worker { id, thread }}
}

lib.rs 修改三

use std::thread;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;pub struct ThreadPool {workers: Vec<Worker>,sender: mpsc::Sender<Job>,
}struct Job;
impl ThreadPool {/// Creates a new ThreadPool.////// The size is the number of threads in the pool.////// # Panics////// The `new` function will panic if the size is zero.pub fn new(size: usize) -> ThreadPool {assert!(size > 0);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 }}pub fn execute<F>(&self, f: F)whereF: FnOnce() + Send + 'static,{}
}struct Worker {id: usize,thread: thread::JoinHandle<()>,
}impl Worker {fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {let thread = thread::spawn(|| {receiver;});Worker { id, thread }}
}

lib.rs 修改四

use std::thread;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;pub struct ThreadPool {workers: Vec<Worker>,sender: mpsc::Sender<Job>,
}// struct Job;
type Job = Box<FnOnce() + Send + 'static>;
impl ThreadPool {/// Creates a new ThreadPool.////// The size is the number of threads in the pool.////// # Panics////// The `new` function will panic if the size is zero.pub fn new(size: usize) -> ThreadPool {assert!(size > 0);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 }}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, 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 }}
}

lib.rs 修改五

use std::thread;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;pub struct ThreadPool {workers: Vec<Worker>,sender: mpsc::Sender<Job>,
}// struct Job;
// type Job = Box<FnOnce() + Send + 'static>;
impl ThreadPool {/// Creates a new ThreadPool.////// The size is the number of threads in the pool.////// # Panics////// The `new` function will panic if the size is zero.pub fn new(size: usize) -> ThreadPool {assert!(size > 0);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 }}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<()>,
}trait FnBox {fn call_box(self: Box<Self>);
}impl<F: FnOnce()> FnBox for F {fn call_box(self: Box<F>) {(*self)()}
}type Job = Box<dyn FnBox + Send + 'static>;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)();job.call_box();});Worker { id, thread }}
}

lib.rs 修改六

use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;pub struct ThreadPool {workers: Vec<Worker>,sender: mpsc::Sender<Job>,
}// struct Job;
// type Job = Box<FnOnce() + Send + 'static>;
impl ThreadPool {/// Creates a new ThreadPool.////// The size is the number of threads in the pool.////// # Panics////// The `new` function will panic if the size is zero.pub fn new(size: usize) -> ThreadPool {assert!(size > 0);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 }}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<()>,
}trait FnBox {fn call_box(self: Box<Self>);
}impl<F: FnOnce()> FnBox for F {fn call_box(self: Box<F>) {(*self)()}
}type Job = Box<dyn FnBox + Send + 'static>;impl Worker {fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {let thread = thread::spawn(move || loop {while let Ok(job) = receiver.lock().unwrap().recv() {println!("Worker {} got a job; executing.", id);job.call_box();}});Worker { id, thread }}
}

优雅的停机和清理

use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;pub struct ThreadPool {workers: Vec<Worker>,sender: mpsc::Sender<Job>,
}// struct Job;
// type Job = Box<FnOnce() + Send + 'static>;
impl ThreadPool {/// Creates a new ThreadPool.////// The size is the number of threads in the pool.////// # Panics////// The `new` function will panic if the size is zero.pub fn new(size: usize) -> ThreadPool {assert!(size > 0);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 }}pub fn execute<F>(&self, f: F)whereF: FnOnce() + Send + 'static,{let job = Box::new(f);self.sender.send(job).unwrap();}
}impl Drop for ThreadPool {fn drop(&mut self) {for worker in &mut self.workers {println!("Shutting down worker {}", worker.id);worker.thread.join().unwrap()}}
}struct Worker {id: usize,thread: thread::JoinHandle<()>,
}trait FnBox {fn call_box(self: Box<Self>);
}impl<F: FnOnce()> FnBox for F {fn call_box(self: Box<F>) {(*self)()}
}type Job = Box<dyn FnBox + Send + 'static>;impl Worker {fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {let thread = thread::spawn(move || loop {while let Ok(job) = receiver.lock().unwrap().recv() {println!("Worker {} got a job; executing.", id);job.call_box();}});Worker { id, thread }}
}

修改优化一:

use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;pub struct ThreadPool {workers: Vec<Worker>,sender: mpsc::Sender<Job>,
}// struct Job;
// type Job = Box<FnOnce() + Send + 'static>;
impl ThreadPool {/// Creates a new ThreadPool.////// The size is the number of threads in the pool.////// # Panics////// The `new` function will panic if the size is zero.pub fn new(size: usize) -> ThreadPool {assert!(size > 0);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 }}pub fn execute<F>(&self, f: F)whereF: FnOnce() + Send + 'static,{let job = Box::new(f);self.sender.send(job).unwrap();}
}impl Drop for ThreadPool {fn drop(&mut self) {for worker in &mut self.workers {println!("Shutting down worker {}", worker.id);if let Some(thread) = worker.thread.take() {thread.join().unwrap();}}}
}struct Worker {id: usize,thread: Option<thread::JoinHandle<()>>,
}trait FnBox {fn call_box(self: Box<Self>);
}impl<F: FnOnce()> FnBox for F {fn call_box(self: Box<F>) {(*self)()}
}type Job = Box<dyn FnBox + Send + 'static>;impl Worker {fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {let thread = thread::spawn(move || loop {while let Ok(job) = receiver.lock().unwrap().recv() {println!("Worker {} got a job; executing.", id);job.call_box();}});Worker {id,thread: Some(thread),}}
}

最终版 lib.rs 文件

use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;enum Message {NewJob(Job),Terminate,
}pub struct ThreadPool {workers: Vec<Worker>,sender: mpsc::Sender<Message>,
}// struct Job;
// type Job = Box<FnOnce() + Send + 'static>;
impl ThreadPool {/// Creates a new ThreadPool.////// The size is the number of threads in the pool.////// # Panics////// The `new` function will panic if the size is zero.pub fn new(size: usize) -> ThreadPool {assert!(size > 0);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 }}pub fn execute<F>(&self, f: F)whereF: FnOnce() + Send + 'static,{let job = Box::new(f);self.sender.send(Message::NewJob(job)).unwrap();}
}impl Drop for ThreadPool {fn drop(&mut self) {println!("Sending terminate message to all workers.");for _ in &mut self.workers {self.sender.send(Message::Terminate).unwrap();}println!("Shutting down all workers.");for worker in &mut self.workers {println!("Shutting down worker {}", worker.id);if let Some(thread) = worker.thread.take() {thread.join().unwrap();}}}
}struct Worker {id: usize,thread: Option<thread::JoinHandle<()>>,
}trait FnBox {fn call_box(self: Box<Self>);
}impl<F: FnOnce()> FnBox for F {fn call_box(self: Box<F>) {(*self)()}
}type Job = Box<dyn FnBox + Send + 'static>;impl Worker {fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker {let thread = thread::spawn(move || loop {let message = receiver.lock().unwrap().recv().unwrap();match message {Message::NewJob(job) => {println!("Worker {} got a job; executing.", id);job.call_box();}Message::Terminate => {println!("Worker {} got a job; executing.", id);break;}}});Worker {id,thread: Some(thread),}}
}

最终版 main.rs 文件

use hello::ThreadPool;
use std::fs;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
use std::thread;
use std::time::Duration;fn main() {let listener = TcpListener::bind("localhost:7878").unwrap();let pool = ThreadPool::new(4);for stream in listener.incoming().take(2) {let stream = stream.unwrap();pool.execute(|| {handle_connection(stream);});}println!("Shutting down.");
}fn handle_connection(mut stream: TcpStream) {let mut buffer = [0; 512];stream.read(&mut buffer).unwrap();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\r\n\r\n", "hello.html")} else if buffer.starts_with(sleep) {thread::sleep(Duration::from_secs(5));("HTTP/1.1 200 OK\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();stream.flush().unwrap();
}

运行

hello on  master [?] is 📦 0.1.0 via 🦀 1.67.1 
➜ cargo run  Compiling hello v0.1.0 (/Users/qiaopengjun/rust/hello)Finished dev [unoptimized + debuginfo] target(s) in 0.43sRunning `target/debug/hello`
Worker 0 got a job; executing.
Shutting down.
Sending terminate message to all workers.
Shutting down all workers.
Shutting down worker 0
Worker 1 got a job; executing.
Worker 2 got a job; executing.
Worker 3 got a job; executing.
Worker 1 got a job; executing.
Worker 0 got a job; executing.
Shutting down worker 1
Shutting down worker 2
Shutting down worker 3hello on  master [?] is 📦 0.1.0 via 🦀 1.67.1 took 21.9s 
➜ 

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

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

相关文章

kaggle之皮肤癌数据的深度学习测试

kaggle之皮肤癌数据的深度学习测试 近期一直在肝深度学习 很久之前&#xff0c;曾经上手搞过一段时间的深度学习&#xff0c;似乎是做轮胎花纹的识别&#xff0c;当初用的是TensorFlow&#xff0c;CPU版本的&#xff0c;但已经很长时间都没弄过了 现在因为各种原因&#xff…

全面解析平台工程与 DevOps 的区别与联系

平台工程的概念非常流行&#xff0c;但很多开发人员仍然不清楚它是如何实际运作的&#xff0c;这是非常正常的。 平台工程是与 DevOps 并行吗&#xff1f;还是可以相互替代&#xff1f;或者 DevOps 和平台工程是两个完全不同的概念&#xff1f; 一种比较容易将两者区分开来的方…

Nginx(三): 项目实战之conf

conf文件详解 #user nobody; # 设置了Nginx的工作进程数。这里设置为1&#xff0c;表示只有一个工作进程。通常在多核CPU的服务器上&#xff0c;可以设置为与CPU核心数相等的值以提高性能 worker_processes 1;# 设置了Nginx工作进程的最大文件打开数量限制。这里设置为65535…

打包的意义 作用等前端概念集合 webpack基础配置等

基础网页是什么&#xff1f; 在学校最基础的三剑客 原生JS CSS H5就可以开发静态网页了 对于浏览器而言也能识别这些基础的文件和语法&#xff0c;真正的所见即所得&#xff0c;非常直接。 为什么要使用框架库&#xff1f; 对于常用的前端框架而言&#xff0c;无论是Vue Rea…

Unity和iOS 原生专题一 unity打包设置C#设置 Xcode添加OTHER_LDFLAGS和Info.plist设置

一、 获取 PBXProject PBXProject的很多操作都是通过guid的 var pbxProject new PBXProject(); 二 、获取projectPath var projectPath PBXProject.GetPBXProjectPath(iosProjectPath); 三 、读取pbxProject 文件 pbxProject.ReadFromFile(projectPath); 四 、 读取Uni…

普通屏幕已过时?裸眼3D屏幕显示效果更胜一筹!

随着多媒体技术的迅猛进步&#xff0c;我们日常生活中的内容展现方式&#xff0c;已经经历了前所未有的变革。在这其中&#xff0c;裸眼3D屏幕的应用&#xff0c;无疑是最为引人注目的亮点&#xff0c;它相较于传统屏幕&#xff0c;在显示效果上展现出了鲜明的优势&#xff0c;…

RGB灯珠的控制-单片机通用模板

RGB灯珠的控制-单片机通用模板 一、RGB控制的原理二、RGB.c的实现三、RGB.h的实现四、color色彩空间变换以及控制渐变一、RGB控制的原理 ①通过IO发送脉冲识别0/1编码,组合24Bit的RGB数据,从而控制RGB;②每个RGB灯珠通过DIN、DOU进行级联起来;③通过HSV色彩转换成RGB从而控…

ArcGIS批量寻找图层要素中的空洞

空洞指的是图层中被要素包围所形成的没有被要素覆盖的地方&#xff0c;当图层要素数量非常庞大时&#xff0c;寻找这些空洞就不能一个一个的通过目测去寻找了&#xff0c;需要通过使用工具来实现这一目标。 一、【要素转线】工具 利用【要素转线】工具可以将空洞同图层要素处于…

【触摸案例-多点触摸的案例 Objective-C语言】

一、我们来做这个多点触摸的案例 1.首先呢,按着这个option键啊,可以模拟多点触摸, 然后呢,再去怎么着去画圈儿, 它这个里边就会产生一个imageView,跟着你去变,会有这么一个效果, 那么,首先啊,我们新建一个项目, Name:03-多点触摸的案例 1)首先,我们把控制器的v…

Xcode for Mac:强大易用的集成开发环境

Xcode for Mac是一款专为苹果开发者打造的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它集成了代码编辑器、编译器、调试器等一系列开发工具&#xff0c;让开发者能够在同一界面内完成应用的开发、测试和调试工作。 Xcode for Mac v15.2正式版下载 Xcode支持多种编程…

【OpenCV】(一)wechat_qrcode检测二维码

一、注意&#xff1a; opencv-4.8.0之前的版本wechat_qrcode检测二维码是矩形检测&#xff1b; opencv-4.8.0的版本wechat_qrcode检测二维码是关键点检测&#xff1b; 二、C代码 代码目前是通用的&#xff08;仅在opencv-4.5.5、opencv-4.8.1上测试过&#xff09;&#xff1…

ShardingSphere 5.x 系列【25】 数据分片原理之 SQL 解析

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 3.1.0 本系列ShardingSphere 版本 5.4.0 源码地址:https://gitee.com/pearl-organization/study-sharding-sphere-demo 文章目录 1. 分片执行流程1.1 Simple Push Down1.2 SQL Federation2. SQL 解析2.1 解析…

力扣700,二叉搜索树中的搜索

700. 二叉搜索树中的搜索 - 力扣&#xff08;LeetCode&#xff09; 法1&#xff0c;递归 给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和一个整数值 val。 你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在&#xff0c;则返…

道可云元宇宙每日资讯|苹果推出开放语言模型OpenELM

道可云元宇宙每日简报&#xff08;2024年4月26日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 工信部发布中关村园区建设方案&#xff1a;加快突破元宇宙核心技术 4 月 25 日&#xff0c;工业和信息化部、科学技术部、北京市人民政府印发了《中关村世界领先科技…

分布式WEB应用中会话管理的变迁之路

Session一词直译为“会话”&#xff0c;意指有始有终的一系列动作&#xff0f;消息。Session是Web应用蓬勃发展的产物之一&#xff0c;在Web应用中隐含有“面向连接”和“状态保持”两个含义&#xff0c;同时也指代了Web服务器与客户端之间进行状态保持的解决方案。 在Web应用…

web server apache tomcat11-23-APR

前言 整理这个官方翻译的系列&#xff0c;原因是网上大部分的 tomcat 版本比较旧&#xff0c;此版本为 v11 最新的版本。 开源项目 从零手写实现 tomcat minicat 别称【嗅虎】心有猛虎&#xff0c;轻嗅蔷薇。 系列文章 web server apache tomcat11-01-官方文档入门介绍 web…

基于JAVA实现的推箱子小游戏

Java推箱子小游戏实现&#xff1a; 推箱子小游戏曾经在我们的童年给我们带来了很多乐趣。推箱子这款游戏现在基本上没人玩了&#xff0c;甚至在新一代人的印象中都已毫无记忆了。。。但是&#xff0c;这款游戏可以在一定程度上锻炼自己的编程能力。 窗口画面设计&#xff1a;i…

8点法估计基础矩阵

估计基础矩阵 文章目录 估计基础矩阵8点法归一化 8点法 8点法 根据两幅图像中8个对应点对之间的关系&#xff0c;采用SVD求 解最小二乘方 约束&#xff1a;det(F) 0 假设已知N对点的对应关系&#xff1a; { x i , x i ′ } i 1 N \{x_i,x^{\prime}_i\}_{i1}^N {xi​,xi′​…

Mysql索引规范及原理分析

1 Mysql存储引擎 MySQL中的数据用各种不同的技术存储在文件中&#xff0c;每一种技术都使用不同的存储机制、索引技巧、锁定水平并最终提供不同的功能和能力&#xff0c;这些不同的技术以及配套的功能在MySQL中称为存储引擎。 存储引擎是MySQL将数据存储在文件系统中的存储方…

【打工日常】云原生之部署个人使用的高速下载器Gopeed

​一、Gopeed介绍1.Gopeed简介 Gopeed(全称 Go Speed),直译过来中文名叫做够快下载器(不是狗屁下载器!),是一款由 Golang + Flutter 开发的高速下载器,支持(HTTP、BitTorrent、Magnet)协议下载,并且支持所有平台使用。支持的平台:Windows,Macos,Linux,Android,…