跳到主要内容

WebSocket

问题

Rust 中如何实现 WebSocket 通信?

答案

最常用的是 tokio-tungstenite(异步)和 tungstenite(同步)。

服务端(配合 axum)

use axum::{
extract::ws::{Message, WebSocket, WebSocketUpgrade},
response::IntoResponse,
routing::get,
Router,
};
use futures::{StreamExt, SinkExt};

async fn ws_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
ws.on_upgrade(handle_socket)
}

async fn handle_socket(mut socket: WebSocket) {
// 接收消息
while let Some(Ok(msg)) = socket.next().await {
match msg {
Message::Text(text) => {
println!("收到: {}", text);
// Echo 回去
if socket.send(Message::Text(format!("Echo: {}", text))).await.is_err() {
break;
}
}
Message::Close(_) => break,
_ => {}
}
}
println!("WebSocket 连接关闭");
}

#[tokio::main]
async fn main() {
let app = Router::new().route("/ws", get(ws_handler));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}

客户端

use tokio_tungstenite::connect_async;
use futures::{StreamExt, SinkExt};
use tungstenite::Message;

async fn ws_client() -> Result<(), Box<dyn std::error::Error>> {
let (mut ws_stream, _) = connect_async("ws://127.0.0.1:3000/ws").await?;

// 发送消息
ws_stream.send(Message::Text("Hello".into())).await?;

// 接收消息
if let Some(Ok(msg)) = ws_stream.next().await {
println!("收到: {}", msg);
}

Ok(())
}

心跳与重连

use tokio::time::{interval, Duration};

async fn handle_socket_with_heartbeat(mut socket: WebSocket) {
let mut heartbeat = interval(Duration::from_secs(30));

loop {
tokio::select! {
Some(Ok(msg)) = socket.next() => {
match msg {
Message::Text(text) => { /* 处理消息 */ }
Message::Ping(data) => {
let _ = socket.send(Message::Pong(data)).await;
}
Message::Close(_) => break,
_ => {}
}
}
_ = heartbeat.tick() => {
if socket.send(Message::Ping(vec![])).await.is_err() {
break; // 连接断开
}
}
}
}
}

常见面试问题

Q1: WebSocket 和 gRPC 流式通信的区别?

答案

维度WebSocketgRPC 流
协议WebSocket 协议HTTP/2
序列化自定义(通常 JSON)Protobuf
类型安全手动Proto 定义
浏览器原生支持需 grpc-web
适用场景聊天、实时推送服务间流式数据

相关链接