← Back to Lessons Lesson 14 of 28
Intermediate api
WebSockets
Introduction
WebSockets enable real-time, bidirectional communication between the browser and a server. Rust/Wasm can manage the WebSocket connection, parse messages, and update application state — all with type safety.
Setup
[dependencies.web-sys]
version = "0.3"
features = [
"WebSocket",
"MessageEvent",
"ErrorEvent",
"CloseEvent",
"BinaryType",
"console",
]Connecting
use web_sys::WebSocket;
let ws = WebSocket::new("wss://echo.websocket.org")?;
// Set binary mode for binary data
ws.set_binary_type(web_sys::BinaryType::Arraybuffer);Sending Messages
// Send text
ws.send_with_str("hello")?;
// Send binary data
let data: Vec<u8> = vec![1, 2, 3, 4];
let array = js_sys::Uint8Array::new_with_length(data.len() as u32);
array.copy_from(&data);
ws.send_with_array_buffer(&array.buffer())?;Receiving Messages
use web_sys::MessageEvent;
use wasm_bindgen::closure::Closure;
let onmessage = Closure::wrap(Box::new(move |e: MessageEvent| {
// Text message
if let Some(text) = e.data().as_string() {
log!("Text: {}", text);
}
// Binary message
if let Ok(buf) = e.data().dyn_into::<js_sys::ArrayBuffer>() {
let array = js_sys::Uint8Array::new(&buf);
let data = array.to_vec();
log!("Binary: {} bytes", data.len());
}
}) as Box<dyn FnMut(MessageEvent)>);
ws.set_onmessage(Some(onmessage.as_ref().unchecked_ref()));
onmessage.forget();Connection Lifecycle
// On open
let onopen = Closure::wrap(Box::new(move || {
log!("Connected!");
}) as Box<dyn FnMut()>);
ws.set_onopen(Some(onopen.as_ref().unchecked_ref()));
onopen.forget();
// On close
let onclose = Closure::wrap(Box::new(move |e: web_sys::CloseEvent| {
log!("Disconnected: code={}, reason={}", e.code(), e.reason());
}) as Box<dyn FnMut(_)>);
ws.set_onclose(Some(onclose.as_ref().unchecked_ref()));
onclose.forget();
// On error
let onerror = Closure::wrap(Box::new(move |e: ErrorEvent| {
log!("Error: {}", e.message());
}) as Box<dyn FnMut(_)>);
ws.set_onerror(Some(onerror.as_ref().unchecked_ref()));
onerror.forget();Reconnection Pattern
#[wasm_bindgen]
pub struct ReconnectingWs {
url: String,
ws: Option<WebSocket>,
retry_count: u32,
}
#[wasm_bindgen]
impl ReconnectingWs {
pub fn connect(&mut self) -> Result<(), JsValue> {
let ws = WebSocket::new(&self.url)?;
let url = self.url.clone();
let onclose = Closure::wrap(Box::new(move || {
// Reconnect after delay
let window = web_sys::window().unwrap();
let url = url.clone();
let closure = Closure::once(move || {
log!("Reconnecting to {}...", url);
// Re-create connection
});
window.set_timeout_with_callback_and_timeout_and_arguments_0(
closure.as_ref().unchecked_ref(), 3000
).unwrap();
closure.forget();
}) as Box<dyn FnMut()>);
ws.set_onclose(Some(onclose.as_ref().unchecked_ref()));
onclose.forget();
self.ws = Some(ws);
Ok(())
}
}Use Cases
| Application | Why Wasm? |
|---|---|
| Chat app | Parse/validate messages in Rust, type-safe protocols |
| Live dashboard | Process streaming data (metrics, logs) at high throughput |
| Multiplayer game | Binary protocol parsing, state synchronization |
| Collaborative editor | OT/CRDT algorithms in Rust for correctness |
Try It
The starter code shows a WebSocket client struct that connects, sends, receives, and handles errors. It wraps the browser WebSocket API with a clean Rust interface.
Try It
Chapter Quiz
Pass all questions to complete this lesson