Rust基于tokio的websocket群聊

原文:Rust基于tokio的websocket群聊

依赖

[package]
name = "chat"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tokio = { version = "1.23",features = ["full"] }
tokio-tungstenite = "*"
futures = "0.3"
serde={version = "1.0",features = ["derive"]}
serde_json = "1.0"
rand = "0.8.5"
tungstenite = "0.19.0"
futures-util = "0.3.30"

定义消息结构

use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, Debug)]
pub struct Message {
    pub username: String,
    pub message: String,
}
后台程序
mod message;

use crate::message::Message;
use futures::channel::mpsc;
use futures::{SinkExt, StreamExt};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use tokio::net::{TcpListener, TcpStream};
use tungstenite::protocol::Message as WsMessage;

type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

#[tokio::main]
async fn main() -> Result<()> {
    let addr = "127.0.0.1:8080";
    let listener = TcpListener::bind(&addr).await?;
    println!("服务端口 :{}", addr);

    //使用HashMap存储连接的客户端
    let clients: Arc<Mutex<HashMap<usize, mpsc::Sender<WsMessage>>>> =
        Arc::new(Mutex::new(HashMap::new()));
    while let Ok((stream, _)) = listener.accept().await {
        // 为每一个连接生成一个唯一的id
        let client_id = rand::random::<usize>();
        // 创建channels用于在服务器内部发送消息
        let (tx, rx) = mpsc::channel(0);
        clients.lock().unwrap().insert(client_id, tx);

        // 处理新的WebSocket连接
        let clients_clone = clients.clone();
        tokio::spawn(async move {
            if let Err(e) = handle_connection(client_id, stream, clients_clone, rx).await {
                println!("Error handling connection: {}", e);
            }
        });
    }

    Ok(())
}

async fn handle_connection(
    client_id: usize,
    stream: TcpStream,
    clients: Arc<Mutex<HashMap<usize, mpsc::Sender<WsMessage>>>>,
    mut rx: mpsc::Receiver<WsMessage>,
) -> Result<()> {
    let addr = stream.peer_addr()?;
    println!("新连接 :{}(ID:{})", addr, client_id);

    let ws_stream = tokio_tungstenite::accept_async(stream).await?;
    println!("WebSocket handshake has been successfully completed");

    //处理来自客户端的消息
    let (mut write, mut read) = ws_stream.split();

    loop {
        tokio::select! {
            msg = read.next()=>{
                match msg {
                    Some(Ok(message)) => {
                     if message.is_text(){
                            let message = message.to_text()?;
                            let message:Message = serde_json::from_str(message)?;
                            let broadcast_message = format!("{}:{}",message.username,message.message);
                            broadcast_message_to_all_clients(&clients,&broadcast_message).await?;
                        }
                    },
                    _ => {}
                }
            }
            // 处理来自服务器内部的消息
            msg = rx.next() =>{
                match msg{
                    Some(msg)=>{
                        if let Err(e) = write.send(msg).await{
                            println!("Error sending message: {}",e);
                            break;
                        }
                    }
                    None=>{
                        println!("Channel closed");
                        break;
                    }
                }
            }
        }
    }

    // 从客户端列表移除断开的客户端
    clients.lock().unwrap().remove(&client_id);
    Ok(())
}

/// 将消息广播给所有连接的客户端
async fn broadcast_message_to_all_clients(
    clients: &Arc<Mutex<HashMap<usize, mpsc::Sender<WsMessage>>>>,
    message: &str,
) -> Result<()> {
    let mut clients = clients.lock().unwrap();
    for (_client_id, tx) in clients.iter_mut() {
        let msg = WsMessage::Text(message.to_string());
        println!("{}", msg);
        tx.start_send(msg).unwrap()
    }
    Ok(())
}

客户端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Title</title>
</head>
<body>
    <h1>Rust chat App</h1>
    <input type="text" id="username" placeholder="username">
    <input type="text" id="message" placeholder="Enter your message">
    <button onclick="sendMessage()">发送</button>
    <ul id="messages"></ul>
    <script>
        let websocket = new WebSocket('ws://localhost:8080')

        websocket.onopen = () =>{
            console.log("连接服务器")
        }

        websocket.onmessage = (event) =>{
            let messages = document.getElementById('messages')
            let li = document.createElement('li')
            li.textContent = event.data
            messages.appendChild(li)
        }

        function sendMessage(){
            let username = document.getElementById('username').value
            let message = document.getElementById('message').value
            let data = {
                username:username,
                message:message
            }
            websocket.send(JSON.stringify(data))
            document.getElementById('message').value = ''
        }
    </script>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值