WebSocket¶
Connects the host to a device on a network: a Linux board, a simulator in another process or on another machine, or a gateway in front of devices on other media.
Host side¶
Enable the ws feature of wire_weaver_client and select the device by URL:
Everything above the medium is the same as with USB or RTT: link setup, version
check, introspection, pings, timeouts, streams. There is no discovery, so device filters (serial, label, ...) are not
checked before connecting, and only plain ws:// is supported, no wss://.
Device side¶
The ws feature of ww_device is the device end: ww_device::ws::WsTx / WsRx implement MessageTx /
MessageRx with the wire format below, on top of any TCP-like ws::Socket (accept one client at a time, read, write).
They run the same Server loop as over USB. The embassy-net feature adds
EmbassyNetSocket on an embassy-net TCP socket, and ws_server() puts it all together:
let mut socket = TcpSocket::new(stack, rx_buf, tx_buf);
socket.set_timeout(Some(Duration::from_secs(5)));
socket.set_keep_alive(Some(Duration::from_secs(2)));
let conn = WS_CONNECTION.init(WsConnection::new(EmbassyNetSocket::new(socket, 8080)));
let mut server = ws_server(link_config, conn, EmbassyClock, WS_BUFFERS.init(WsBuffers::new()));
server.run(&mut state).await;
- No allocation, no extra copies: frames are received and unmasked in place, replies are accumulated in the tx
buffer until the event loop flushes.
WsBuffers<MAX_MESSAGE_LEN>holds all of it; the client's HTTP upgrade request must fit into the rx buffer too (a few hundred bytes). wait_message()andwait_connected()are cancel-safe: the handshake and partially received frames are kept inWsRx, soserver.wait()can be selected on together with anything else.- Handshake and frame headers come from the sans-IO parts of
edge-httpandedge-ws. Any request path is accepted, a plain HTTP request gets400 Bad Request. - Unfragmented binary messages only; Ping and Pong are ignored, Text, fragmented messages and Close drop the connection. When the link goes down (e.g., ping timeout), the client is dropped and the next one is accepted.
EmbassyNetSocketdisables Nagle's algorithm on each accepted connection, for the same reason as on the host. Set a timeout on the socket, so that writes to a host that is gone without closing the connection fail.
examples_mcu/rp2 has a complete example, ww_ws_ncm: the board shows up on the host as a USB CDC-NCM Ethernet
adapter, runs embassy-net with a static address and a small DHCP server (edge-dhcp), so the host is configured
automatically, and serves the blinky API at ws://192.168.7.1:8080/ww. Run it with just run rp2 ww_ws_ncm from
examples_mcu/rp2, then blink from the host with cargo run -p blinky --features ws --example blinky_ws.
Wire format¶
Each ww_link message is one binary WebSocket message:
kind is ww_link::Kind, the payload is what the same message carries over other media. WebSocket has its own
framing over a reliable stream, so ww_framer is not used: messages are never split or packed
together, and there is nothing to re-synchronize on. Small messages still share TCP segments: the host buffers them
until the event loop flushes (after the accumulation time) and writes them out in one go. The host's socket has
TCP_NODELAY set, so Nagle's algorithm doesn't hold back a flush while an earlier segment is waiting for its ACK,
which would add up to a delayed-ACK timeout (tens of ms) to a request.
Text messages are not expected and drop the connection. Ping and Pong are answered by the WebSocket library and
are not needed to keep the link alive, ww_link pings do that.