Skip to content

A guide for minitalk, a client-server communication system implemented in C using UNIX signals.

Notifications You must be signed in to change notification settings

leogaudin/minitalk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🗣️ minitalk

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.

wakatime

📖 Table of Contents

Usage

  1. Clone the repository:
git clone https://github.com/leogaudin/minitalk.git
  1. Compile the source code using the provided Makefile:
make

This will generate two executables: client and server.

  1. Launch the server by executing the server binary:
./server

The server will display its process ID (PID) on the console.

  1. 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.

  1. The server will receive the message and display it on the console:
Hello

The server will display each character as it receives them.

  1. You can repeat steps 4 and 5 to send multiple messages to the server.

How Does Minitalk Work?

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.

Client

  1. The client reads the server's PID and the message from the command-line arguments.
  2. It iterates over each character in the message.
  3. For each character, it calls the send_signal function, which sends 8 signals to the server.
  4. The send_signal function converts the character into 8 bits and sends each bit as a signal to the server. It uses SIGUSR1 to represent a 1 bit and SIGUSR2 to represent a 0 bit.
  5. 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.

Server

  1. The server starts by displaying its PID on the console.
  2. It sets up signal handlers for SIGUSR1 and SIGUSR2 signals using the sigaction function.
  3. The server enters an infinite loop and waits for signals using the pause function.
  4. When a signal is received, the corresponding signal handler (handle_signal) is called.
  5. The handle_signal function extracts the bit value (0 or 1) from the received signal and appends it to the current character being received.
  6. 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.
  7. 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.

Bonus Features

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" for SIGUSR2. 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 sends SIGUSR1 to the client. If the received signal was SIGUSR2, it sends SIGUSR2. 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.

Functions Explained

send_signal (client)

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 of character shifted to the right i 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 a SIGUSR2 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 a SIGUSR1 signal to the server, which will interpret it as a 1 bit.

    • We repeat this process until we have sent all 8 bits.

handle_signal (server)

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.

Credits

🙇🏻‍♂️ This project is largely based on the work and explanations of ealgar-c.

Link to ealgar-c/minitalk🗣️

About

A guide for minitalk, a client-server communication system implemented in C using UNIX signals.

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published