6
\$\begingroup\$

For school, I have to create a server-client communication through Unix signals and only with SIGUSR1 and SIGUSR2.


Client program

#include "common.h"

int s_received = 0;

int ft_atoi(const char *nptr)
{
    int i;
    int sign;
    int res;

    i = 0;
    sign = 1;
    res = 0;
    while (nptr[i] == ' ' || (nptr[i] >= 9 && nptr[i] <= 13))
        i++;
    if (nptr[i] == '+' || nptr[i] == '-')
    {
        if (nptr[i] == '-')
            sign = -1;
        i++;
    }
    while (nptr[i] >= '0' && nptr[i] <= '9')
    {
        res = res * 10 + (nptr[i] - '0');
        i++;
    }
    return (res * sign);
}

Then in handle_char, I do a binary operation on every bit to send either a 1 or a 0 to the server, using kill function with SIGUSR1 and SIGUSR2. After each signal is sent, the process waits for a server response before it continues:

void handle_char(int pid, unsigned char c)
{
    int i;
    unsigned char mask;

    i = 7;
    mask = 1u << i;
    while (mask)
    {
        s_received = 0;
        if (mask & c)
            kill(pid, SIGUSR1);
        else
            kill(pid, SIGUSR2);
        if (!s_received)
            pause();
        i--;
        mask >>= 1;
    }
}

Then I handle my string to process each char of my string, through the handle_char function.

void handle_string(int pid, char *msg)
{
    int i;

    i = -1;
    while (msg[++i])
        handle_char(pid, msg[i]);
    handle_char(pid, 0);
}

When a signal is received from the server, this function is called either to put my global variable to 1, so I can send the next bit, or sending me the final signal meaning I have sent all of my chars to the server and the server has received them.

void handler(int signal, siginfo_t *client, void *ucontext)
{
    (void)ucontext;
    if (signal == SIGUSR1)
        s_received = 1;
    else if (signal == SIGUSR2)
    {
        ft_putstr_fd("Message succesfully sent to the server, Congrats !", 1);
        exit(EXIT_SUCCESS);
    }
}

First I take user input string and server PID:

int main(int argc, char *argv[])
{
    pid_t pid;
    struct sigaction sig;

    if (argc != 3)
    {
        ft_putstr_fd("Usage : ./client <pid> <message_to_send>\n", 1);
        exit(EXIT_FAILURE);
    }
    sig.sa_flags = SA_SIGINFO | SA_RESTART;
    sig.sa_sigaction = handler;
    sigemptyset(&(sig.sa_mask));
    sigaction(SIGUSR2, &sig, NULL);
    sigaction(SIGUSR1, &sig, NULL);
    pid = ft_atoi(argv[1]);
    if (!pid)
    {
        ft_putstr_fd("PID of , is not possible..., please stop joking enter the real value!\n", 1);
        exit(EXIT_FAILURE);
    }
    handle_string(pid, argv[2]);
}

Server program

#include "common.h"

//int f_msg = 1;

Only checking if the sender process is still ongoing:

void is_process_running(pid_t pid)
{
    if (kill(pid, 0) < 0)
    {
        ft_putstr_fd(("An error occured during the process, exiting...\n"), 1);
        exit(EXIT_FAILURE);
    }
}

Here each time a signal is received from a client, I stack into my unsigned char variable, when my shift value is negative it means that I received a full char so now I can print it and reset the shift value. Each time a signal is received a signal is sent back to confirm this.

void handler(int signal, siginfo_t *client, void *ucontext)
{
    (void)ucontext;
    static int shift = -1;
    static unsigned char c;

    is_process_running(client->si_pid);
    /*if (f_msg)
    {
        ft_putstr_fd("\nMessage from process ", 1);
        ft_putnbr_fd((int)client->si_pid, 1);
        ft_putstr_fd(": ", 1);
        f_msg = 0;
    }*/
    if (shift < 0)
        shift = 7;
    if (signal == SIGUSR1)
        c |= (1 << shift);
    shift--;
    if (shift < 0 && c)
    {
        ft_putchar_fd(c, 1);
        c = 0;
    }
    else if (shift < 0 && !c)
        kill(client->si_pid, SIGUSR2);
        //f_msg = 1;
    kill(client->si_pid, SIGUSR1);
}

