Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Semaphore

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.

What it is

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::net and threads, no tokio.

The wire format

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.

Message types

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)

Usage

Start the server:

cargo run -- serve --addr 127.0.0.1:7878

Talk 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 name

Or 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")?;

Design

See DESIGN.md for the frame format, the streaming decoder state machine, and the server/client model in detail.

Tests

cargo test

Covers 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.

About

Semaphore is a custom network protocol with framing, handshaking, and reliability.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages