This project has been created as part of the 42 curriculum by maaugust.
Minitalk is a project in the 42 curriculum. The purpose of this project is to code a small data exchange program using UNIX signals.
We are tasked with creating a communication program in the form of a client and a server. The client must send a specified string to the server, which must then receive and display the string without delay. To make things interesting, communication between the client and server must exclusively use UNIX signals, specifically only SIGUSR1 and SIGUSR2.
- Sequential Processing: The server can receive strings from several clients in a row without needing to restart.
- Speed: The program parses and prints bits rapidly, ensuring large strings (100+ characters) are displayed in under a second.
- Robust Error Handling: The program handles errors thoroughly and will not quit unexpectedly (no segmentation faults, bus errors, or double frees).
- Ping-Pong Acknowledgment: The server acknowledges every single received message by sending a signal back to the client.
- Unicode Support: Natively supports and prints Unicode characters.
Since we are only allowed to use two signals (SIGUSR1 and SIGUSR2), we cannot send standard char data types directly. Instead, the client translates every character of the string into its 8-bit binary representation.
- It sends
SIGUSR1to represent a1. - It sends
SIGUSR2to represent a0.
The server catches these signals one by one, shifts the bits back into an 8-bit unsigned char (to avoid sign-extension issues), and prints the reconstructed byte to the standard output. Because it prints raw bytes, UTF-8/Unicode characters are implicitly supported in both versions!
- Mandatory Approach: The client paces its signal transmission using
usleep()between each bit to prevent overwhelming the server's signal queue and dropping bits. - Bonus Approach (Ping-Pong): To achieve flawless transmission without relying on arbitrary sleep times, the bonus client utilizes a single global variable as a lock, pausing the client loop until an acknowledgment signal (
SIGUSR1) is received from the server before dispatching the next bit.
The project subject permits the use of one global variable per program, provided its use is strictly justified. In the bonus client, volatile sig_atomic_t g_ack; is used. Here is the technical justification:
sig_atomic_t: This integer type guarantees that reading and writing to the variable happens in a single, atomic instruction. This prevents a scenario where the main program is halfway through reading the variable when a signal interrupts it and changes the value, which would result in corrupted or "half-read" data.volatile: This keyword tells the compiler that the variable's value can change at any time asynchronously (outside the normal flow of the code, such as inside a signal handler). Withoutvolatile, an optimizing compiler might look at thewhile (g_ack == PAUSE)loop, assumeg_ackis never modified inside that loop, and aggressively compile it into an infinite loop.volatileforces the program to fetch the actual, most up-to-date value from memory on every single iteration.
To compile the project, run the following commands in the root of the repository.
- To build the mandatory executables (
clientandserver):
make- To build the bonus executables with acknowledgment features (
client_bonusandserver_bonus):
make bonusmake clean: Removes the compiled object files (.o).make fclean: Removes object files and all executables.make re: Performs a clean re-build.
1. Start the Server: The server must be started first. Upon launch, it will print its PID.
./server
# OR for the bonus version:
./server_bonusOutput:
The process ID is: 12345.
2. Run the Client: In a separate terminal window, run the client by passing the server's PID and the string you wish to send.
./client 12345 "Hello, 42!"
# OR for the bonus version:
./client_bonus 12345 "Hello, 42!"The server terminal will immediately print Hello, 42!. If using the bonus version, the client terminal will also print a success message upon full acknowledgment.
If you are testing the mandatory version of this project on a slow Virtual Machine (VM) or Windows Subsystem for Linux (WSL), you might see random garbage characters printed to the terminal.
The 42 subject requires that displaying 100 characters must take less than 1 second. However, Linux does not queue pending signals of the same type. In the mandatory version, the client relies on a hardcoded usleep() delay to pace the signals.
- If the sleep time is too low, the VM's OS scheduler fails to context-switch fast enough, the server drops the incoming signal, and the bits become permanently misaligned (resulting in garbage data).
- If the sleep time is too high, the program avoids garbage data but fails the strict speed requirement.
The Solution: The usleep value in the mandatory client.c is optimized for bare-metal campus machines (like the 42 iMacs). If you test this on a slow personal machine and get garbage output, simply increase the usleep delay in client.c to account for your hypervisor's latency. Alternatively, test the Bonus versionโits ping-pong acknowledgment system dynamically adapts to the CPU's exact speed, completely eliminating this hardware bottleneck!
A common debate among 42 students is whether the server should print characters one-by-one as they arrive, or buffer the entire string and print it all at once at the very end. This implementation intentionally streams the output character-by-character. Here is why:
- "Without Delay": The subject requires the server to display the string "without delay". Waiting a full minute to receive a massive string before printing anything looks like a frozen program or a crash. Streaming characters provides real-time, live feedback of the communication.
- The
mallocTrap: The server has no prior knowledge of the incoming message's length. To buffer the string, it would require continuous, dynamicmallocandfreeoperations inside a signal handler as the buffer expands. This introduces severe performance overhead and massive memory leak risks. By streaming directly viawrite(), this implementation maintains a memory footprint of effectively zero bytes and avoids the dynamic memory trap entirely.
Minitalk can be deceivingโit might perfectly transmit a small string like "Hello" but completely break, crash, or print random garbage data when handling massive strings or complex characters due to signal dropping or race conditions. Rigorous testing is essential.
1. Unicode & Emoji Stress Test Because this implementation passes raw bytes, it natively supports multi-byte Unicode characters. Try sending complex emojis to ensure bits aren't being misaligned:
./client_bonus 12345 "๐ฅ ๐พ 42 Porto is awesome! ๐พ ๐ฅ"2. Third-Party Testers To ensure your Minitalk output is correct every single time without dropping signals in the middle of a massive transmission, highly recommend running your code through these community testers:
- ThibaudM13 / minitalk-Tester - Excellent for automated stress testing.
- MalwarePup / minitalk_tester - Great for catching edge cases and checking transmission speeds.
Moulinette relies on a program called norminette to check if your files comply with the 42 Norm. Every single .c and .h file must pass this check. If there is a norm error, you will receive a 0.
The 42 Header:
Before writing any code, every file must start with the standard 42 header. norminette will automatically fail any file missing this specific signature.
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* client.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maaugust <maaugust@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/07/01 15:19:21 by maaugust #+# #+# */
/* Updated: 2025/07/09 17:18:26 by maaugust ### ########.fr */
/* */
/* ************************************************************************** */Run the following command in the root of your repository to check all your files at once:
norminette -R CheckForbiddenSourceHeader srcs/ bonus/ includes/System Manuals:
man 2 kill,man 2 signal,man 2 sigaction,man 2 pause- Essential Linux programmer's manuals for understanding system calls related to process signaling.
Articles & Guides:
- UNIX Signals Tutorial (TutorialsPoint): A great written introduction to understanding how signals and traps operate under the hood.
- 42 Minitalk Explained (Medium): A clear breakdown of the Minitalk project constraints and implementation strategies.
- Building a Simple Client-Server Communication Program in C (Medium): A practical guide on setting up the server and client architecture using bitwise operations.
- Step-by-Step Guide to Minitalk (Medium): A walkthrough of the project from basic signal handling to full string transmission.
- Minitalk 42 Project Guide (Medium): Comprehensive guide covering edge cases, the use of
sigaction, and global variables. - A Simple Client-Server Communication System (Medium): An excellent article dissecting the bit-shifting logic required to pass characters over UNIX signals.
Video Tutorials:
- CodeVault - Unix Signals: Exceptional playlist breaking down process communication and signal handling in C.
- Oceano - Minitalk Explanation: Great conceptual overview and visualization tailored specifically for this 42 project.
42 Standards:
- 42 Norm V4: The strict coding standard for 42 C projects.
- Official 42 Norminette Repository: The open-source linter enforcing the strict 42 coding standard.
In the spirit of transparency and the learning objectives of the 42 curriculum, here is how AI tools were utilized during this project:
- Tooling & Formatting: Assisted in restructuring and fixing minor variable duplication in the
Makefile, formatting thisREADME.mdlayout, and generating comprehensive Doxygen comments for the source files. - Zero Code Generation: No core logic was generated by AI. The core bitwise parsing algorithm, signal management via
sigaction, and the ping-pong acknowledgment loop were 100% manually researched and written. This guarantees a true understanding of UNIX process communication.
