本文中,我们将使用 WebSockets 构建一个实时聊天应用程序,由 Rust 处理后台,React 驱动前端。WebSockets 可在客户端和服务器之间提供高效的双向通信通道,是聊天应用等实时交互的理想选择。
为什么选择 WebSockets?
WebSockets 非常适合聊天服务、在线游戏和金融交易仪表盘等实时应用,在这些应用中,即时数据交换至关重要。与依赖请求-响应周期的 REST API 不同,WebSockets 提供全双工通信,这意味着服务器和客户端都可以随时发送信息。
使用 REST API 时,我们经常需要依赖轮询,客户端会频繁发送请求来检查更新。这可能会导致网络使用效率低下和不必要的延迟。另一方面,WebSockets 保持开放连接,允许实时数据交换,而无需不断发出请求,从而优化性能和响应能力。
第 1 部分:在 Rust 中设置 WebSockets
让我们先用 Rust 创建一个基本的 WebSocket echo 服务器。我们将使用 tokio-tungstenite,它是 Tokio 运行时的常用 WebSocket 库。
依赖项
首先,我们将在 Cargo.toml 文件中添加必要的依赖项:
[dependencies]
tokio = { version = "1", features = ["full"] }
tungstenite = "0.17"
tokio-tungstenite = "0.17"
futures-util = "0.3"
步骤 1:构建 Echo 服务器
现在让我们深入代码,构建 Echo 服务器,它将简单地返回收到的任何消息:
use futures_util::{StreamExt, SinkExt};
use tokio::net::TcpListener;
use tokio_tungstenite::tungstenite::Message;
use tokio_tungstenite::accept_async;
#[tokio::main]
async fn main() {
let addr = "127.0.0.1:8080";
let listener = TcpListener::bind(&addr).await.expect("Failed to bind");
println!("WebSocket server running at {}", addr);
while let Ok((stream, _)) = listener.accept().await {
tokio::spawn(async move {
let ws_stream = accept_async(stream)
.await
.expect("Error during the WebSocket handshake");
let (mut write,mut read) = ws_stream.split();
while let Some(message) = read.next().await {
match message {
Ok(Message::Text(text)) => {
write.send(Message::Text(format!(" {}",text))).await.unwrap();
}
Ok(_) => {
println!("Received a non-text message");
}
Err(e) => {
println!("Error: {}", e);
}
}
};
});
}
}
代码说明
- Tokio:我们使用 Tokio 的异步运行时和
#[tokio::main]
宏来驱动我们的异步代码。 - TcpListener:监听指定地址上的传入 TCP 连接。
- WebSocket 握手:一旦客户端连接,我们就用
accept_async
来升级到 WebSocket 的连接。 - 流分割:我们将 WebSocket 流分割为
read
(接收)和write
(发送)部分,以进行异步通信。 - 回显消息:服务器会回显其收到的任何文本消息,并以
>
为前缀。
测试 Echo 服务器
为了测试服务器,我们可以使用 WebSocket 客户端,例如wscat
:
% wscat -c ws://127.0.0.1:8080
Connected (press CTRL+C to quit)
> hello
< >hello
> Hey dude
< >Hey dude
到此为止,我们已经构建了一个基本的回声服务器。接下来通过添加广播功能来进一步完善它。
步骤 2:添加广播功能
在聊天应用程序中,我们希望将一个用户的消息广播给所有连接的用户,而不仅仅是回传给发送者。让我们修改代码,使用 Tokio 的广播频道实现这一行为。
use futures_util::{StreamExt, SinkExt};
use tokio::net::TcpListener;
use tokio_tungstenite::tungstenite::Message;
use tokio_tungstenite::accept_async;
use tokio::sync::broadcast;
#[tokio::main]
async fn main() {
let addr = "127.0.0.1:8080";
let listener = TcpListener::bind(&addr).await.expect("Failed to bind");
let (tx, rx) = broadcast::channel::<String>(10);
println!("WebSocket server running at {}", addr);
while let Ok((stream, _)) = listener.accept().await {
let tx = tx.clone();
let mut rx = rx.resubscribe();
tokio::spawn(async move {
let ws_stream = accept_async(stream)
.await
.expect("Error during the WebSocket handshake");
let (mut write,mut read) = ws_stream.split();
loop {
tokio::select! {
received_message = rx.recv() => {
if let Ok(received_message) = received_message {
write.send(Message::Text(received_message)).await.unwrap();
}
}
read_message = read.next() => {
if let Some(Ok(Message::Text(text))) = read_message {
tx.send(text).unwrap();
}
}
}
}
});
}
}
代码说明
- 广播频道:我们使用 Tokio 的
broadcast
频道向所有连接的客户端发送消息。每个新连接都会克隆tx
,客户端会使用tx.subscribe()
来监听消息。 - tokio::select!:这个强大的宏允许我们同时等待多个异步事件。在我们的例子中,要么等待来自 WebSocket 的新消息,要么等待来自广播频道的新消息。
测试广播功能
打开多个终端来查看广播的实际工作方式:
终端 1:
% wscat -c ws://127.0.0.1:8080
Connected (press CTRL+C to quit)
< Hey man
< how is it going
> fine
< fine
> how are you mate?
< how are you mate?
>
终端 2:
% wscat -c ws://127.0.0.1:8080
Connected (press CTRL+C to quit)
> Hey man
< Hey man
> how is it going
< how is it going
< fine
< how are you mate?
现在,来自一个终端的消息会广播到所有其他已连接的客户端,为多用户聊天应用奠定了基础。
第 2 部分:React 前端
我们将分两步构建前端。首先将构建一个简单的 React UI,然后添加 WebSocket 代码。
步骤 1:构建简单的用户界面
在这一步中,我们将只关注用户界面(UI)。我们将创建一个基本的聊天布局,其中包含用户名和消息的输入以及聊天消息的显示区域。
import React, { useState } from 'react';
function App() {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [username, setUsername] = useState('');
const handleSendMessage = () => {
const message = `${username}: ${input}`;
setMessages((prevMessages) => [...prevMessages, message]);
setInput('');
};
return (
<div style={{ padding: '20px' }}>
<h2>Simple Chat UI</h2>
<input
type="text"
placeholder="Enter your username"
value={username}
onChange={(e) => setUsername(e.target.value)}
style={{ marginBottom: '10px' }}
/>
<div>
<input
type="text"
placeholder="Type a message..."
value={input}
onChange={(e) => setInput(e.target.value)}
style={{ width: '70%', marginRight: '10px' }}
/>
<button onClick={handleSendMessage}>
Send
</button>
</div>
<div
style={{
marginTop: '20px',
border: '1px solid black',
padding: '10px',
height: '300px',
overflowY: 'scroll',
}}
>
<h3>Messages</h3>
{messages.map((msg, index) => (
<div key={index}>{msg}</div>
))}
</div>
</div>
);
}
export default App;
代码说明:
此代码只是创建了一个基本表单,用户可以在其中输入用户名和消息。messages
状态将保存消息,并在单击“发送”按钮时将其显示在屏幕上。
测试简单聊天 UI
步骤 2:添加 WebSocket
接下来,我们将把 WebSocket 连接到聊天用户界面,让它通过 WebSocket 发送和接收消息。
import React, { useState } from 'react';
import useWebSocket from 'react-use-websocket';
function App() {
const [socketUrl] = useState('ws://127.0.0.1:8080');
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [username, setUsername] = useState('');
const { sendMessage, lastMessage } = useWebSocket(socketUrl);
React.useEffect(() => {
if (lastMessage !== null) {
setMessages((prevMessages) => [...prevMessages, lastMessage.data]);
}
}, [lastMessage]);
const handleSendMessage = () => {
if (input && username) {
const message = `${username}: ${input}`;
sendMessage(message);
setInput('');
}
};
return (
<div style={{ padding: '20px' }}>
<h2>WebSocket Chat</h2>
<input
type="text"
placeholder="Enter your username"
value={username}
onChange={(e) => setUsername(e.target.value)}
style={{ marginBottom: '10px' }}
/>
<div>
<input
type="text"
placeholder="Type a message..."
value={input}
onChange={(e) => setInput(e.target.value)}
style={{ width: '70%', marginRight: '10px' }}
/>
<button onClick={handleSendMessage}>
Send
</button>
</div>
<div
style={{
marginTop: '20px',
border: '1px solid black',
padding: '10px',
height: '300px',
overflowY: 'scroll',
}}
>
<h3>Messages</h3>
{messages.map((msg, index) => (
<div key={index}>{msg}</div>
))}
</div>
</div>
);
}
export default App;
代码说明:
我们使用 useWebSocket
钩子添加了 WebSocket 集成。当收到新消息时,它会被添加到messages
数组中,而当你发送消息时,它会通过 WebSocket 发送。
测试 WebSocket 聊天
不错。我们可以在两个不同的聊天窗口之间进行通信。
以上,该文介绍了如何使用 Rust 设置 WebSocket 并制作简单的 React 前端。它演示了如何使用 Tokio 来维护活动连接。一个潜在的改进方法是在 React 前端添加验证,以管理 WebSocket 不可用的情况。
作者:Sangho Bose
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/im/52538.html