WebSocket Libraries for Rust: tungstenite and tokio-tungstenite
WebSocket is a protocol that provides full-duplex communication between a client and a server over a single TCP connection. In the Rust ecosystem, the most popular libraries for working with WebSocket are tungstenite (synchronous) and tokio-tungstenite (asynchronous based on Tokio). Both libraries provide a high-level API for creating WebSocket servers and clients, support standard extensions, and security via TLS.
Key Features
- tungstenite: synchronous WebSocket implementation (RFC 6455), supports blocking operations, suitable for simple scripts and embedding.
- tokio-tungstenite: asynchronous wrapper over tungstenite, integrates with the Tokio runtime, allows handling thousands of connections simultaneously.
- Support for frames, messages, pings/pongs, connection closure.
- TLS support (via
rustlsornative-tls). - Support for WebSocket extensions (e.g., permessage-deflate compression).
- Simple setup and minimal dependencies.
Installation
Add the library to Cargo.toml:
[dependencies]tokio-tungstenite = "0.21" # asynchronous varianttungstenite = "0.21" # synchronous varianttokio = { version = "1", features = ["full"] } # for tokio-tungsteniteExample of an Asynchronous WebSocket Server (tokio-tungstenite)
Let's create a simple echo server that receives text messages and sends them back:
use tokio::net::TcpListener;use tokio_tungstenite::accept_async;use tokio_tungstenite::tungstenite::Message;use futures_util::{SinkExt, StreamExt};
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let listener = TcpListener::bind("127.0.0.1:9001").await?; println!("WebSocket server started on ws://127.0.0.1:9001");
while let Ok((stream, peer)) = listener.accept().await { tokio::spawn(handle_connection(stream, peer)); } Ok(())}
async fn handle_connection(stream: tokio::net::TcpStream, peer: std::net::SocketAddr) { let ws_stream = match accept_async(stream).await { Ok(ws) => ws, Err(e) => { eprintln!("Error connecting {}: {}", peer, e); return; } };
let (mut write, mut read) = ws_stream.split(); println!("New connection: {}", peer);
while let Some(msg) = read.next().await { match msg { Ok(Message::Text(text)) => { println!("Received from {}: {}", peer, text); if let Err(e) = write.send(Message::Text(text.into())).await { eprintln!("Error sending: {}", e); break; } } Ok(Message::Close(_)) => { println!("Client {} disconnected", peer); break; } Err(e) => { eprintln!("Error: {}", e); break; } _ => {} // Ignore binary and control messages } }}Example of a Synchronous WebSocket Client (tungstenite)
A simple client that connects to a server and sends a message:
use tungstenite::{connect, Message};use url::Url;
fn main() -> Result<(), Box<dyn std::error::Error>> { let url = Url::parse("ws://echo.websocket.org")?; let (mut socket, response) = connect(url)?;
println!("Connected to server"); println!("HTTP status: {}", response.status());
socket.send(Message::Text("Hello, WebSocket!".into()))?; println!("Message sent");
let msg = socket.read()?; if let Message::T Also in library
ASIO for C++: Complete Guide with Examples
Rate Limiting in C# (.NET): Complete Guide to System.Threading.RateLimiting Library
Sending SMS in C++ using the libcurl API library