使用 Rust 和 React 构建实时聊天应用程序

本文介绍如何使用Rust后端和React前端构建一个实时聊天应用,利用WebSockets实现双向通信。首先介绍了实时聊天的重要性,然后详细讲解了从设计应用架构、在Rust中构建WebSocket服务器到使用React创建客户端UI的步骤。涵盖了房间和用户管理、数据库操作、WebSocket通信以及React组件和Hook的使用。最后,展示了如何在React中实现聊天界面和交互功能。
摘要由CSDN通过智能技术生成

如果您希望构建既快速又可靠的实时聊天应用程序,请考虑使用 Rust 和 React。Rust 以其速度和可靠性着称,而React 是最流行的用于构建用户界面的前端框架之一。

在本文中,我们将演示如何使用 Rust 和 React 构建一个实时聊天应用程序,该应用程序提供聊天、检查用户状态和指示用户何时输入的功能。我们将使用 WebSockets 启用双向客户端-服务器通信。

跳跃前进:

  • 实时聊天应用简介

  • WebSocket 简介

  • 入门

  • 设计实时聊天应用架构

  • 在 Rust 中构建 WebSocket 服务器创建路线处理用户会话

  • 使用 SQLite 准备数据库生成模式创建结构设置查询通过电话号码查找用户添加新用户查找聊天室和参与者

  • 使用 React 构建客户端 UI头像组件登录组件房间组件会话组件使用Websocket Hook使用LocalStorage Hook使用会话挂钩

  • 构建聊天应用程序

实时聊天应用简介

实时聊天应用程序允许用户通过文本、语音或视频彼此实时交流。这种类型的应用程序允许比其他类型的通信(例如电子邮件或 IM)更即时的消息传递。

聊天应用程序必须实时工作有几个原因:

  • 改进的性能:更直接的通信允许更自然的对话

  • 更强的响应能力:实时功能可改善用户体验

  • 卓越的可靠性:通过实时功能,消息丢失或延迟的机会更少

WebSocket 简介

WebSockets 在实时聊天应用程序中启用客户端和服务器之间的双向通信。使用 Rust 构建 WebSocket 服务器将使服务器能够处理大量连接而不会降低速度。这是由于 Rust 的速度和可靠性。

现在我们对 WebSockets 有了更好的了解,让我们开始构建我们的实时聊天应用程序吧!

入门

首先,让我们回顾一些先决条件:

  • Rust:确保您的计算机上安装了 Rust。如果没有,请使用以下命令安装它:卷曲-原型'=https' - tlsv1 。2 - sSf https : //sh.rustup.rs | sh // 如果你在 Windows 中,请在此处查看更多安装方法 https : //forge.rust-lang.org/infra/other-installation-methods.html

  • React:确保您的环境已准备好进行 React 开发;如果您还没有安装 React,请使用以下命令之一来安装它:// 在苹果机上 酿造安装节点 // 在 linux nvm install v14上。10 .0 // 在 Windows 上,您可以在此处下载 nodejs 安装程序 https : //nodejs.org/en/download/

接下来,运行以下命令来验证所有内容是否已安装并正常工作:

rustc——版本_

货物——版本

节点——版本

npm——版本_

设计实时聊天应用架构

让我们为我们的实时聊天应用程序创建一些设计架构。我们将构建一个简单的服务器;我们的应用程序架构将涵盖以下功能:

  • 聊天:两个用户之间通过直接消息传递

  • 打字指示器:当用户开始向他们输入聊天内容时通知收件人

  • 用户状态:表示用户在线还是离线

实时聊天应用系统架构。

