Bibliothèques WebSocket pour Rust : tungstenite et tokio-tungstenite
WebSocket est un protocole offrant une communication bidirectionnelle entre un client et un serveur sur une seule connexion TCP. Dans l'écosystème Rust, les bibliothèques les plus populaires pour travailler avec WebSocket sont tungstenite (synchrone) et tokio-tungstenite (asynchrone basée sur Tokio). Les deux bibliothèques fournissent une API de haut niveau pour créer des serveurs et clients WebSocket, prennent en charge les extensions standard et la sécurité via TLS.
Fonctionnalités principales
- tungstenite : implémentation synchrone de WebSocket (RFC 6455), prend en charge les opérations bloquantes, adaptée aux scripts simples et à l'intégration.
- tokio-tungstenite : enveloppe asynchrone autour de tungstenite, s'intègre avec le runtime Tokio, permet de gérer des milliers de connexions simultanément.
- Prise en charge des trames, messages, pings/pongs, fermeture de connexion.
- Fonctionnement avec TLS (via
rustlsounative-tls). - Prise en charge des extensions WebSocket (par exemple, compression permessage-deflate).
- Configuration simple et dépendances minimales.
Installation
Ajoutez la bibliothèque dans Cargo.toml :
[dependencies]tokio-tungstenite = "0.21" # version asynchronetungstenite = "0.21" # version synchronetokio = { version = "1", features = ["full"] } # pour tokio-tungsteniteExemple de serveur WebSocket asynchrone (tokio-tungstenite)
Créons un simple serveur echo qui reçoit des messages texte et les renvoie :
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!("Serveur WebSocket démarré sur 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!("Erreur lors de la connexion {} : {}", peer, e); return; } };
let (mut write, mut read) = ws_stream.split(); println!("Nouvelle connexion : {}", peer);
while let Some(msg) = read.next().await { match msg { Ok(Message::Text(text)) => { println!("Reçu de {} : {}", peer, text); if let Err(e) = write.send(Message::Text(text.into())).await { eprintln!("Erreur d'envoi : {}", e); break; } } Ok(Message::Close(_)) => { println!("Client {} déconnecté", peer); break; } Err(e) => { eprintln!("Erreur : {}", e); break; } _ => {} // Ignorer les messages binaires et de contrôle } }}Exemple de client WebSocket synchrone (tungstenite)
Un client simple qui se connecte au serveur et envoie un 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!("Connecté au serveur"); println!("Statut HTTP : {}", response.status());
socket.send(Message::Text("Bonjour, WebSocket !".into()))?; println!("Message envoyé");
let msg = socket.read()?; if let Message::T