Client-Server communication using UNIX signals - A deep dive into Inter-Process Communication
Minitalk is a 42 School project that implements a simple client-server communication system using only UNIX signals (SIGUSR1 and SIGUSR2). The challenge is to transmit strings between processes using only these two signals, encoding data bit by bit.
The Challenge: Build a communication protocol using the most basic form of inter-process communication - signals. No pipes, no sockets, just two signals and clever bit manipulation.
Why this matters:
- Understanding UNIX signals and signal handling
- Mastering bit manipulation and binary encoding
- Learning asynchronous programming concepts
- Implementing reliable data transmission protocols
- Introduction to Inter-Process Communication (IPC)
- Server starts and displays its Process ID (PID)
- Client receives server PID and a message to send
- Client converts each character to binary (8 bits)
- Client sends each bit using signals:
SIGUSR1represents binary0SIGUSR2represents binary1
- Server receives signals and reconstructs the message bit by bit
- Server displays the complete message
Message: "Hi"
'H' = 72 = 01001000 (binary)
'i' = 105 = 01101001 (binary)
Transmission sequence:
SIGUSR1 (0) β SIGUSR2 (1) β SIGUSR1 (0) β SIGUSR1 (0) β SIGUSR2 (1) β ...
- β Server displays its PID on startup
- β Client sends string to server using server's PID
- β Server receives and displays the message
- β
Communication using only
SIGUSR1andSIGUSR2 - β Handles multiple clients sequentially
- β Fast and efficient transmission
- β No global variables allowed
- β Server acknowledges each received message
- β Bidirectional communication (server β client confirmation)
- β Support for Unicode characters (UTF-8)
- β Multiple client handling with queuing
Language: C
Allowed functions: write, signal, sigemptyset, sigaddset, sigaction, kill, getpid, malloc, free, pause, sleep, usleep, exit
- Signal Handling - Using
sigaction()to catch and process signals - Bit Manipulation - Converting characters to/from binary
- Process Communication - Sending signals between processes with
kill() - Asynchronous Programming - Handling signals that arrive at unpredictable times
- Protocol Design - Ensuring reliable message transmission
minitalk/
βββ Makefile # Compilation rules
βββ inc/ # Header files
β βββ minitalk.h # Function prototypes and structures
βββ src/ # Source files
β βββ server.c # Server implementation
β βββ client.c # Client implementation
β βββ utils.c # Helper functions (if applicable)
βββ printf/ # ft_printf library (reused from previous project)
βββ libftprintf.a # Static library for formatted output
# Clone the repository
git clone https://github.com/Axel92803/Minitalk.git
cd Minitalk
# Compile mandatory part
make
# Compile bonus part
make bonus
# Clean object files
make clean
# Clean everything
make fclean
# Recompile
make reThis creates two executables: server and client (or server_bonus and client_bonus).
./serverThe server will display its PID:
Server PID: 12345
Waiting for messages...
In another terminal:
./client [SERVER_PID] "Your message here"Example:
./client 12345 "Hello, 42!"The server terminal will display:
Hello, 42!
# Terminal 1: Server
./server
# Terminal 2: Client sends multiple messages
./client 12345 "First message"
./client 12345 "Second message"
./client 12345 "Third message"./client_bonus 12345 "Hello δΈη! π"./client 12345 "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."# Test 1: Simple message
./client [PID] "Hello"
# Test 2: Empty message
./client [PID] ""
# Test 3: Special characters
./client [PID] "!@#$%^&*()_+-=[]{}|;:',.<>?"
# Test 4: Numbers
./client [PID] "1234567890"
# Test 5: Long message (1000+ characters)
./client [PID] "$(python -c 'print("A"*1000)')"
# Test 6: Newlines
./client [PID] $'First line\nSecond line\nThird line'
# Test 7: Unicode (bonus)
./client_bonus [PID] "Γmojis: πππ₯"# Test multiple rapid messages
for i in {1..10}; do
./client [PID] "Message $i" &
done
wait# Send maximum size message
./client [PID] "$(head -c 10000 /dev/urandom | base64)"# With bonus implementation
./client_bonus [PID] "Test acknowledgment"
# Client should receive confirmation from servervoid signal_handler(int signal, siginfo_t *info, void *context)
{
static int bit_count = 0;
static char current_char = 0;
// Shift left and add new bit
current_char = (current_char << 1) | (signal == SIGUSR2);
bit_count++;
// When 8 bits received, we have a complete character
if (bit_count == 8)
{
if (current_char == '\0')
write(1, "\n", 1);
else
write(1, ¤t_char, 1);
bit_count = 0;
current_char = 0;
}
// Send acknowledgment (bonus)
kill(info->si_pid, SIGUSR1);
}void send_char(int pid, char c)
{
int bit;
int i;
i = 7;
while (i >= 0)
{
bit = (c >> i) & 1; // Extract bit at position i
if (bit == 0)
kill(pid, SIGUSR1);
else
kill(pid, SIGUSR2);
usleep(100); // Small delay for reliability
i--;
}
}-
Signal Race Conditions
- Problem: Signals sent too quickly can be lost
- Solution: Implemented proper timing with
usleep()and acknowledgment system
-
Bit Order Management
- Problem: Ensuring correct bit transmission order (MSB to LSB)
- Solution: Careful bit shifting and masking operations
-
Message Reconstruction
- Problem: Assembling bits back into characters reliably
- Solution: Static variables to maintain state between signal calls
-
Multiple Client Handling
- Problem: Server receiving signals from multiple clients simultaneously
- Solution: Client PID tracking and signal queuing (bonus)
-
Unicode Support (Bonus)
- Problem: Handling multi-byte UTF-8 characters
- Solution: Proper byte sequence handling and encoding detection
- UNIX Signals Mastery - Deep understanding of signal handling mechanisms
- Bit Manipulation - Converting data to binary and back efficiently
- Asynchronous Programming - Managing unpredictable event timing
- Protocol Design - Creating reliable communication with minimal tools
- Process Communication - Understanding IPC at a fundamental level
- Timing & Synchronization - Balancing speed with reliability
| Configuration | Speed | Reliability | Use Case |
|---|---|---|---|
| No delay | Fastest | Low (signals lost) | Not recommended |
| 100ΞΌs delay | Fast | High | Standard usage |
| 500ΞΌs delay | Moderate | Very High | Stress testing |
| With ACK | Slower | Maximum | Production-grade |
- Minimal delay - Using
usleep(100)balances speed and reliability - Acknowledgment system (Bonus) - Guarantees no data loss
- Efficient bit operations - Direct bit manipulation without conversion
- Signal masking - Preventing signal interruption during critical sections
β
Empty strings
β
Very long messages (10,000+ characters)
β
Special characters and symbols
β
Null bytes in the middle of messages
β
Unicode and multi-byte characters (bonus)
β
Multiple concurrent clients (bonus)
β
Server receiving signals while processing
β
Client terminating unexpectedly
Grade: 100/100 β
Evaluation Date: [N/A]
Peer Review Highlights:
- Clean, efficient implementation
- No global variables used
- Proper error handling
- Fast and reliable transmission
- Bonus: Perfect acknowledgment system
- Handles Unicode correctly
This project builds upon:
- Libft - Used for string operations if needed
This project prepares for:
- Philosophers - Process synchronization concepts
- pipex - More advanced IPC with pipes
- minishell - Signal handling in shell environment
Solution: Increase usleep() delay in client or implement acknowledgment system (bonus)
Solution: Use sigaction() instead of signal() and ensure proper signal masking
Solution: Implement proper UTF-8 byte sequence handling (requires bonus)
This project is part of the 42 School curriculum. Feel free to reference this code for learning purposes, but please complete your own 42 projects independently to get the full educational benefit.
This is a completed school project, but feedback and suggestions are always welcome! If you found an interesting optimization or have questions about the implementation, feel free to reach out.
Author: Alex Tanvuia (Ionut Tanvuia)
42 Login: itanvuia
School: 42 London
Project Completed: [December 2025]
Exploring the fundamentals of Inter-Process Communication through UNIX signals. Check out my other projects on my GitHub profile!
| Function | Purpose |
|---|---|
signal() |
Simple signal handler (not recommended) |
sigaction() |
Advanced signal handler with more control |
kill() |
Send signal to a process |
getpid() |
Get current process ID |
pause() |
Wait for signal |
usleep() |
Microsecond delay |
| Signal | Value | Purpose in Minitalk |
|---|---|---|
| SIGUSR1 | 10 | Represents binary 0 |
| SIGUSR2 | 12 | Represents binary 1 |
Client: "Hi" β [72, 105] β [01001000, 01101001]
β
Server: Receives 16 signals (8 bits Γ 2 chars)
β
Server: Reconstructs "Hi"
β
Server: Prints "Hi\n"