聊天室实战 #
项目结构 #
text
chat-app/
├── Cargo.toml
├── src/
│ └── main.rs
└── static/
└── index.html
服务端实现 #
rust
use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder};
use actix_ws::{Message, Session};
use std::collections::HashSet;
use std::sync::{Arc, Mutex};
use tokio::sync::broadcast;
struct ChatState {
tx: broadcast::Sender<String>,
users: Mutex<HashSet<String>>,
}
async fn ws_chat(
req: HttpRequest,
body: web::Payload,
state: web::Data<Arc<ChatState>>,
) -> Result<HttpResponse, Error> {
let (response, mut session, mut msg_stream) = actix_ws::handle(&req, body)?;
let tx = state.tx.clone();
let mut rx = tx.subscribe();
actix_web::rt::spawn(async move {
loop {
tokio::select! {
Some(msg_result) = msg_stream.next() => {
match msg_result {
Ok(Message::Text(text)) => {
let _ = tx.send(text.to_string());
}
Ok(Message::Close(_)) => {
session.close(None).await.unwrap();
return;
}
_ => {}
}
}
Ok(msg) = rx.recv() => {
session.text(msg).await.unwrap();
}
}
}
});
Ok(response)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let (tx, _) = broadcast::channel(100);
let state = Arc::new(ChatState {
tx,
users: Mutex::new(HashSet::new()),
});
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(state.clone()))
.route("/ws", web::get().to(ws_chat))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
客户端实现 #
html
<!DOCTYPE html>
<html>
<head>
<title>Chat Room</title>
</head>
<body>
<div id="messages"></div>
<input type="text" id="input" />
<button onclick="send()">Send</button>
<script>
const ws = new WebSocket('ws://localhost:8080/ws');
const messages = document.getElementById('messages');
ws.onmessage = (event) => {
const div = document.createElement('div');
div.textContent = event.data;
messages.appendChild(div);
};
function send() {
const input = document.getElementById('input');
ws.send(input.value);
input.value = '';
}
</script>
</body>
</html>
下一步 #
继续学习 测试,了解应用测试!
最后更新:2026-03-29