Introduction à WebSocket en Rust : pourquoi c'est le choix des professionnels
Les applications web modernes nécessitent une transmission instantanée des données. Qu'il s'agisse d'un chat en ligne, d'un terminal boursier, d'un jeu multijoueur ou d'un système de surveillance — le protocole WebSocket est devenu la norme pour la communication en temps réel. Rust, avec son accent sur la performance et la sécurité mémoire, est une plateforme idéale pour créer de tels systèmes.
Dans cet article, nous allons détailler la création d'applications WebSocket en Rust à partir de zéro. Vous apprendrez comment fonctionnent les bibliothèques principales, comment gérer de multiples connexions et comment construire des systèmes temps réel évolutifs. Ce contenu est destiné aux développeurs familiers avec les bases de Rust et la programmation asynchrone.
1. Bases de WebSocket en Rust : choisir une bibliothèque
L'écosystème Rust propose plusieurs solutions matures pour travailler avec WebSocket. Les plus populaires :
- tokio-tungstenite — WebSocket asynchrone basé sur tokio, bas niveau et flexible
- warp — framework web avec support WebSocket intégré
- actix-web — framework puissant avec support WebSocket
Pour notre guide, nous choisirons tokio-tungstenite comme solution la plus polyvalente.
Installation des dépendances
Créez un nouveau projet et ajoutez dans Cargo.toml :
[dependencies]tokio = { version = "1", features = ["full"] }tokio-tungstenite = "0.21"futures-util = "0.3"serde = { version = "1", features = ["derive"] }serde_json = "1"Serveur WebSocket de base
Écrivons un simple serveur echo qui renvoie le message reçu :
use futures_util::{SinkExt, StreamExt};use tokio::net::TcpListener;use tokio_tungstenite::accept_async;use tokio_tungstenite::tungstenite::Message;
#[tokio::main]async fn main() { let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap(); println!("Serveur démarré sur ws://127.0.0.1:8080");
while let Ok((stream, addr)) = listener.accept().await { println!("Nouvelle connexion : {}", addr); tokio::spawn(handle_connection(stream)); }}
async fn handle_connection(stream: tokio::net::TcpStream) { let ws_stream = accept_async(stream).await.unwrap(); let (mut write, mut read) = ws_stream.split();
while let Some(Ok(msg)) = read.next().await { if let Message::Text(text) = msg { println!("Reçu : {}", text); let response = format!("Echo : {}", text); write.send(Message::Text(response.into())).await.unwrap(); } }}Ce code illustre la structure de base : un serveur TCP asynchrone, l'acceptation de connexions WebSocket et le traitement des messages dans des tâches séparées.
2. Architecture d'un chat temps réel en Rust
Construisons maintenant un chat complet où les messages d'un client sont diffusés à tous les utilisateurs connectés. Pour cela, nous avons besoin d'un état partagé — une liste de connexions actives.
Implémentation avec état partagé
use std::collections::HashMap;use std::sync::Arc;use tokio::sync::{Mutex, broadcast};
type Tx = broadcast::Sender<String>;type Clients = Arc<Mutex<HashMap<String, Tx>>>;
#[tokio::main]async fn main() { let clients: Clients = Arc::new(Mutex::new(HashMap::new())); let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();
while let Ok((stream, addr)) = listener.accept().await { let clients = clients.clone(); tokio::spawn(handle_client(stream, addr, clients)); }}
async fn handle_client( stream: tokio::net::TcpStream, addr: std::net::SocketAddr, clients: Clients) { let ws_stream = accept_async(stream).await.unwrap(); let (mut write, mut read) = ws_stream.split();
// Création d'un canal de diffusion