WebSocket in Rust: Real-Time Applications from A to Z

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to WebSocket in Rust: Why Professionals Choose It



Modern web applications require instant data transfer. Whether it's an online chat, a trading terminal, a multiplayer game, or a monitoring system — the WebSocket protocol has become the standard for real-time communication. Rust, with its emphasis on performance and memory safety, is an ideal platform for building such systems.



In this article, we will walk through creating WebSocket applications in Rust from scratch. You will learn how the main libraries work, how to handle multiple connections, and how to build scalable real-time systems. The material is intended for developers familiar with the basics of Rust and asynchronous programming.



1. WebSocket Basics in Rust: Choosing a Library



The Rust ecosystem offers several mature solutions for working with WebSocket. The most popular ones are:



  • tokio-tungstenite — an asynchronous WebSocket library based on tokio, low-level and flexible
  • warp — a web framework with built-in WebSocket support
  • actix-web — a powerful framework with WebSocket support


For our guide, we will choose tokio-tungstenite as the most versatile solution.



Installing Dependencies



Create a new project and add to Cargo.toml:



[dependencies]tokio = { version = "1", features = ["full"] }tokio-tungstenite = "0.21"futures-util = "0.3"serde = { version = "1", features = ["derive"] }serde_json = "1"


Basic WebSocket Server



Let's write a simple echo server that sends back the received message:



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!("Server started on ws://127.0.0.1:8080");

while let Ok((stream, addr)) = listener.accept().await { println!("New connection: {}", 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!("Received: {}", text); let response = format!("Echo: {}", text); write.send(Message::Text(response.into())).await.unwrap(); } }}


This code demonstrates the basic structure: an asynchronous TCP server, accepting WebSocket connections, and handling messages in separate tasks.



2. Architecture of a Real-Time Chat in Rust



Now let's build a full-fledged chat where messages from one client are broadcast to all connected users. For this, we need shared state — a list of active connections.



Implementation with Shared State



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();

// Create a broadcast channel

Blogs

Book Recommendations