A tiny, readable network protocol in Rust you can understand end to end.
Semaphore is a length-prefixed binary message protocol built from scratch on top of raw TCP. No framework, no async runtime, no hidden magic. Just a framing codec, a handful of message types, and a server and client that speak them.
Most people reach for a framework the moment they need two processes to talk over a socket. Semaphore takes the other path: it is small enough to read in one sitting, and every layer is a pure, unit-tested piece you can hold in your head.
- A framing codec that turns a stream of bytes into discrete messages.
- A typed protocol (PING/PONG and a small key-value SET/GET) encoded on top of frames.
- A TCP server and client using
std::netand threads, notokio.
Every message on the wire is a 4-byte big-endian length prefix followed by exactly that many payload bytes:
+----------------------+----------------------------+
| length (4 bytes, BE) | payload (length bytes) |
+----------------------+----------------------------+
The decoder is a small state machine that reassembles a frame correctly even if the bytes arrive one at a time across multiple reads, and it rejects any declared length over 16 MiB before allocating a single byte for the payload.
The payload of a frame is itself a tiny typed protocol: a 1-byte tag followed by a tag-specific body.
| Message | Tag | Body |
|---|---|---|
| PING | 0x01 | (none) |
| PONG | 0x02 | (none) |
| SET | 0x03 | key (u16-len string), value (u32-len bytes) |
| GET | 0x04 | key (u16-len string) |
| VALUE | 0x05 | value (u32-len bytes) |
| NOT_FOUND | 0x06 | (none) |
Start the server:
cargo run -- serve --addr 127.0.0.1:7878Talk to it from another terminal:
cargo run -- ping --addr 127.0.0.1:7878
cargo run -- set --addr 127.0.0.1:7878 name semaphore
cargo run -- get --addr 127.0.0.1:7878 nameOr use it as a library:
use semaphore::Client;
let mut client = Client::connect("127.0.0.1:7878")?;
client.set("name", b"semaphore")?;
let value = client.get("name")?;See DESIGN.md for the frame format, the streaming decoder state machine, and the server/client model in detail.
cargo testCovers codec roundtrips (empty, small, large payloads), byte-at-a-time streaming reassembly, oversize length prefix rejection without allocation, malformed message body errors, and a real TCP integration test exercising SET/GET/PING against a live server.
By Pavan Nallamothu.