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

本文中,我们将使用 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

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

步骤 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 聊天

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

不错。我们可以在两个不同的聊天窗口之间进行通信。

以上,该文介绍了如何使用 Rust 设置 WebSocket 并制作简单的 React 前端。它演示了如何使用 Tokio 来维护活动连接。一个潜在的改进方法是在 React 前端添加验证,以管理 WebSocket 不可用的情况。

作者:Sangho Bose

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/im/52538.html

(0)

相关推荐

发表回复

登录后才能评论