C中的客户端/服务器通信,带有系统信号:客户端未处理来自服务器的信号并崩溃

fnatzsnv  于 2023-10-16  发布在  其他
关注(0)|答案(1)|浏览(123)

我正在构建一个客户端服务器通信项目。客户端将char *字符串编码为比特并将其发送到服务器。使用SIGUSR10发送到服务器,使用SIGUSR21发送到服务器
每次服务器收到一个信号,它发送回SIGUSR1到客户端。一旦所有内容都收到(\0),服务器打印消息并将SIGUSR2发送给客户端。
有时,当运行代码时,客户端不处理信号,我在终端中有这个错误消息:

c4c4c2% ./client 46339 test
zsh: user-defined signal 1  ./client 46339 test

信号未被处理,进程中断,项目停止工作。
你知道为什么会这样吗

客户端代码

#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);
    }
}

我试图在客户端和服务器端的usleep()函数中添加更多的时间,但这并没有阻止这个错误的发生。
每次发生错误时,服务器端的buffer仍然有一些内容,因此下一次调用./client无法工作,并且打印了一些不可打印的字符

kgqe7b3p

kgqe7b3p1#

在信号处理器中做任何事情都要小心。它可以在任何时间和任何指令中断您的执行线程。当在信号处理程序内部和外部调用任何函数时,必须确保该函数是可重入的。主程序可能在信号处理器之前进入它。如果它与某些全局状态发生冲突或可能持有锁,那么您就有麻烦了。
如果这是一个特定于Linux的解决方案,我建议您使用signalfd编写它。您可以通过这样的文件描述符安全且同步地接收信号。
或者,POSIX提供sigtimedwaitsigwaitinfo用于同步信号传递。

相关问题