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 流式通信的区别?
答案:
| 维度 | WebSocket | gRPC 流 |
|---|---|---|
| 协议 | WebSocket 协议 | HTTP/2 |
| 序列化 | 自定义(通常 JSON) | Protobuf |
| 类型安全 | 手动 | Proto 定义 |
| 浏览器 | 原生支持 | 需 grpc-web |
| 适用场景 | 聊天、实时推送 | 服务间流式数据 |