A custom reliable data transfer protocol built on top of UDP with support for file transfer, chat mode, three-way handshake, four-way connection termination, sliding window flow control, and configurable packet loss simulation.
This project implements a reliable transport protocol called S.H.A.M. (Simple Handshake and Messaging) over UDP, featuring:
- Three-way handshake for connection establishment
- Four-way handshake for graceful connection termination
- Sliding window protocol with Go-Back-N ARQ for reliable data transfer
- Flow control with dynamic window advertisement
- Timeout and retransmission mechanism
- Packet loss simulation for testing reliability
- File transfer mode with MD5 checksum verification
- Interactive chat mode for real-time messaging
- Comprehensive logging for debugging and analysis
struct sham_header {
uint32_t seq_num; // Sequence Number
uint32_t ack_num; // Acknowledgment Number
uint16_t flags; // Control flags (SYN, ACK, FIN)
uint16_t window_size; // Flow control window size
};SYN_FLAG (0x1): Synchronize sequence numbers (connection establishment)ACK_FLAG (0x2): AcknowledgmentFIN_FLAG (0x4): Finish connection (graceful termination)
- Three-Way Handshake: SYN → SYN-ACK → ACK sequence for reliable connection establishment
- Four-Way Handshake: FIN → ACK → FIN → ACK sequence for graceful connection termination
- Retransmission: Automatic retransmission on timeout with configurable RTO (500ms default)
- Sliding Window Protocol: Client uses 4-packet window, server buffers up to 20 packets
- Selective Acknowledgment: Cumulative ACKs with duplicate detection
- Out-of-Order Buffering: Server buffers out-of-order packets for efficient delivery
- Flow Control: Dynamic window advertisement prevents receiver buffer overflow
- Configurable Loss Rate: Simulate packet loss with rates between 0.0 and 1.0
- Timeout Detection: Detect lost packets using RTO timer
- Automatic Retransmission: Retransmit unacknowledged packets on timeout
- Event Logging: Detailed logs for all protocol events (send, receive, drop, timeout)
- Timestamped Entries: Microsecond-precision timestamps for debugging
- Environment Variable Control: Enable logging with
RUDP_LOG=1
.
├── client.c # Client implementation (sender)
├── server.c # Server implementation (receiver)
├── sham.c # Protocol helper functions (byte order conversion)
├── sham.h # Protocol header definitions
├── Makefile # Build configuration
├── test.txt # Sample test file
├── client_log.txt # Client event log (generated)
└── server_log.txt # Server event log (generated)
# Build both client and server
make
# Clean build artifacts
make cleanThe Makefile automatically:
- Detects OpenSSL installation via Homebrew (macOS)
- Links with OpenSSL's crypto library for MD5 checksums
- Compiles with C11 standard and warning flags
# On macOS with Homebrew OpenSSL
gcc -Wall -Wextra -std=c11 -I$(brew --prefix openssl)/include -L$(brew --prefix openssl)/lib server.c sham.c -o server -lcrypto
gcc -Wall -Wextra -std=c11 -I$(brew --prefix openssl)/include -L$(brew --prefix openssl)/lib client.c sham.c -o client -lcrypto
# On Linux (standard OpenSSL paths)
gcc -Wall -Wextra -std=c11 server.c sham.c -o server -lcrypto
gcc -Wall -Wextra -std=c11 client.c sham.c -o client -lcrypto# Basic usage
./server <port>
# With packet loss simulation
./server <port> <loss_rate>
# Examples
./server 8080
./server 8080 0.1 # 10% packet loss# Basic usage
./client <server_ip> <port> <input_file> <output_filename>
# With packet loss simulation
./client <server_ip> <port> <input_file> <output_filename> <loss_rate>
# Examples
./client 127.0.0.1 8080 test.txt received.txt
./client 127.0.0.1 8080 test.txt received.txt 0.15 # 15% packet loss# Start server in chat mode
./server <port> --chat [loss_rate]
# Examples
./server 8080 --chat
./server 8080 --chat 0.05 # 5% packet loss# Connect to server in chat mode
./client <server_ip> <port> --chat [loss_rate]
# Examples
./client 127.0.0.1 8080 --chat
./client 127.0.0.1 8080 --chat 0.1 # 10% packet lossChat Commands:
- Type message and press Enter to send
- Type
/quitto exit chat mode and close connection
# Set environment variable before running
export RUDP_LOG=1
# Run client/server
./client 127.0.0.1 8080 test.txt output.txt
./server 8080
# Logs will be written to client_log.txt and server_log.txtWindow Management:
- Maintains sliding window of 4 packets (configurable with
WINDOW_SIZE) - Tracks base sequence number and next sequence to send
- Implements dynamic timeout calculation based on oldest unacknowledged packet
Flow Control:
- Monitors receiver's advertised window size
- Suspends transmission when receiver buffer is full
- Resumes transmission when window opens
Retransmission:
- 500ms RTO (Retransmission Timeout)
- Retransmits oldest unacknowledged packet on timeout
- Uses non-blocking socket with
select()for multiplexing
Buffer Management:
- Buffers up to 20 out-of-order packets (2× window size)
- Delivers packets to application in order
- Dynamically updates advertised window based on buffer occupancy
ACK Generation:
- Sends cumulative ACK for next expected sequence number
- Sends duplicate ACKs for out-of-order packets
- Includes current window size in every ACK
File Reception:
- Writes received data to file as it arrives in order
- Calculates MD5 checksum after complete file reception
- Displays checksum for verification
Bidirectional Communication:
- Uses
select()to multiplex between stdin and socket - Sends messages as data packets with S.H.A.M. header
- Real-time display of incoming and outgoing messages
Connection Termination:
- Client initiates termination with
/quitcommand - Four-way handshake ensures graceful shutdown
- Both sides can detect premature disconnection
Client Server
| |
| SYN (seq=1000) |
|----------------------------->|
| |
| SYN-ACK (seq=5000, ack=1001)|
|<-----------------------------|
| |
| ACK (ack=5001) |
|----------------------------->|
| |
| [Connection Established] |
Client Server
| |
| DATA (seq=X, len=1024) |
|----------------------------->|
| |
| ACK (ack=X+1024) |
|<-----------------------------|
| |
| DATA (seq=X+1024, len=512) |
|----------------------------->|
| |
| ACK (ack=X+1536) |
|<-----------------------------|
Client Server
| |
| FIN (seq=Y) |
|----------------------------->|
| |
| ACK (ack=Y+1) |
|<-----------------------------|
| |
| FIN (seq=Z) |
|<-----------------------------|
| |
| ACK (ack=Z+1) |
|----------------------------->|
| |
| [Connection Closed] |
WINDOW_SIZE: 4 packetsRTO_MS: 500 millisecondsPAYLOAD_SIZE: 1024 bytesBUFFER_SIZE: 1040 bytes (header + payload)
WINDOW_SIZE: 10 packets (buffer = 2× window)PAYLOAD_SIZE: 1024 bytesBUFFER_SIZE: 1040 bytes
- Compiler: GCC with C11 standard
- Flags:
-Wall -Wextra(strict warnings) - OpenSSL: Automatic detection via Homebrew (macOS)
- Library:
-lcryptofor MD5 checksums - Targets:
serverandclientexecutables
- GCC compiler with C11 support
- OpenSSL library (
libssl-devon Ubuntu/Debian,opensslvia Homebrew on macOS) - Standard POSIX headers:
sys/socket.h,netinet/in.h,arpa/inet.h
- Socket creation/binding failures with descriptive error messages
- File open/read/write errors with errno reporting
- Handshake timeout and automatic retransmission
- Packet loss detection and recovery via timeout mechanism
- Graceful handling of unexpected disconnections
- Invalid command-line argument validation
# Terminal 1: Start server
./server 8080
# Terminal 2: Send file
./client 127.0.0.1 8080 test.txt received.txt
# Verify transfer
md5sum test.txt received.txt # Linux
md5 test.txt received.txt # macOS# Terminal 1: Server with 20% loss
export RUDP_LOG=1
./server 8080 0.2
# Terminal 2: Client with 20% loss
export RUDP_LOG=1
./client 127.0.0.1 8080 test.txt received.txt 0.2
# Check logs
tail -f client_log.txt
tail -f server_log.txt# Terminal 1: Server in chat mode
./server 8080 --chat
# Terminal 2: Client in chat mode
./client 127.0.0.1 8080 --chat
# Exchange messages, then type /quit on client# Create a large test file (10 MB)
dd if=/dev/urandom of=largefile.bin bs=1024 count=10240
# Transfer the file
./server 8080
./client 127.0.0.1 8080 largefile.bin received_large.bin
# Verify integrity
diff largefile.bin received_large.bin[2025-11-15 01:30:45.123456] [LOG] SND SYN SEQ=1000
[2025-11-15 01:30:45.234567] [LOG] RCV SYN-ACK SEQ=5000 ACK=1001
[2025-11-15 01:30:45.345678] [LOG] SND DATA SEQ=1001 LEN=1024
[2025-11-15 01:30:45.456789] [LOG] RCV ACK=2025
[2025-11-15 01:30:46.567890] [LOG] TIMEOUT SEQ=2025
[2025-11-15 01:30:46.678901] [LOG] RETX DATA SEQ=2025 LEN=512
[2025-11-15 01:30:46.789012] [LOG] DROP DATA SEQ=3000
# Install OpenSSL via Homebrew
brew install openssl
# Update Makefile or set paths manually
export OPENSSL_ROOT=$(brew --prefix openssl)# Install OpenSSL development package
sudo apt-get install libssl-dev # Ubuntu/Debian
sudo yum install openssl-devel # CentOS/RHEL# Find process using the port
lsof -i :8080 # macOS/Linux
netstat -ano | grep 8080 # Linux
# Kill the process or use a different port
./server 8081- Fixed payload size of 1024 bytes per packet
- No congestion control (only flow control)
- Simple Go-Back-N retransmission (not selective repeat)
- Single-threaded server (handles one client at a time)
- No encryption or authentication
- No adaptive RTO calculation (fixed 500ms timeout)
dawnbliss-coder