I am building a client server communication project.
The client encodes a char *
string into bits and sends it to the server. 0
s are sent to the server with SIGUSR1
and 1
s are sent to the server using SIGUSR2
Each time server receives a signals, it sends back SIGUSR1
to client. Once everything is received (\0
), server prints message and sends SIGUSR2
to the client.
Sometimes, when running the code, client does not process the signal and I have this error message in the terminal:
c4c4c2% ./client 46339 test
zsh: user-defined signal 1 ./client 46339 test
The signal not being treated, process interrupts and project stops working.
Do you know why this is happening?
#include "minitalk.h"
volatile sig_atomic_t g_ack_received = 0;
/*Waits for a acknowledgment rom the server before processing the next signal.
If no acknowledgment has been received after one second, '\1' is sent to the
server so that server resources are properly freed and the server can receive a
new message*/
void wait_for_server_ack(int pid, int delay)
{
int timeout;
int i;
timeout = 0;
while (!g_ack_received)
{
usleep(delay);
if (++timeout > 10 * delay)
{
i = 0;
while (i--)
{
if ('\1' >> i & 1)
send_signal(pid, SIGUSR2);
else
send_signal(pid, SIGUSR1);
usleep(delay);
}
exit(ft_printf_colour(RED_BOLD, TIME_OUT));
}
}
}
/*Checks that the arguments of the program are valid:
• Two arguments to the 'client' program
• First argument is numeric (PID of the 'server')
• First argument is not a protected process (PID < 1050)
• Second argument is a not empty string*/
t_bool argument_is_valid(int argc, char **argv)
{
const char *error_msg = NULL;
if (argc != 3)
error_msg = ERR_ARG_NR;
else if (!ft_isnumeric(argv[1]))
error_msg = ERR_NON_NUM_PID;
else if (ft_atoi(argv[1]) < 1050)
error_msg = PROTECTED_PID;
else if (!ft_strlen(argv[2]))
error_msg = ERR_EMPT_STR;
if (error_msg)
{
ft_printf_colour(RED_BOLD, error_msg);
return (FALSE);
}
return (TRUE);
}
/*Sends a bit encoded message to the server whose PID is 'pid' with SIGUSR1 to
represent 0 and SIGUSR2 to represent 1
Once the message is sent, 11111111 (bit representation of the NULL terminator) is
sent to the server to indicate message is over*/
void send_message(int pid, char *str)
{
int i;
char c;
while (*str)
{
i = 8;
c = *str++;
while (i--)
{
g_ack_received = 0;
if (c >> i & 1)
send_signal(pid, SIGUSR2);
else
send_signal(pid, SIGUSR1);
wait_for_server_ack(pid, 500);
}
}
i = 8;
while (i--)
{
g_ack_received = 0;
send_signal(pid, SIGUSR1);
wait_for_server_ack(pid, 500);
}
}
/*Displays a message from the client side to assess that the server did
receive the message properly*/
void handle_sigusr_client(int signum)
{
static int bit_count = 0;
if (signum == SIGUSR1)
{
bit_count++;
g_ack_received = 1;
}
if (signum == SIGUSR2)
ft_printf_colour(GREEN_LIGHT,
"Done, %d characters received by server", bit_count / 8);
}
/*Checks that the program arguments are valids and sends message to the server.
Expects a signal from the server once the message has been received*/
int main(int argc, char **argv)
{
struct sigaction sa;
if (!argument_is_valid(argc, argv))
return (1);
sa.sa_handler = handle_sigusr_client;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGUSR1, &sa, NULL) == -1
|| sigaction(SIGUSR2, &sa, NULL) == -1)
{
ft_printf(RED_BOLD, ERR_SIGAC);
return (1);
}
send_message(ft_atoi(argv[1]), argv[2]);
return (0);
}
#include "minitalk.h"
/*Checks that the message buffer can be freed, frees it and sets it to NULL*/
int free_resources(char **message)
{
if (*message)
{
free(*message);
*message = NULL;
}
return (0);
}
/*Adds the character 'c' at the end of the string pointed by 'str'. If 'str' is
not big enough to receive the new character, memory is reallocated to increase
the capacity of 'str' by BLOCK_SIZE
add_char_to_str ensures that 'str' is NULL terminated*/
void add_char_to_str(char c, char **str)
{
static int capacity = BLOCK_SIZE;
static int size = 0;
char *new_str;
if (!(*str))
{
capacity = BLOCK_SIZE;
size = 0;
*str = (char *)malloc(capacity * sizeof(char));
if (!(*str))
exit(ft_printf_colour(RED_BOLD, "%s", ERR_MALLOC));
}
if (size + 2 > capacity)
{
capacity += BLOCK_SIZE;
new_str = (char *)malloc(capacity * sizeof(char));
if (!new_str)
exit(ft_printf_colour(RED_BOLD, "%s", ERR_MALLOC));
ft_memmove(new_str, *str, size);
free(*str);
*str = new_str;
}
(*str)[size] = c;
(*str)[++size] = '\0';
}
/*Functions checks that only SIGUSR1 and SIGUSR2 are processed by the server.
It accumulates bits received by the client in a buffer int before storing each
byte in a static char * 'message'
Once a NULL terninator is received by the client, 'message' is displayed on the
standard output and memory is properly freed*/
void handle_sigusr_server(int signum, siginfo_t *info, void *context)
{
static int buffer = 0;
static int bits_received = 0;
static char *message = NULL;
(void)context;
if (signum == SIGINT)
exit(free_resources(&message));
buffer = (buffer << 1 | (signum == SIGUSR2));
if (++bits_received == 8)
{
if ((char)buffer == '\1')
free_resources(&message);
else if ((char)buffer == '\0')
{
ft_printf("%s\n", message);
free_resources(&message);
send_signal(info->si_pid, SIGUSR2);
}
else
add_char_to_str((char)buffer, &message);
buffer = 0;
bits_received = 0;
}
send_signal(info->si_pid, SIGUSR1);
}
/*Displays the PID of the server once it is launched and then waits for SIGUSR1
and SIGUSR2 from the client to display the encoded message*/
int main(void)
{
struct sigaction sa;
sa.sa_handler = 0;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handle_sigusr_server;
sigemptyset(&sa.sa_mask);
ft_printf_colour(YELLOW_BOLD, "Server PID: %i\n\n", getpid());
if (sigaction(SIGINT, &sa, NULL) == -1
|| sigaction(SIGUSR1, &sa, NULL) == -1
|| sigaction(SIGUSR2, &sa, NULL) == -1)
{
ft_printf(RED_BOLD, ERR_SIGAC);
return (1);
}
while (1)
pause();
}
#include "minitalk.h"
/*Sends the signal 'signu;' to the process whith ID 'PID' and prints the errno
and exits the process in case of failure of kill() function*/
void send_signal(pid_t pid, int signum)
{
if (kill(pid, signum) == -1)
{
ft_printf_colour(RED_BOLD, KILL_FAIL, errno);
exit (EXIT_FAILURE);
}
}
I tried to add more time in my usleep()
functions both on client and server side but it did not prevent this bug from happening
Each time a bug occurs, buffer
on server side still has some content so that the next call of ./client
does not work and some non printable characters are being printed
Be careful with anything you do inside a signal handler. It can interrupt your thread of execution at any time and any instruction. When calling any function inside the signal handler and outside, you have to be sure that this function is reentrant. The main program could have entered it before the signal handler. If it does mangle with some global state or possibly hold locks, you are in trouble.
If this is a Linux-specific solution, I would advise you to write it using signalfd
. You can receive signals safely and synchronously via such a file descriptor.
Alternatively, POSIX provides sigtimedwait
and sigwaitinfo
for synchronous signal delivery.