Network protocol in Rust

Bytes on the wire,
framed by hand.

Semaphore is a length-prefixed binary message protocol built from scratch, with a framing codec you can read end to end, plus a TCP server and client that speak it.

View on GitHub Try the live demo

pick a message, watch it become bytes, then decode those bytes one at a time

Encoder

len 4 byte length prefix tag 1 byte message kind body the rest of the frame

Streaming decoder

Ready.

Oversize length prefix

The decoder checks the declared length against the frame cap before it allocates anything for the payload. A length prefix above the cap is rejected on the spot.

Not tested yet.

The wire format

Every message on the wire is a 4 byte big-endian length prefix followed by exactly that many payload bytes. That is the entire framing layer.

length u32, big-endian
payload exactly `length` bytes

Why it exists

Most people reach for a framework the moment two processes need to talk over a socket. Semaphore takes the other path, small enough to read in one sitting, with every layer a pure, unit-tested piece you can hold in your head.

Framing codec

A pure, dependency-free encoder and a streaming decoder that reassembles a frame correctly even when it arrives split across many reads, and rejects an oversize length prefix before allocating anything.

Typed protocol

PING and PONG, plus a small key-value SET and GET request-response protocol encoded on top of frames, with a one-byte tag and bounds-checked bodies.

TCP server and client

Plain std::net and OS threads, no async runtime. The server holds an in-memory map, the client issues blocking SET, GET, and PING calls.

Message types

The payload of a frame is a small typed protocol, a one-byte tag followed by a tag-specific body.

MessageTagBody
PING0x01none
PONG0x02none
SET0x03key (u16-len string), value (u32-len bytes)
GET0x04key (u16-len string)
VALUE0x05value (u32-len bytes)
NOT_FOUND0x06none

Usage

Start the server, then talk to it from the CLI or as a library.

cargo run -- serve --addr 127.0.0.1:7878

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 name