DMS — Distributed Message Queue System

A message broker built from scratch in C. No frameworks. No dependencies. Just TCP sockets, pthreads, and a clean binary protocol.

Producers send messages to named queues. Consumers pull from those queues. Messages stay until explicitly acknowledged — no silent data loss, no ambiguity about what happened.

How it works

The broker manages a registry of named queues. Each queue is a bounded ring buffer — a fixed-size array with head and tail pointers, O(1) enqueue and dequeue. Messages are copied in, copied out. No heap allocation per message.

  Producer ──PRODUCE──► ┌──────────────────┐ ──DELIVER──► Consumer
                        │   Ring Buffer    │
  Producer ──PRODUCE──► │   head/tail      │ ──DELIVER──► Consumer
                        │   mutex          │
                        │   not_empty cond  │
                        │   not_full cond   │
                        └──────────────────┘

                         ACK / NACK

Thread safety isn’t bolted on — it’s the foundation. Each queue has its own pthread_mutex_t and two condition variables. Lock ordering is documented and enforced: registry lock first, queue lock second, never reversed. Concurrent producers and consumers coordinate through signals, not polling.

The wire protocol is binary, length-prefixed, with a 16-byte header:

┌──────────┬──────────┬──────┬─────────┬────────┬────────┐
│  Magic   │ Payload  │ Type │ Version │ Status │ Msg ID │
│ DEADBEEF │  length  │ 1 B  │  1 B    │ 2 B    │ 4 B    │
└──────────┴──────────┴──────┴─────────┴────────┴────────┘

Every byte has a reason. The magic number catches framing errors immediately. The length prefix eliminates parsing ambiguity. Partial TCP reads are handled correctly — dms_recv_exact loops until every byte arrives, retrying on EINTR.

The file structure

DMS/
├── protocol.h    383 lines  — Wire format, message types, I/O helpers
├── queue.h       483 lines  — Ring buffer, mutex, condition variables
├── server.c      942 lines  — Broker: registry, client threads, persistence
├── client.c                 — CLI: produce, consume, list, stress test
└── Makefile       50 lines  — Two targets, zero dependencies

The entire broker fits in your head. One file for the protocol. One file for the data structure. One file for the server logic.

Quick start

git clone https://github.com/devwail/DMS.git
cd DMS && make

./server &
./client create -q orders
./client produce -q orders -m "order #42"
./client consume -q orders

GCC, C11, pthreads. Nothing else. Read the full write-up →

Source code on GitHub