此架构非常简单且易于遵循。它仅由几个组件组成:

  • WebSocket 服务器:这是我们应用程序中最重要的组件;它处理客户和房间之间的所有通信

  • 房间管理器:该组件负责管理我们应用程序中的所有房间。它将创建、更新和删除房间。该组件将位于 HTTP 服务器上

  • 用户管理器:该组件负责管理我们应用程序中的所有用户。它将创建、更新和删除用户。该组件也将位于 HTTP 服务器上

  • 消息管理器:该组件负责管理我们应用程序中的所有消息。它将创建、更新和删除消息。这个组件一将在 WebSocket 服务器和 HTTP 服务器上。它将用于存储来自 WebSockets 的传入消息,并在用户通过 Rest API 打开聊天室时检索数据库中已有的所有消息

在 Rust 中构建 WebSocket 服务器

我们可以使用许多包在 Rust 中编写 WebSocket 服务器。对于本教程,我们将使用Actix Web;它是一个成熟的软件包并且易于使用。

首先,使用以下命令创建一个 Rust 项目:

cargo new rust -反应-聊天

接下来,将这个包添加到文件中:Cargo.toml

[ package ]

name = "rust-react-chat"

version = "0.1.0"

edition = "2021"

[依赖项]

actix = “0.13.0”

actix - files = “0.6.2”

actix - web = “4.2.1”

actix - web - actors = “4.1.0”

rand = “0.8.5”

serde = “1.0 .147"

serde_json = "1.0.88"

现在,安装diesel_cli;我们将使用它作为我们的 ORM:

cargo install diesel_cli -- no - default - features -- features sqlite

项目的结构应该如下所示:

. ├──货物。lock

├── Cargo . toml

├──自述文件。md

├──聊天。分贝

├── . 环境

└──源

├──数据库. rs

├──主要。rs

├──模型. rs

├──路线. rs

├──架构. rs

├──服务器. rs

└──会话. rs

└──

静态└──用户界面

现在,这里有一些关于文件夹的信息:

  • src:此文件夹包含我们所有的 Rust 代码

  • static:此文件夹包含我们所有的静态资产、HTML 文件、JavaScript 文件和图像

  • ui:这个文件夹包含我们的 React 代码;我们稍后将其编译为static文件并将其导出到static文件夹

接下来,让我们编写 WebSocket 服务器的入口点:

// src/main.rs #[ macro_use ] extern crate diesel ;

使用 actix ::*;

使用 actix_cors :: Cors ;

使用 actix_files ::文件;

使用 actix_web ::{ web , http , App , HttpServer };

使用 diesel ::{ prelude ::*, r2d2 ::{ self , ConnectionManager }, };

模组数据库;

模组模型;

模组路线;

模式架构;

模组服务器;

模组会话;#[ actix_web :: main ] async fn main () -> std :: io :: Result <()> { let server = server :: ChatServer :: new (). 开始();让conn_spec = "chat.db" ; 让manager = ConnectionManager :: < SqliteConnection > :: new ( conn_spec );

let pool = r2d2::Pool::builder().build(manager).expect("Failed to create pool.");

let server_addr = "127.0.0.1";

let server_port = 8080;

let app = HttpServer::new(move || {

let cors = Cors::default()

.allowed_origin("http://localhost:3000")

.allowed_origin("http://localhost:8080")

.allowed_methods(vec!["GET", "POST"])

.allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])

.allowed_header(http::header::CONTENT_TYPE)

.max_age(3600);

App::new()

.app_data(web::Data::new(server.clone()))

.app_data(web::Data::new(pool.clone()))

.wrap(cors)

.service(web::resource("/").to(routes::index))

.route("/ws", web::get().to(routes::chat_server))

.service(routes::create_user)

.service(routes::get_user_by_id)

.service(routes::get_user_by_phone)

.service(routes::get_conversation_by_id)

.service(routes::get_rooms)

.service(Files::new( "/" , "./static" )) }) 。工人( 2 ) 。绑定((服务器地址,服务器端口))?. 运行();

println !( "服务器运行在 http://{server_addr}:{server_port}/" );

应用程序。等待}

