This project is a simple client-server communication system implemented using signals in C. The client sends a message to the server, and the server receives and displays the message character by character.
- Clone the repository:
git clone https://github.com/leogaudin/minitalk.git
- Compile the source code using the provided Makefile:
make
This will generate two executables:
client
andserver
.
- Launch the server by executing the
server
binary:
./server
The server will display its process ID (PID) on the console.
- In a separate terminal window, launch the client by executing the client binary with the server's PID and the message you want to transmit:
./client <server_pid> <message>
Replace
<server_pid>
with the PID displayed by the server, and<message>
with the text you want to send.
Example:
./client 12345 Hello
This will send the message "Hello" to the server with the PID 12345.
- The server will receive the message and display it on the console:
Hello
The server will display each character as it receives them.
- You can repeat steps 4 and 5 to send multiple messages to the server.
The communication between the client and server is achieved using signals.
The client converts each character of the message into 8 bits (1 byte) and sends them to the server one by one.
- The client reads the server's PID and the message from the command-line arguments.
- It iterates over each character in the message.
- For each character, it calls the
send_signal
function, which sends 8 signals to the server. - The
send_signal
function converts the character into 8 bits and sends each bit as a signal to the server. It usesSIGUSR1
to represent a 1 bit andSIGUSR2
to represent a 0 bit. - After sending each bit, the client waits for a short duration (42 microseconds) using the
usleep
function. This delay allows the server to process the received signals.
- The server starts by displaying its PID on the console.
- It sets up signal handlers for
SIGUSR1
andSIGUSR2
signals using thesigaction
function. - The server enters an infinite loop and waits for signals using the
pause
function. - When a signal is received, the corresponding signal handler (
handle_signal
) is called. - The
handle_signal
function extracts the bit value (0 or 1) from the received signal and appends it to the current character being received. - If the character is fully received (8 bits), it is displayed on the console using
ft_printf
. The bit index and current character are reset to prepare for the next character. - After processing the received signal, the server sends a signal back to the client. If the received signal was SIGUSR1, it sends SIGUSR1 to acknowledge the received bit. If the received signal was SIGUSR2, it sends SIGUSR2.
The provided source code includes a bonus version (client_bonus.c
and server_bonus.c
) that adds additional functionality:
- Unicode characters like 🦁 are already supported in the mandatory part.
- Sending back a read receipt can be achieved by using the
sigaction
structure, that allows to access metadata about signal transmission.- The client displays a message on the console for each bit it sends. It prints "Received bit 1" for
SIGUSR1
and "Received bit 0" forSIGUSR2
. This can help visualize the communication process. - The server sends an acknowledgment signal back to the client after receiving each bit. If the received signal was
SIGUSR1
, it sendsSIGUSR1
to the client. If the received signal wasSIGUSR2
, it sendsSIGUSR2
. This allows the client to know that the server has successfully received the bit. These additional features enhance the interactivity and feedback during the communication process.
- The client displays a message on the console for each bit it sends. It prints "Received bit 1" for
void send_signal(int pid, unsigned char character)
-
Uses an intermediate
temp_char
buffer to read every bit of the character. -
At every iteration,
temp_char
is assigned the value ofcharacter
shifted to the righti
times.-
Let's say we want to read 01100001 bit by bit, starting from the Most Significant Bit (left)
-
We first shift the value of
character
to the right 7 times, which gives us 00000000.
⚠️ Note: the Least Significant Bit (right) determines if a number is odd or even. If the LSB is 1, the number is odd. If the LSB is 0, the number is even.-
We then calculate
temp_char % 2
, which reveals if the number is odd (LSB = 1) or even (LSB = 0). -
In this case,
temp_char % 2
is 0, so we send aSIGUSR2
signal to the server, which will interpret it as a 0 bit. -
We then shift the value of
character
to the right 6 times, which gives us 00000001. -
In this case,
temp_char % 2
is 1, so we send aSIGUSR1
signal to the server, which will interpret it as a 1 bit. -
We repeat this process until we have sent all 8 bits.
-
void handle_signal(int signal)
-
Uses two static variables:
static unsigned char current_char; static int bit_index;
-
The use of static variables allows us to keep track of the information throughout the program's execution.
-
current_char
stores the current character being received. -
bit_index
stores the index of the current bit being received.
-
-
The following line extracts the bit value (0 or 1) from the received signal in a very condensed way:
current_char |= (signal == SIGUSR1);
-
The result of
(signal == SIGUSR1)
will either be 00000000 for false or 00000001 for true. -
The
|=
operator is a bitwise OR assignment operator. It compares two bytes in their binary representation, and for every bit, assigns 0 if both bits are 0, and 1 otherwise. -
Example:
00101100 current_char 00000001 result of (sigsent == SIGUSR1) -------- 00101101 result stored in message after the bitwise OR operation
-
Repeatingly doing this and shifting it to the left will allow us to store the entire character in
current_char
.
-
🙇🏻♂️ This project is largely based on the work and explanations of ealgar-c.
Link to ealgar-c/minitalk🗣️