Introduction to WebSocket in Rust
Real-time applications have become an integral part of the modern web. Chat systems, online games, stock quotes, and real-time notifications all require constant two-way data exchange between client and server. The WebSocket protocol provides exactly this capability, and Rust, thanks to its performance and safety, is an ideal language for building such systems.
In this article, we will thoroughly examine how to create real-time applications in Rust using WebSocket. You will learn about popular crates, architectural approaches, and get ready-made code examples for a quick start.
WebSocket Basics and Library Selection
WebSocket is a protocol that provides full-duplex communication over a single TCP connection. Unlike HTTP, where the client initiates a request and the server responds, WebSocket allows both sides to send data at any time.
Popular Crates for WebSocket in Rust
- tokio-tungstenite — an asynchronous WebSocket client and server based on Tokio. Lightweight and fast.
- actix-web — a powerful web framework with built-in WebSocket support.
- tungstenite — a synchronous version, convenient for simple scenarios.
- warp — another asynchronous framework with WebSocket support.
For most projects, we recommend tokio-tungstenite or actix-web. The former is for lightweight servers and clients, the latter for full-fledged web applications.
Creating a WebSocket Server in Rust
Let's look at creating a simple echo server that receives messages and sends them back to the client. We'll use tokio-tungstenite.
Project Setup
Create a new Cargo project and add dependencies to Cargo.toml:
[dependencies]tokio = { version = "1", features = ["full"] }tokio-tungstenite = "0.21"futures-util = "0.3"Basic Echo Server
use futures_util::{SinkExt, StreamExt};use tokio::net::TcpListener;use tokio_tungstenite::accept_async;
#[tokio::main]async fn main() -> Result<(), Box> { let listener = TcpListener::bind("127.0.0.1:8080").await?; 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)); }
Ok(())}
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(msg) = read.next().await { let msg = msg.unwrap(); // Send the message back write.send(msg).await.unwrap(); }} This code creates a TCP server on port 8080, accepts WebSocket connections, and for each client launches an asynchronous task that echoes all incoming messages.
Creating a WebSocket Client in Rust
Now let's write a client that connects to our server, sends a message, and receives a response.
use futures_util::StreamExt;use tokio_tungstenite::connect_async;
#[tokio::main]async fn main() -> Result<(), Box> { let url = "ws://127.0.0.1:8080"; let (ws_stream, _) = connect_async(url).await?;
println!("Connected to server");
let (mut write, mut read) = ws_stream.split();
// Send a message write.send("Hello, WebSocket!".into()).await?;
// Receive a response if let Some(msg) = read.next().await { let msg = msg?; println!("Received: {}", msg.to_text().unwrap()); }
Ok(())} Real-time Chat Using actix-web
Now let's