本文將帶你一步步構建一個即時聊天套用,使用 WebSocket 進行即時通訊, Rust 構建後端伺服器, React 構建前端界面。WebSocket 提供了一種高效的雙向通訊通道,非常適合即時互動套用,比如聊天套用。
為什麽選擇 WebSocket?
WebSocket 是即時套用的理想選擇,比如聊天服務、線上遊戲、金融交易平台等等,這些套用都需要即時的數據交換。與依賴請求-響應迴圈的 REST API 不同,WebSocket 提供 全雙工 通訊,這意味著伺服器和客戶端可以在任何時候發送訊息。
使用 REST API 時,我們通常需要依靠 輪詢 ,即客戶端頻繁發送請求以檢查更新。這會導致網路使用效率低下,並造成不必要的延遲。另一方面,WebSocket 保持一個開放的連線,允許 即時數據交換 ,無需不斷發送請求,從而最佳化效能和響應能力。
第一部份:在 Rust 中設定 WebSocket
讓我們從建立一個簡單的 Rust WebSocket 回聲伺服器開始。我們將使用
tokio-tungstenite
,一個用於 Tokio 執行時的流行 WebSocket 庫。
依賴項
首先,我們將把必要的依賴項添加到我們的
Cargo.toml
檔中:
[dependencies]
tokio = { version = "1", features = ["full"] }
tungstenite = "0.17"
tokio-tungstenite = "0.17"
futures-util = "0.3"
步驟 1:構建回聲伺服器
現在讓我們深入程式碼,構建一個回聲伺服器,它將簡單地返回它接收到的任何訊息:
use futures_util::{StreamExt, SinkExt};
use tokio::net::TcpListener;
use tokio_tungstenite::tungstenite::Message;
use tokio_tungstenite::accept_async;
#[tokio::main]
asyncfnmain() {
let addr = "127.0.0.1:8080";
let listener = TcpListener::bind(&addr).await.expect("Failed to bind");
println!("WebSocket server running at {}", addr);
whileletOk((stream, _)) = listener.accept().await {
tokio::spawn(asyncmove {
let ws_stream = accept_async(stream)
.await
.expect("Error during the WebSocket handshake");
let (mut write,mut read) = ws_stream.split();
whileletSome(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
(發送)部份,用於異步通訊。
回聲訊息
: 伺服器將接收到的任何文本訊息回送,並在前面加上
>
。
測試回聲伺服器
為了測試伺服器,我們可以使用 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]
asyncfnmain() {
let addr = "127.0.0.1:8080";
let listener = TcpListener::bind(&addr).await.expect("Failed to bind");
let (tx, rx) = broadcast::channel::(10);
println!("WebSocket server running at {}", addr);
whileletOk((stream, _)) = listener.accept().await {
let tx = tx.clone();
letmut rx = rx.resubscribe();
tokio::spawn(asyncmove {
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() => {
ifletOk(received_message) = received_message {
write.send(Message::Text(received_message)).await.unwrap();
}
}
read_message = read.next() => {
ifletSome(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?
現在,來自一個終端的訊息被廣播到所有其他連線的客戶端,為多使用者聊天應用程式奠定了基礎。
第二部份:React 前端
我們將分兩步構建我們的前端。首先,我們將構建一個簡單的 React UI,然後添加 WebSocket 程式碼。
步驟 1:構建一個簡單的 UI
在這一步中,我們將只關註使用者介面 (UI)。我們將建立一個基本的聊天布局,包含使用者名稱和訊息的輸入框,以及一個顯示聊天訊息的區域。
import React, { useState } from'react';
functionApp() {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [username, setUsername] = useState('');
const handleSendMessage = () => {
const message = `${username}: ${input}`;
setMessages((prevMessages) => [...prevMessages, message]);
setInput('');
};
return (
Simple Chat UI
setUsername(e.target.value)}
style={{ marginBottom: '10px' }}
/>
setInput(e.target.value)}
style={{ width: '70%', marginRight: '10px' }}
/>
Messages
{messages.map((msg, index) => (
{msg}
))}
);
}
exportdefault App;
程式碼解釋
這段程式碼只是建立了一個基本的表單,使用者可以在其中輸入使用者名稱和訊息。
messages
狀態將保存訊息,並在點選「發送」按鈕時將它們顯示在螢幕上。
測試簡單的聊天 UI
步驟 2:添加 WebSocket
接下來,我們將連線 WebSocket 到聊天 UI,並讓它透過 WebSocket 發送和接收訊息。我們將使用庫:
react-use-websocket
import React, { useState } from'react';
import useWebSocket from'react-use-websocket';
functionApp() {
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 (
WebSocket Chat
setUsername(e.target.value)}
style={{ marginBottom: '10px' }}
/>
setInput(e.target.value)}
style={{ width: '70%', marginRight: '10px' }}
/>
Messages
{messages.map((msg, index) => (
{msg}
))}
);
}
exportdefault App;
程式碼解釋
我們添加了使用
useWebSocket
勾點的 WebSocket 整合。當收到新訊息時,它會被添加到
messages
陣列中,當你發送訊息時,它會透過 WebSocket 傳輸。
測試 WebSocket 聊天
不錯。我們可以在兩個不同的聊天視窗之間進行通訊。
我們介紹了如何使用 Rust 設定 WebSocket,以及如何制作一個簡單的 React 前端。它演示了使用 Tokio 來維護活動連線。一個潛在的改進是向 React 前端添加驗證,以管理 WebSocket 不可用時的場景。
文章精選