Skip to main content

Overview

The client is the sending component of the Minitalk communication system. It converts a string message into individual bits and transmits each bit to the server using UNIX signals (SIGUSR1 for 0, SIGUSR2 for 1).

Source Code Location

client.c - Lines 1-93

Architecture

The client follows a straightforward transmission pipeline:
  1. Validates command-line arguments (PID and message)
  2. Converts PID string to integer using ft_atoi
  3. Sends the message character-by-character via send_message
  4. Each character is transmitted as 8 individual signals
  5. Returns status code based on success or failure

PID Parsing Function

ft_atoi()

int	ft_atoi(const char *str)
{
	int	i;
	int	sign;
	int	num;

	i = 0;
	sign = 1;
	num = 0;
	while (str[i] == ' ' || (str[i] >= 9 && str[i] <= 13))
		i++;
	if (str[i] == '+' || str[i] == '-')
	{
		if (str[i] == '-')
			sign = -1;
		i++;
	}
	while (str[i] != '\0' && str[i] >= '0' && str[i] <= '9')
	{
		num = (num * 10) + (str[i] - '0');
		i++;
	}
	return (sign * num);
}
Lines: 17-40 Converts a string representation of a number to an integer. Used to parse the server PID from command-line arguments.
str
const char*
required
The string to convert to an integer. Expected to be a valid PID in string format.
return
int
The parsed integer value, with sign applied if present.

Parsing Algorithm

1

Skip Whitespace

while (str[i] == ' ' || (str[i] >= 9 && str[i] <= 13))
    i++;
Skips leading whitespace characters:
  • Space ( ) = ASCII 32
  • Tab (\t) = ASCII 9
  • Newline (\n) = ASCII 10
  • Vertical tab (\v) = ASCII 11
  • Form feed (\f) = ASCII 12
  • Carriage return (\r) = ASCII 13
2

Handle Sign

if (str[i] == '+' || str[i] == '-')
{
    if (str[i] == '-')
        sign = -1;
    i++;
}
Detects optional + or - sign. Defaults to positive (sign = 1).
3

Convert Digits

while (str[i] != '\0' && str[i] >= '0' && str[i] <= '9')
{
    num = (num * 10) + (str[i] - '0');
    i++;
}
Iterates through digit characters, converting each to its numeric value:
  • Multiplies current number by 10 (shift decimal place)
  • Adds the digit value (ASCII ‘0’ = 48, so '5' - '0' = 5)
4

Apply Sign and Return

return (sign * num);
Multiplies the result by the sign and returns.

Example Conversion

StepCharacterisignnumAction
1 010Skip whitespace
2 110Skip whitespace
3-2-10Set sign to -1
413-11num = (0 * 10) + 1
524-112num = (1 * 10) + 2
635-1123num = (12 * 10) + 3
746-11234num = (123 * 10) + 4
8\07-11234Stop and return
Final result: -1 * 1234 = -1234

Message Transmission Function

send_message()

int	send_message(unsigned int pid, unsigned char *str)
{
	int	bits;
	int	i;

	i = 0;
	while (str[i])
	{
		bits = 8;
		while (--bits >= 0)
		{
			if ((str[i] >> bits) & 1)
			{
				if (kill(pid, SIGUSR2) == -1)
					return (1);
			}
			else
			{
				if (kill(pid, SIGUSR1) == -1)
					return (1);
			}
			usleep(700);
		}
		i++;
	}
	return (0);
}
Lines: 42-68 Transmits a string message to the server by sending each character bit-by-bit using UNIX signals.
pid
unsigned int
required
The Process ID of the server to send signals to. Must be a valid running process ID.
str
unsigned char*
required
The message string to transmit. Each character will be sent as 8 bits (most significant bit first).
return
int
  • 0 on successful transmission
  • 1 if any kill() call fails

Bit Extraction Logic

This expression extracts individual bits from a character, starting from the most significant bit (MSB).

Breakdown:

Right Shift Operation: str[i] >> bits
  • Shifts all bits in the character to the right by bits positions
  • Higher-order bits are filled with 0
  • Lower-order bits are discarded
Bitwise AND with 1: ... & 1
  • Masks all bits except the least significant bit (LSB)
  • Result is either 0 or 1

Example: Sending ‘A’ (ASCII 65 = 0b01000001)

char c = 'A';  // 0b01000001 = 65
Iterationbitsc >> bitsBinary Result& 1Signal Sent
1765 >> 70b000000000SIGUSR1
2665 >> 60b000000011SIGUSR2
3565 >> 50b000000100SIGUSR1
4465 >> 40b000001000SIGUSR1
5365 >> 30b000010000SIGUSR1
6265 >> 20b000100000SIGUSR1
7165 >> 10b001000000SIGUSR1
8065 >> 00b010000011SIGUSR2
The bits are sent MSB-first (left to right): 0, 1, 0, 0, 0, 0, 0, 1

Signal Transmission