以下是有关我们正在使用的软件包的一些信息:

  • actix_cors: 将用于调试 UI;我们将接受来自或的POST 和 GET 请求localhost:3000localhost:8080

  • actix_web:对于 Actix Web 包中所有与 HTTP 相关的功能

  • actix_files:用于将静态文件嵌入到我们的路由之一

  • diesel:将用于从我们的 SQLite 数据库中查询数据。如果您愿意,可以将其更改为 Postgres 或 MySQL

  • serde_json:将用于解析我们将发送到 React 应用程序的 JSON 数据

创建路线

现在让我们为我们的服务器创建路由。由于我们将使用 REST HTTP 和 WebSocket 服务器,我们可以轻松地将所有内容放在一个文件中。

首先,添加我们需要的所有包:

// src/routes.rs

使用 std :: time :: Instant ;

使用 actix ::*;

使用 actix_files :: NamedFile ;

使用 actix_web ::{ get , post , web , Error , HttpRequest , HttpResponse , Responder };

使用 actix_web_actors :: ws ;

使用 diesel ::{ prelude ::*, r2d2 ::{ self , ConnectionManager }, };

使用 serde_json ::

JSON ;

使用 uuid :: Uuid ;

使用 crate :: db ;

使用板条箱::模型;

使用箱子::服务器;

使用 crate :: session ;

输入DbPool = r2d2 :: Pool < ConnectionManager < SqliteConnection >>;

然后,添加一个用于将主页嵌入到根 URL 的路由:

// src/routes.rs

pub async fn index () -> impl Responder { NamedFile :: open_async ( "./static/index.html" ). 等待。展开()}

这是我们的 WebSocket 服务器的入口点。现在它在路线上,但您可以将其更改为您喜欢的任何路线名称。由于我们已经在文件中注册了我们需要的所有依赖项,我们可以将依赖项传递给函数参数,如下所示:/wsmain.rs

// src/routes.rs

pub async fn chat_server (

req : HttpRequest , stream : web :: Payload , pool : web :: Data < DbPool >, srv : web :: Data < Addr < server :: ChatServer >>, ) ->结果< HttpResponse ,错误> { ws ::开始(

会话::

WsChatSession { id : 0 , hb : Instant :: now (), room : "main" . to_string (),名称:无,地址:srv 。get_ref ()。克隆(), db_pool :池, }, & req ,

溪流

) }

接下来,我们需要向我们的路由添加一个 REST API,以便获取必要的数据来使我们的聊天正常进行:

// src/routes.rs #[ post ( "/users/create" )]

pub async fn create_user (

pool : web :: Data < DbPool >, form : web :: Json < models :: NewUser >, ) ->结果< HttpResponse ,错误> { let user = web :: block ( move || { let mut conn =

游泳池。得到()?;

db :: insert_new_user (& mut conn , & form . username , & form . phone ) }) 。等待?. map_err ( actix_web :: error :: ErrorUnprocessableEntity )?; 好的( HttpResponse :: Ok () .json ( user )) } #[ get ( "/users/{user_id}" )]

pub

async fn get_user_by_id (

pool : web :: Data < DbPool >, id : web :: Path < Uuid >, ) ->结果< HttpResponse , Error > {让user_id = id 。to_owned (); let user = web :: block (移动|| { let mut conn = pool . get

()?;

db::find_user_by_uid(&mut conn, user_id)

})

.await?

.map_err(actix_web::error::ErrorInternalServerError)?;

if let Some(user) = user {

Ok(HttpResponse::Ok().json(user))

} else {

let res = HttpResponse::NotFound().body(

json!({

"error": 404,

"message": format!("No user found with phone: {id}")

})

.to_string(),

);

Ok(res)

}

}

#[get("/conversations/{uid}")]

pub async fn get_conversation_by_id(

pool: web::Data<DbPool>,

uid: web::Path<Uuid>,

) -> Result<HttpResponse, Error> {

let room_id = uid.to_owned();

let conversations = web::block(move || {

let mut conn = pool.get()?;

db::get_conversation_by_room_uid(&mut conn, room_id)

})

.await?

.map_err(actix_web::error::ErrorInternalServerError)?;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pxr007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值