Relic-UDP --- A Simple Reliable UDP Implementation
This is a sample learning project where I delved into the working principles of the network & transport layer with particular focus on the UDP(User Datagram Protocol).
This project demonstrates how to add TCP-like reliability guarantees (acknowledgments, retransmission, ordering) to UDP's speed and simplicity.
The aim is to create a rust project that uses tokio and async rust to test and understand how UDP works and then improve on its shortcomings
Why This Exists
This is an educational project designed to build deep understanding of:
- Transport layer protocols (TCP, UDP, QUIC)
- Network reliability mechanisms (sequence numbers, ACKs, timeouts)
- Async networking in Rust (Tokio, async/await)
- Blockchain networking fundamentals (Solana validator communication, gossip protocols) By building reliability from scratch, I gain insight into how production protocols like QUIC work and how Solana validators communicate efficiently.
How It Works
This is a reliable UDP implemetation that gives the speed of UDP together with reliablity, retransmission and certainty of TCP. In Relic UDP we have:
- Sequence numbers for packet ordering
- Acknowledgments (ACKs) to confirm delivery
- Timeouts & retransmission for handling packet loss
- Flow control for managing sender/receiver speeds
UDP is connectionless, stateless and fast. There are short comings while using UDP, like packet rearrangement, loss etc. So Relic UDP will contain retransmission, Acknowledgement as well as speed
Architecture
┌─────────────┐ ┌─────────────┐
│ Client │ │ Server │
│ │ │ │
│ Protocol │ ←──── UDP ────→ │ Protocol │
│ Layer │ (unreliable) │ Layer │
│ │ │ │
│ Tokio UDP │ │ Tokio UDP │
│ Socket │ │ Socket │
└─────────────┘ └─────────────┘
Components:
- protocol.rs: Packet serialization/deserialization, message types
- client.rs: Sender logic with retransmission
- server.rs: Receiver logic with ACK generation
Key Concepts
- Sequence Numbers: Each packet gets a unique number to track order and detect duplicates/loss
- Acknowledgments (ACKs): Receiver confirms each packet received by sending ACK with sequence number
- Timeouts: If no ACK received within timeout period, sender retransmits the packet
- Retransmission: Lost packets are automatically resent based on timeout or negative ACK
Features
Implemented:
- Basic UDP client/server communication
- Packet structure with sequence numbers
- Serialization/deserialization (to_bytes/from_bytes)
- Unit tests for protocol layer
In Progress:
- ACK message type
- Timeout & retransmission logic
- Packet loss simulation
Future:
- Go-Back-N sliding window protocol
- Selective Repeat ARQ
- Flow control (window size management)
- Congestion control
- Custom UDP implementation from scratch (using tun/tap)
Prerequisites
- Rust 1.70+ (with Cargo)
- Linux/macOS/WSL (for full feature support)
Installation
git clone https://github.com/AlphaR2/Relic-UDP.git
cd Relic-UDP
cargo buildRunning the Server
Expected output:
=================================
🚀 RELIABLE UDP SERVER
=================================
Server listening on: 127.0.0.1:8080
RECEIVED Packet #1 from 127.0.0.1:xxxxx
Data: "Hello from packet #1"
ECHOED Packet #1 back
Running the Client
# In a separate terminal
cargo run --bin clientExpected output:
=================================
📡 RELIABLE UDP CLIENT
=================================
Client: 127.0.0.1:xxxxx
Server: 127.0.0.1:8080
SENDING Packet #1: Hello from packet #1
RECEIVED Packet #1: "Hello from packet #1"
SENDING Packet #2: Hello from packet #2
RECEIVED Packet #2: "Hello from packet #2"
...
All packets sent successfully!
Protocol Specification
Packet Format
Current implementation (Data packet):
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number (u32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data (variable length) |
| ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- Sequence Number: 4 bytes (big-endian), tracks packet order
- Data: Variable length payload (text, binary, any data)
Message Types
Current:
- DATA Packet: Contains sequence number + payload data
Coming: 2. ACK Packet: Acknowledgment with sequence number 3. NACK Packet: Negative acknowledgment for explicit retransmission
Protocol Flow
Basic Echo (Current):
Client Server
| |
|-----DATA Packet #1------------->|
| [seq: 1, data: "Hello"] |
| |
|<----Echo Packet #1--------------|
| [seq: 1, data: "Hello"] |
| |
With ACKs (Next Phase):
Client Server
| |
|-----DATA Packet #1------------->|
| [seq: 1, data: "Hello"] |
| |
|<----ACK #1----------------------|
| |
|-----DATA Packet #2------------->|
| [seq: 2, data: "World"] |
| |
|<----ACK #2----------------------|
| |
Testing
# Run all tests cargo test # Run with output cargo test -- --nocapture # Test specific module cargo test protocol::tests
Current tests:
test_packet_seri_deseri: Verifies packet serialization round-triptest_packet_with_empty_data: Tests edge case of empty payload
Technical Decisions
Why UDP?
Speed over guarantees:
- No connection establishment overhead (vs TCP's 3-way handshake)
- Lower latency for time-sensitive applications
- Full control over reliability mechanism
- Matches Solana's networking approach (QUIC on UDP)
Learning opportunity:
- Understand the problems UDP has (packet loss, reordering, duplication)
- Implement solutions used in production protocols
- Appreciate what TCP provides for free
Why This Reliability Approach?
Stop-and-Wait (Current):
- Simplest to implement and understand
- Send one packet, wait for ACK, send next
- Great for learning fundamentals
Sliding Window (Future):
- Much better performance (send multiple packets before waiting)
- Used by TCP, QUIC, and other production protocols
- More complex but necessary for real-world use
Challenges Faced - Working on a fix
1. Packet Buffering Issues
- Problem: Client receiving old packets from OS buffer
- Cause: UDP socket buffers incoming packets in kernel FIFO queue
- Learning: Discovered why sequence number checking is critical
- Solution: Next phase will implement ACK verification loop
2. Serialization Design
- Problem: Converting Rust structs to/from network bytes
- Solution: Used big-endian encoding for network byte order
- Learning: Understood why protocols specify byte order
3. Async Rust Learning Curve
- Problem: Understanding futures, .await, and Tokio runtime
- Solution: Started with simple examples, gradually added complexity
- Learning: Async is necessary for high-performance network code
Current Phase
Phase 2 Complete: Sequence Numbers
- Implemented Packet struct with seq_num field
- Built serialization/deserialization (to_bytes/from_bytes)
- Client sends numbered packets (1, 2, 3, 4, 5)
- Server decodes and echoes back with sequence numbers
- Discovered UDP buffering behavior
Phase 3 Next: ACKs & Verification
- Add Message enum (Data vs Ack)
- Server sends ACK after receiving packet
- Client waits for correct ACK before proceeding
- Implement ACK timeout and retry logic
Roadmap
Phase 1: Basic UDP (Complete)
- UDP echo server/client
- Tokio async networking
- Send/receive raw bytes
Phase 2: Sequence Numbers (Complete)
- Packet structure with seq_num
- Serialization/deserialization
- Numbered packet transmission
Phase 3: ACKs (In Progress)
- ACK message type
- Sequence number verification
- Correct packet ordering
Phase 4: Timeouts & Retransmission
- Timeout detection
- Automatic retransmission
- Retry limits
Phase 5: Sliding Window
- Go-Back-N protocol
- Multiple outstanding packets
- Window size management
Phase 6: Advanced Features
- Flow control
- Congestion control
- Performance optimization
Phase 7: Deep Dive
- Build UDP from scratch using tun/tap
- Implement IP layer
- Packet fragmentation
Real-World Connections
Similar Protocols
TCP
- Similarities: Both use sequence numbers, ACKs, retransmission
- Differences: TCP has connection state, more overhead, automatic flow control
- Relic-UDP: Lighter weight, explicit control over reliability
QUIC
- Similarities: UDP-based, multiplexing, modern design
- Differences: QUIC has encryption (TLS 1.3), connection migration, 0-RTT
- Relic-UDP: Educational simplification of QUIC concepts
TFTP
- Similarities: Simple reliable UDP, stop-and-wait protocol
- Differences: TFTP is for file transfer only
- Relic-UDP: General-purpose protocol learning
Solana Connection
Why this matters for Solana(My major focus):
- Validator Networking: Solana uses QUIC (UDP-based) for transaction ingestion (TPU)
- Turbine Protocol: Block propagation uses UDP with erasure coding
- Gossip: Validator discovery and comm protocol
- Performance: Understanding UDP is key to understanding Solana's speed
Learning Resources
Networking Fundamentals:
- Beej's Guide to Network Programming - UDP/TCP in C (concepts apply to Rust)
- [TCP vs UDP Crash Course by Hussein Nasser ] (https://youtu.be/qqRYkcta6IE?si=NCM3qwO6z3HcJxCk)
- [The OSI Model ] (https://youtu.be/7IS7gigunyI?si=d_o_M06iSJvnByPC)
Async Rust:
- Tokio Tutorial - Official async Rust networking guide
- Async Rust Book - In-depth async/await
- Jon Gjengset's Crust of Rust: async/await
Performance
Benchmarks coming in Phase 4 after timeout/retransmission
Planned metrics:
- Throughput vs TCP (Mbps)
- Latency comparison (RTT)
- Packet loss resilience (% loss vs delivery rate)
Project Structure
relic-udp/
├── src/
│ ├── server.rs # UDP server with packet decoding and echo
│ ├── client.rs # UDP client with numbered packet sending
│ └── protocol.rs # Packet struct, serialization, and tests
├── Cargo.toml # Dependencies (tokio)
├── README.md # This file
└── .gitignore
Status: 🚧 Active Development | Phase 2 Complete | Learning in Public
Last updated: 11/17/2025# Relic-UDP