if ((str[i] >> bits) & 1)
{
    if (kill(pid, SIGUSR2) == -1)
        return (1);
}
else
{
    if (kill(pid, SIGUSR1) == -1)
        return (1);
}
usleep(700);

Bit = 1

Sends SIGUSR2 to represent binary 1

Bit = 0

Sends SIGUSR1 to represent binary 0

kill() System Call

kill
int (pid_t pid, int sig)
Sends a signal to a process or group of processes.Parameters:
  • pid: Target process ID
  • sig: Signal number (SIGUSR1 or SIGUSR2)
Returns:
  • 0 on success
  • -1 on error (sets errno)
If kill() returns -1, the function immediately returns 1 to indicate failure. Common reasons for failure:
  • Invalid PID (process doesn’t exist)
  • Insufficient permissions
  • Signal delivery failure

Timing Delay

usleep(700);
700 microseconds delay between each signal transmission. This prevents signal queue overflow and ensures the server has time to process each bit before the next arrives.
usleep
int (useconds_t usec)
Suspends execution for usec microseconds. 700µs = 0.7 milliseconds.

Main Function

main()

int	main(int argc, char **argv)
{
	int	j;

	j = 0;
	if (argc != 3 || !argv[2])
		return (ft_printf("Usage : ./client <PID> <string to send>\n"), 1);
	while (argv[1][j] != '\0')
	{
		if (argv[1][j] < '0' || argv[1][j] > '9')
		{
			ft_printf("Wrong PID\n");
			return (1);
		}
		j++;
	}
	if (send_message(ft_atoi(argv[1]), (unsigned char *)argv[2]))
	{
		ft_printf("Client failed sending signal\n");
		return (1);
	}
	return (0);
}
Lines: 70-92
argc
int
required
Argument count. Must be exactly 3 (program name, PID, message).
argv
char**
required
Argument vector:
  • argv[0]: Program name (”./client”)
  • argv[1]: Server PID as string
  • argv[2]: Message to send
return
int
  • 0 on successful message transmission
  • 1 on any validation or transmission error

Validation and Execution Flow

1

Argument Count Validation

if (argc != 3 || !argv[2])
    return (ft_printf("Usage : ./client <PID> <string to send>\n"), 1);
Checks:
  • Exactly 3 arguments provided
  • The message string (argv[2]) is not NULL
Returns usage message and exits with code 1 on failure.
2

PID Format Validation

while (argv[1][j] != '\0')
{
    if (argv[1][j] < '0' || argv[1][j] > '9')
    {
        ft_printf("Wrong PID\n");
        return (1);
    }
    j++;
}
Validates that the PID string contains only digits (0-9). This ensures the PID is numeric before attempting conversion.
This validation does NOT check if the PID belongs to a running process. That validation occurs when kill() is called.
3

Message Transmission

if (send_message(ft_atoi(argv[1]), (unsigned char *)argv[2]))
{
    ft_printf("Client failed sending signal\n");
    return (1);
}
Calls send_message() with:
  • Converted PID from ft_atoi(argv[1])
  • Message string cast to unsigned char*
If send_message() returns non-zero (failure), displays error and exits.
4

Success

return (0);
Returns 0 to indicate successful transmission of all characters.

Error Handling Summary

Invalid Arguments

Exit Code: 1
Message: “Usage : ./client <PID> <string to send>”

Non-Numeric PID

Exit Code: 1
Message: “Wrong PID”

Signal Send Failure

Exit Code: 1
Message: “Client failed sending signal”

Success

Exit Code: 0
Message: (none)

Dependencies

Included Headers

#include "../libft/ft_printf.h"
#include <signal.h>
#include <time.h>

ft_printf.h

Custom printf for error messages and output

signal.h

Signal constants and kill() function

time.h

Time-related functions (for usleep() timing)

System Calls Used

kill
int (pid_t pid, int sig)
Sends signals to the server process. Core mechanism for bit transmission.
usleep
int (useconds_t usec)
Introduces timing delays between signal transmissions to prevent queue overflow.

Execution Flow Diagram


Usage Examples

./client 12345 "Hello World"

Performance Characteristics

Transmission Rate

Speed: Approximately 1,429 bits per second
  • 700µs per bit
  • 8 bits per character
  • ~5.6ms per character
  • ~178 characters per second
The 700µs delay is a balance between speed and reliability. Shorter delays risk signal loss; longer delays reduce throughput.

Key Design Decisions

The bits are sent from most significant to least significant (left to right). This matches:
  • Human reading order
  • Network byte order conventions
  • Simpler reconstruction logic on the server side (always left-shift)
send_message(ft_atoi(argv[1]), (unsigned char *)argv[2])
Using unsigned char* ensures:
  • Bit operations work correctly (no sign extension)
  • All 8 bits are treated as data (0-255 range)
  • Consistent behavior across all character values
while (argv[1][j] != '\0')
{
    if (argv[1][j] < '0' || argv[1][j] > '9')
        // Error
}
Early validation prevents:
  • ft_atoi parsing garbage values
  • Wasted signal transmission attempts
  • Clearer error messages to the user

Build docs developers (and LLMs) love