在给大家介绍这个关于Rust的项目前,大家可以先看看我之前写的这两篇博客,巩固一下Rust的基础知识
博客1:为什么不来学一下Rust!!!
https://blog.csdn.net/speaking_me/article/details/143450484
博客2:Rust的应用开发场景,抓住Rust的红利开吃!!!
https://blog.csdn.net/speaking_me/article/details/143621520
同时也回答一下某些小伙伴的问题,说我为什么老是公开博客,不设置需粉丝观看或者VIP,哈哈哈,我之前是有这样的想法,但是经历了秋招面试,我慢慢的发现我的竞争者根本就不是大家而是自己,看自己能否在时代的洪流中坚持自我,找寻自我,是啊,我拿着那些虚拟财产有什么用呢,帮助大家,帮助自己进步才是我原本的出发点,我在这里看到了很多没见过的技术,找到了很多问题的解决方法,结识了各种领域的大佬,在社区畅聊,我觉得这就够了,不要在乎那些,坚持自我,做出一些贡献就足够。
这里送给自己也送给大家一句话,这也是我最喜欢的一句:你说遇事不决可问春风,春风不语既随本心。可我若本心坚定,怎会遇事不决。春风也有春风愁,不劳春风为我忧。(意思就不给大家解释了哈,懂的自然明白如何做)
那我们先从简单的项目做起
下面是一个简单的 Rust 项目示例,包括一个命令行应用程序,该应用程序可以读取用户的输入并打印出来。我们将逐步介绍如何创建和运行这个项目。
1. 安装 Rust
首先,你需要安装 Rust。最简单的方法是使用 rustup
,这是一个官方的 Rust 工具链管理器。
在 Linux 和 macOS 上安装
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
在 Windows 上安装
你可以从 Rust 官方网站 下载安装程序并按照提示进行安装。
为了方便大家我把链接也搬过来了,喂大家嘴里:https://blog.csdn.net/xinyingzai/article/details/135459640
安装完成后,打开终端并运行以下命令以确保 Rust 已正确安装:
rustc --version
2. 创建一个新的 Rust 项目
使用 Cargo(Rust 的包管理器和构建系统)创建一个新的项目。
cargo new hello_world cd hello_world
3. 编写代码
编辑 src/main.rs
文件,添加以下代码:
use std::io; fn main() { println!("Hello, world!"); // 读取用户输入 println!("Please enter your name:"); let mut name = String::new(); io::stdin().read_line(&mut name).expect("Failed to read line"); // 去除输入末尾的换行符 name = name.trim().to_string(); // 打印用户输入 println!("Hello, {}!", name); }
4. 运行项目
在终端中运行以下命令来编译和运行项目:
cargo run
你应该会看到类似以下的输出:
Hello, world! Please enter your name: John Doe Hello, John Doe!
5. 项目结构
Rust 项目的基本结构如下:
hello_world/ ├── Cargo.toml └── src/ └── main.rs
- Cargo.toml: 项目的配置文件,包含依赖项、元数据等。
- src/main.rs: 项目的主文件,包含
main
函数。
6. 添加依赖项
如果你需要添加外部库,可以在 Cargo.toml
文件中指定依赖项。例如,如果你想使用 serde
库进行序列化和反序列化,可以编辑 Cargo.toml
文件:
[dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = "1.0"
然后在 src/main.rs
中使用这些库:
use serde::{Deserialize, Serialize}; use std::io; #[derive(Serialize, Deserialize)] struct Person { name: String, age: u8, } fn main() { println!("Hello, world!"); // 读取用户输入 println!("Please enter your name:"); let mut name = String::new(); io::stdin().read_line(&mut name).expect("Failed to read line"); let name = name.trim().to_string(); println!("Please enter your age:"); let mut age = String::new(); io::stdin().read_line(&mut age).expect("Failed to read line"); let age: u8 = age.trim().parse().expect("Please type a number!"); // 创建 Person 结构体实例 let person = Person { name, age }; // 序列化为 JSON let json = serde_json::to_string(&person).unwrap(); println!("Serialized person: {}", json); // 反序列化为 Person let deserialized_person: Person = serde_json::from_str(&json).unwrap(); println!("Deserialized person: {:?}", deserialized_person); }
7. 构建和运行项目
再次运行 cargo run
命令来构建和运行项目:
cargo run
你应该会看到类似以下的输出:
Hello, world! Please enter your name: John Doe Please enter your age: 30 Serialized person: {"name":"John Doe","age":30} Deserialized person: Person { name: "John Doe", age: 30 }
总结
通过以上步骤,你已经创建了一个简单的 Rust 项目,并学会了如何读取用户输入、使用外部库以及序列化和反序列化数据。Rust 的生态系统非常丰富,你可以继续探索更多的特性和库,以满足你的开发需求。这就需要自己加油了哈。
那我们就进入进阶篇了哈
下面是一个较为复杂和深入的 Rust 项目示例,该项目将结合多个领域的知识,包括异步编程、网络通信、数据库操作、并发处理和图形界面。
我们将创建一个简单的聊天应用程序,支持客户端和服务器之间的实时通信,并使用 SQLite 数据库存储消息历史。
项目概述
- 服务器端:使用 Tokio 进行异步网络编程,处理客户端连接和消息转发。
- 客户端端:使用 GTK-rs 创建图形用户界面,使用 WebSocket 与服务器通信。
- 数据库:使用 SQLite 存储消息历史。
项目结构
chat-app/ ├── server/ │ ├── Cargo.toml │ └── src/ │ └── main.rs ├── client/ │ ├── Cargo.toml │ └── src/ │ ├── main.rs │ └── gui.rs ├── Cargo.toml └── README.md
服务器端
1. 初始化项目
mkdir chat-app cd chat-app cargo new server --bin cd server
2. 添加依赖
编辑 Cargo.toml
文件,添加必要的依赖:
[dependencies] tokio = { version = "1", features = ["full"] } futures = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sqlx = { version = "0.5", features = ["runtime-tokio-native-tls", "sqlite"] }
3. 编写服务器代码
编辑 src/main.rs
文件:
use tokio::net::{TcpListener, TcpStream}; use tokio::sync::broadcast; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use futures::stream::StreamExt; use serde::{Serialize, Deserialize}; use sqlx::sqlite::SqlitePool; use std::collections::HashMap; use std::sync::Arc; use std::time::SystemTime; #[derive(Serialize, Deserialize, Debug)] struct Message { username: String, content: String, timestamp: u64, } #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let listener = TcpListener::bind("127.0.0.1:8080").await?; let pool = SqlitePool::connect("sqlite:chat.db").await?; sqlx::migrate!().run(&pool).await?; let (tx, _rx) = broadcast::channel::<Message>(100); let clients = Arc::new(tokio::sync::Mutex::new(HashMap::new())); loop { let (socket, addr) = listener.accept().await?; let tx = tx.clone(); let clients = Arc::clone(&clients); let pool = pool.clone(); tokio::spawn(async move { handle_client(socket, addr, tx, clients, pool).await; }); } } async fn handle_client( mut socket: TcpStream, addr: std::net::SocketAddr, tx: broadcast::Sender<Message>, clients: Arc<tokio::sync::Mutex<HashMap<std::net::SocketAddr, broadcast::Receiver<Message>>>>, pool: SqlitePool, ) -> Result<(), Box<dyn std::error::Error>> { let mut buf = [0; 1024]; let mut rx = tx.subscribe(); loop { let mut readable = false; let mut writable = false; { let clients = clients.lock().await; if let Some(rx) = clients.get(&addr) { readable = rx.recv().await.is_ok(); } } if readable { let message = rx.recv().await?; socket.write_all(&serde_json::to_vec(&message)?).await?; } if socket.readable().await? { let n = socket.read(&mut buf).await?; if n == 0 { break; } let message: Message = serde_json::from_slice(&buf[..n])?; let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_secs(); let message = Message { username: message.username, content: message.content, timestamp: now, }; sqlx::query!( "INSERT INTO messages (username, content, timestamp) VALUES (?, ?, ?)", message.username, message.content, message.timestamp ) .execute(&pool) .await?; tx.send(message)?; clients.lock().await.insert(addr, rx.resubscribe()); writable = true; } if writable { socket.writable().await?; socket.flush().await?; } } Ok(()) }
客户端端
1. 初始化项目
cd .. cargo new client --bin cd client
2. 添加依赖
编辑 Cargo.toml
文件,添加必要的依赖:
[dependencies] gtk = "0.11" glib = "0.11" gio = "0.11" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "1", features = ["full"] } tokio-tungstenite = "0.13"
3. 编写客户端代码
编辑 src/main.rs
文件:
use gtk::prelude::*; use gio::prelude::*; use tokio::runtime::Runtime; use tokio_tungstenite::tungstenite::protocol::Message as WsMessage; use serde::{Serialize, Deserialize}; use std::sync::mpsc; #[derive(Serialize, Deserialize, Debug)] struct ChatMessage { username: String, content: String, timestamp: u64, } fn main() { if gtk::init().is_err() { println!("Failed to initialize GTK."); return; } let application = gtk::Application::new( Some("com.example.chat-client"), Default::default(), ); application.connect_activate(|app| { build_ui(app); }); application.run(); } fn build_ui(application: >k::Application) { let window = gtk::ApplicationWindow::new(application); window.set_title("Chat Client"); window.set_default_size(400, 600); let vbox = gtk::Box::new(gtk::Orientation::Vertical, 5); let scrolled_window = gtk::ScrolledWindow::new(None, None); let text_view = gtk::TextView::new(); let entry = gtk::Entry::new(); let send_button = gtk::Button::with_label("Send"); scrolled_window.add(&text_view); vbox.pack_start(&scrolled_window, true, true, 0); vbox.pack_start(&entry, false, false, 0); vbox.pack_start(&send_button, false, false, 0); window.add(&vbox); window.show_all(); let (tx, rx) = mpsc::channel(); let text_buffer = text_view.buffer().clone(); send_button.connect_clicked(clone!(@strong entry, @strong tx => move |_| { let message = entry.text().to_string(); entry.set_text(""); tx.send(message).unwrap(); })); let runtime = Runtime::new().unwrap(); runtime.spawn(async move { let ws_stream = tokio_tungstenite::connect_async("ws://127.0.0.1:8080").await.unwrap().0; let (mut ws_write, mut ws_read) = ws_stream.split(); while let Ok(message) = rx.recv() { let message = ChatMessage { username: "User".to_string(), content: message, timestamp: std::time::SystemTime::now() .duration_since(std::time::SystemTime::UNIX_EPOCH) .unwrap() .as_secs(), }; let json_message = serde_json::to_string(&message).unwrap(); ws_write.send(WsMessage::Text(json_message)).await.unwrap(); } while let Some(message) = ws_read.next().await { if let Ok(WsMessage::Text(text)) = message { let message: ChatMessage = serde_json::from_str(&text).unwrap(); let message_text = format!("{}: {}\n", message.username, message.content); glib::idle_add_once(move || { text_buffer.insert_at_cursor(&message_text); }); } } }); }
数据库迁移
在服务器端目录下创建一个 migrations
文件夹,并在其中创建一个迁移文件 V1__create_messages_table.sql
:
CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, content TEXT NOT NULL, timestamp INTEGER NOT NULL );
运行项目
-
启动服务器
cd chat-app/server cargo run
-
启动客户端
cd chat-app/client cargo run
项目总结
通过以上步骤,你已经创建了一个完整的聊天应用程序,其中包括:
- 服务器端:使用 Tokio 进行异步网络编程,处理客户端连接和消息转发。
- 客户端端:使用 GTK-rs 创建图形用户界面,使用 WebSocket 与服务器通信。
- 数据库:使用 SQLite 存储消息历史。
这个项目结合了异步编程、网络通信、图形界面和数据库操作等多个领域的知识,展示了 Rust 在构建复杂应用程序方面的强大能力。
对进阶篇详细讲解(对前面这个不是很理解的,可以看这个,这个代码有注释)当然,我重新写了一遍,方便大家看
让我们一步一步地详细解释这个复杂的 Rust 项目,确保每一个部分都清晰易懂。
项目概述
我们将会创建一个简单的聊天应用程序,分为服务器端和客户端两部分。服务器端负责处理客户端的连接和消息转发,客户端则提供图形用户界面,允许用户发送和接收消息。此外,我们还会使用 SQLite 数据库来存储消息历史。
项目结构
chat-app/ ├── server/ │ ├── Cargo.toml │ └── src/ │ └── main.rs ├── client/ │ ├── Cargo.toml │ └── src/ │ ├── main.rs │ └── gui.rs ├── Cargo.toml └── README.md
服务器端
1. 初始化项目
首先,我们需要初始化服务器端项目:
mkdir chat-app cd chat-app cargo new server --bin cd server
2. 添加依赖
编辑 Cargo.toml
文件,添加必要的依赖:
[dependencies] tokio = { version = "1", features = ["full"] } futures = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sqlx = { version = "0.5", features = ["runtime-tokio-native-tls", "sqlite"] }
3. 编写服务器代码
编辑 src/main.rs
文件,编写服务器逻辑:
use tokio::net::{TcpListener, TcpStream}; use tokio::sync::broadcast; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use futures::stream::StreamExt; use serde::{Serialize, Deserialize}; use sqlx::sqlite::SqlitePool; use std::collections::HashMap; use std::sync::Arc; use std::time::SystemTime; #[derive(Serialize, Deserialize, Debug)] struct Message { username: String, content: String, timestamp: u64, } #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { // 绑定服务器到 IP 地址和端口 let listener = TcpListener::bind("127.0.0.1:8080").await?; // 连接到 SQLite 数据库 let pool = SqlitePool::connect("sqlite:chat.db").await?; // 运行数据库迁移脚本 sqlx::migrate!().run(&pool).await?; // 创建广播通道,用于消息传递 let (tx, _rx) = broadcast::channel::<Message>(100); // 创建客户端列表,用于管理连接的客户端 let clients = Arc::new(tokio::sync::Mutex::new(HashMap::new())); // 监听客户端连接 loop { let (socket, addr) = listener.accept().await?; // 复制广播发送者和客户端列表 let tx = tx.clone(); let clients = Arc::clone(&clients); let pool = pool.clone(); // 为每个客户端创建一个新的任务 tokio::spawn(async move { handle_client(socket, addr, tx, clients, pool).await; }); } } async fn handle_client( mut socket: TcpStream, addr: std::net::SocketAddr, tx: broadcast::Sender<Message>, clients: Arc<tokio::sync::Mutex<HashMap<std::net::SocketAddr, broadcast::Receiver<Message>>>>, pool: SqlitePool, ) -> Result<(), Box<dyn std::error::Error>> { let mut buf = [0; 1024]; let mut rx = tx.subscribe(); loop { let mut readable = false; let mut writable = false; // 检查是否有新的消息需要发送给客户端 { let clients = clients.lock().await; if let Some(rx) = clients.get(&addr) { readable = rx.recv().await.is_ok(); } } if readable { let message = rx.recv().await?; socket.write_all(&serde_json::to_vec(&message)?).await?; } // 检查是否有来自客户端的消息 if socket.readable().await? { let n = socket.read(&mut buf).await?; if n == 0 { break; } let message: Message = serde_json::from_slice(&buf[..n])?; let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_secs(); let message = Message { username: message.username, content: message.content, timestamp: now, }; // 将消息存入数据库 sqlx::query!( "INSERT INTO messages (username, content, timestamp) VALUES (?, ?, ?)", message.username, message.content, message.timestamp ) .execute(&pool) .await?; // 广播消息给所有客户端 tx.send(message)?; clients.lock().await.insert(addr, rx.resubscribe()); writable = true; } // 发送消息到客户端 if writable { socket.writable().await?; socket.flush().await?; } } Ok(()) }
客户端端
1. 初始化项目
初始化客户端项目:
cd .. cargo new client --bin cd client
2. 添加依赖
编辑 Cargo.toml
文件,添加必要的依赖:
[dependencies] gtk = "0.11" glib = "0.11" gio = "0.11" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "1", features = ["full"] } tokio-tungstenite = "0.13"
3. 编写客户端代码
编辑 src/main.rs
文件,编写客户端逻辑:
use gtk::prelude::*; use gio::prelude::*; use tokio::runtime::Runtime; use tokio_tungstenite::tungstenite::protocol::Message as WsMessage; use serde::{Serialize, Deserialize}; use std::sync::mpsc; #[derive(Serialize, Deserialize, Debug)] struct ChatMessage { username: String, content: String, timestamp: u64, } fn main() { if gtk::init().is_err() { println!("Failed to initialize GTK."); return; } let application = gtk::Application::new( Some("com.example.chat-client"), Default::default(), ); application.connect_activate(|app| { build_ui(app); }); application.run(); } fn build_ui(application: >k::Application) { let window = gtk::ApplicationWindow::new(application); window.set_title("Chat Client"); window.set_default_size(400, 600); let vbox = gtk::Box::new(gtk::Orientation::Vertical, 5); let scrolled_window = gtk::ScrolledWindow::new(None, None); let text_view = gtk::TextView::new(); let entry = gtk::Entry::new(); let send_button = gtk::Button::with_label("Send"); scrolled_window.add(&text_view); vbox.pack_start(&scrolled_window, true, true, 0); vbox.pack_start(&entry, false, false, 0); vbox.pack_start(&send_button, false, false, 0); window.add(&vbox); window.show_all(); let (tx, rx) = mpsc::channel(); let text_buffer = text_view.buffer().clone(); send_button.connect_clicked(clone!(@strong entry, @strong tx => move |_| { let message = entry.text().to_string(); entry.set_text(""); tx.send(message).unwrap(); })); let runtime = Runtime::new().unwrap(); runtime.spawn(async move { let ws_stream = tokio_tungstenite::connect_async("ws://127.0.0.1:8080").await.unwrap().0; let (mut ws_write, mut ws_read) = ws_stream.split(); while let Ok(message) = rx.recv() { let message = ChatMessage { username: "User".to_string(), content: message, timestamp: std::time::SystemTime::now() .duration_since(std::time::SystemTime::UNIX_EPOCH) .unwrap() .as_secs(), }; let json_message = serde_json::to_string(&message).unwrap(); ws_write.send(WsMessage::Text(json_message)).await.unwrap(); } while let Some(message) = ws_read.next().await { if let Ok(WsMessage::Text(text)) = message { let message: ChatMessage = serde_json::from_str(&text).unwrap(); let message_text = format!("{}: {}\n", message.username, message.content); glib::idle_add_once(move || { text_buffer.insert_at_cursor(&message_text); }); } } }); }
数据库迁移
在服务器端目录下创建一个 migrations
文件夹,并在其中创建一个迁移文件 V1__create_messages_table.sql
:
CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, content TEXT NOT NULL, timestamp INTEGER NOT NULL );
运行项目
-
启动服务器
cd chat-app/server cargo run
-
启动客户端
cd chat-app/client cargo run
项目总结
通过以上步骤,我们创建了一个完整的聊天应用程序,包括以下几个部分:
-
服务器端:
- 使用 Tokio 进行异步网络编程,处理客户端连接和消息转发。
- 使用 SQLite 数据库存储消息历史。
- 使用广播通道(
broadcast::channel
)管理消息的发送和接收。
-
客户端端:
- 使用 GTK-rs 创建图形用户界面,允许用户输入和显示消息。
- 使用 WebSocket 与服务器通信,发送和接收消息。
这个项目结合了异步编程、网络通信、图形界面和数据库操作等多个领域的知识,展示了 Rust 在构建复杂应用程序方面的强大能力。
标签:顶流,use,进阶,await,serde,let,new,message,Rust From: https://blog.csdn.net/speaking_me/article/details/143980505