当前位置: 欣欣网 > 码农

使用 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 不可用时的场景。

    文章精选