Printing sever pid and initializing signal handler:

int main(int argc, char *argv[])
{
    int i;
    pid_t pid;
    SA sig;

    sig.sa_flags = SA_SIGINFO | SA_RESTART;
    sig.sa_sigaction = handler;
    sigemptyset(&(sig.sa_mask));
    pid = getpid();
    ft_putstr_fd("Server PID is : ", 1);
    ft_putnbr_fd(pid, 1);
    ft_putchar_fd('\n', 1);
    sigaction(SIGUSR2, &sig, NULL);
    sigaction(SIGUSR1, &sig, NULL);
    while (1)
        sleep(1);
}

and here is my header:

#ifndef COMMON_H
#define COMMON_H

#define SA struct sigaction
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>

void ft_putchar_fd(char c, int fd);
void ft_putnbr_fd(int n, int fd);
void ft_putstr_fd(char *s, int fd);
#endif

ft_putchar :

void    ft_putchar_fd(char c, int fd)
{
    write(fd, &c, 1);
}

My code works well. So I'd like some review of improvement that could be made.

I'm actually surprised that code supports Unicode characters as I have not made any changes to make that happen. For example, when sending "hello 😃" from client, the server is able to print the "😃".

\$\endgroup\$

1 Answer 1

7
\$\begingroup\$

Why Unicode works

The reason why unicode works is that UTF-8 encoding uses one or more chars per unicode character. Thus, UTF-8 encoded strings fit into a C string, and C strings are agnostic about what kind of encoding is used.

Use atoi() from the standard library

Instead of implementing your own version of atoi(), why not use the one from the standard C library? I see other ft_*() functions, but since you already depend on the standard library (since you #include <stdlib.h> and use POSIX functions that are part of the standard library you are using), there is no reason to roll your own.

Don't assume pause() returns only if your desired signals are received

Consider that signals from other processes might be received. I would use a while-loop to make sure s_received is set:

while (!s_received)
    pause();

Optimizing data transfer via signals

Using SIGUSR1 and SIGUSR2 allows you to send only one bit at a time, and you have to wait for a return signal to indicate it was received. You could use more signals to send a few more bits at a time, but that's about it.

However, POSIX.1b introduced the concept of real-time signals. There are at least 8 different real-time signal numbers, but operating systems can supply more, and Linux supplies more than 32, so you can send 5 bits at a time. It gets better though: real-time signals have a guaranteed delivery order, and two identical signals sent in quick succession will never get merged into one upon delivery, unlike the normal signals. This means you don't have to wait for the signal to be received before sending another one (at least, not until the signal delivery queue is full).

Finally, real-time signals allow you to add a small payload if you use sigqueue(): either an int or a void*. On 64-bit systems, this means you can send 8 whole bytes along with a single signal, greatly reducing the amount of signals you need to send a string.

\$\endgroup\$
6
  • \$\begingroup\$ Thanks for your reply, and I have change the if statement for the while, the reason why i don't use atoi() is because it's a school project where I only have the right to use self-made function. \$\endgroup\$
    – dieri
    Oct 24, 2022 at 18:38
  • 1
    \$\begingroup\$ And for signals, I only have the right to use these two signal so that's why I use them, and I am only allowed to use either sigaction or signal and kill function \$\endgroup\$
    – dieri
    Oct 24, 2022 at 18:39
  • \$\begingroup\$ do you have ressources about the answer you gave me for utf-8, so i can expand my knowledge on the subject to really understand what happened ? \$\endgroup\$
    – dieri
    Oct 24, 2022 at 18:40
  • 1
    \$\begingroup\$ In my answer I linked to the Wikipedia article about UTF-8. In the second paragraph it mentions: “It was designed for backward compatibility with ASCII”. There's lots of information about how UTF-8 works here, and there are links at the end for more information. \$\endgroup\$
    – G. Sliepen
    Oct 24, 2022 at 19:00
  • 1
    \$\begingroup\$ If the school gave you these restrictions, then of course your code is fine! I'm just mentioning these things because others might also read this page and try to learn from it, and it might be good to warn about things that should not be copied blindly. It's an interesting school you go to if they have this assignment about communicating via UNIX signals :) \$\endgroup\$
    – G. Sliepen
    Oct 24, 2022 at 19:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.