當前位置: 妍妍網 > 碼農

使用 WebSocket、Rust 和 React 打造即時聊天套用

2024-09-27碼農

本文將帶你一步步構建一個即時聊天套用,使用 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 不可用時的場景。

    文章精選