This example connects to an echo server on wss://echo.websocket.org, sends a ping message, and receives the response. _ wasm-bindgen Guide{target="_blank"}
web-sys: WebSockets{target="_blank"}
It is already a no bundle example. :-)
setup the project
cargo new websockets --lib
cd websockets
mkdir -p www/js www/html
cargo add wasm-bindgen js-sys
- Edit Cargo.toml
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2.89"
js-sys = "0.3.66"
[dependencies.web-sys]
version = "0.3.66"
features = [
"BinaryType",
"Blob",
"ErrorEvent",
"FileReader",
"MessageEvent",
"ProgressEvent",
"WebSocket",
]
The code
- index.html
<!doctype html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
<title>web sockets: nobundle</title>
</head>
<body>
<script type="module" src="../js/index.js"></script>
</body>
</html>
- index.js
import init from '../pkg/websockets.js';
window.addEventListener('load', async () => {
await init('../pkg/websockets_bg.wasm');
});
/*
async function run() {
const wasm = await init().catch(console.error);
}
run();
*/
- Rust side
#![allow(unused)] fn main() { // src/lib.rs use wasm_bindgen::prelude::*; use web_sys::{ErrorEvent, MessageEvent, WebSocket}; macro_rules! console_log { ($($t:tt)*) => (log(&format_args!($($t)*).to_string())) } #[wasm_bindgen] extern "C" { #[wasm_bindgen(js_namespace = console)] fn log(s: &str); } #[wasm_bindgen(start)] fn start_websocket() -> Result<(), JsValue> { // Connect to an echo server let ws = WebSocket::new("wss://echo.websocket.events")?; // For small binary messages, like CBOR, Arraybuffer is more efficient than Blob handling ws.set_binary_type(web_sys::BinaryType::Arraybuffer); // create callback let cloned_ws = ws.clone(); let onmessage_callback = Closure::<dyn FnMut(_)>::new(move |e: MessageEvent| { // Handle difference Text/Binary,... if let Ok(abuf) = e.data().dyn_into::<js_sys::ArrayBuffer>() { console_log!("message event, received arraybuffer: {:?}", abuf); let array = js_sys::Uint8Array::new(&abuf); let len = array.byte_length() as usize; console_log!("Arraybuffer received {}bytes: {:?}", len, array.to_vec()); // here you can for example use Serde Deserialize decode the message // for demo purposes we switch back to Blob-type and send off another binary message cloned_ws.set_binary_type(web_sys::BinaryType::Blob); match cloned_ws.send_with_u8_array(&[5, 6, 7, 8]) { Ok(_) => console_log!("binary message successfully sent"), Err(err) => console_log!("error sending message: {:?}", err), } } else if let Ok(blob) = e.data().dyn_into::<web_sys::Blob>() { console_log!("message event, received blob: {:?}", blob); // better alternative to juggling with FileReader is to use https://crates.io/crates/gloo-file let fr = web_sys::FileReader::new().unwrap(); let fr_c = fr.clone(); // create onLoadEnd callback let onloadend_cb = Closure::<dyn FnMut(_)>::new(move |_e: web_sys::ProgressEvent| { let array = js_sys::Uint8Array::new(&fr_c.result().unwrap()); let len = array.byte_length() as usize; console_log!("Blob received {}bytes: {:?}", len, array.to_vec()); // here you can for example use the received image/png data }); fr.set_onloadend(Some(onloadend_cb.as_ref().unchecked_ref())); fr.read_as_array_buffer(&blob).expect("blob not readable"); onloadend_cb.forget(); } else if let Ok(txt) = e.data().dyn_into::<js_sys::JsString>() { console_log!("message event, received Text: {:?}", txt); } else { console_log!("message event, received Unknown: {:?}", e.data()); } }); // set message event handler on WebSocket ws.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref())); // forget the callback to keep it alive onmessage_callback.forget(); let onerror_callback = Closure::<dyn FnMut(_)>::new(move |e: ErrorEvent| { console_log!("error event: {:?}", e); }); ws.set_onerror(Some(onerror_callback.as_ref().unchecked_ref())); onerror_callback.forget(); let cloned_ws = ws.clone(); let onopen_callback = Closure::<dyn FnMut()>::new(move || { console_log!("socket opened"); match cloned_ws.send_with_str("ping") { Ok(_) => console_log!("message successfully sent"), Err(err) => console_log!("error sending message: {:?}", err), } // send off binary message match cloned_ws.send_with_u8_array(&[0, 1, 2, 3]) { Ok(_) => console_log!("binary message successfully sent"), Err(err) => console_log!("error sending message: {:?}", err), } }); ws.set_onopen(Some(onopen_callback.as_ref().unchecked_ref())); onopen_callback.forget(); Ok(()) } }
build and serve
wasm-pack build --target web --no-typescript --out-dir www/pkg
http www
open index.html
firefox http://localhost:8000/html/
What's next?
Next example: WebRTC DataChannel -->