WebSockets
Build WebSocket clients and standalone servers with tokio-tungstenite when you are not using Axum's built-in WebSocket support.
Busca en todas las páginas de la documentación
Build WebSocket clients and standalone servers with tokio-tungstenite when you are not using Axum's built-in WebSocket support.
Quick-reference recipe card - copy-paste ready.
use tokio_tungstenite::connect_async;
use futures_util::{SinkExt, StreamExt};
use tungstenite::Message;
let (ws, _) = connect_async("wss://echo.websocket.events").await?;
let (mut write, mut read) = ws.split();
write.send(Message::Text("hello".into())).await?;
if let Some(Ok(Message::Text(reply))) = read.next().await {
println!("{reply}");
}When to reach for this: Standalone WebSocket clients, custom servers, or protocols outside Axum.
use futures_util::{SinkExt, StreamExt};
use tokio::net::{TcpListener, TcpStream};
use tokio_tungstenite::accept_async;
use tungstenite::Message;
async fn handle_connection(stream: TcpStream) -> Result<(), Box<dyn std::error::Error>> {
let ws = accept_async(stream).await?;
let (mut write, mut read) = ws.split();
while let Some(msg) = read.next().await {
match msg? {
Message::Text(t) => write.send(Message::Text(format!("echo: {t}").into())).await?,
Message::Close(_) => break,
_ => {}
}
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:9002").await?;
while let Ok((stream, _)) = listener.accept().await {
tokio::spawn(handle_connection(stream));
}
Ok(())
}What this demonstrates:
accept_async completes server-side handshake on TCP stream.Text and Close messages explicitly.connect_async symmetrically.tokio-tungstenite integrates with Tokio async IO.| Layer | Tool |
|---|---|
| Axum route | WebSocketUpgrade extractor |
| Raw TCP server | tokio-tungstenite::accept_async |
| Client | connect_async |
// Ping keepalive
write.send(Message::Ping(vec![].into())).await?;tokio-rustls for WSS. Fix: TLS terminator or rustls handshake before accept.max_message_size / max_frame_size.Origin header during handshake.| Alternative | Use When | Don't Use When |
|---|---|---|
| Axum WebSocket | Already on Axum | Non-HTTP bootstrap |
| SSE | Server-to-client only | Bidirectional chat |
| WebTransport | Modern browsers, QUIC | Broad legacy support needed |
connect_async to wss:// with rustls-enabled connector feature.
Message::Binary for protobuf or compressed payloads.
Query token at upgrade or cookie on initial HTTP request.
Redis pub/sub bridge between server instances.
permessage-deflate extension - enable carefully for CPU trade-off.
tokio-tungstenite in integration test against local server.
Send CloseFrame with reason for debugging clients.
See WebSockets & SSE in web-backends for Axum-native pattern.
Rare; most WS is HTTP/1.1 upgrade path.
tokio::time::timeout around read.next().
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.
Revisado por Chris St. John·Última actualización: 19 jul 2026