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

问题描述 投票:0回答:1

我正在构建一个客户端服务器通信项目。 客户端将

char *
字符串编码为位并将其发送到服务器。
0
使用
SIGUSR1
发送到服务器,
1
使用
SIGUSR2

发送到服务器

服务器每次收到信号时,都会将

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()
功能中添加更多时间,但这并没有阻止此错误的发生

每次出现bug,服务器端的

buffer
仍然有一些内容,导致下一次调用
./client
不起作用,并且正在打印一些不可打印的字符

c signals
1个回答
0
投票

在信号处理程序中执行的任何操作都要小心。它可以随时中断您执行任何指令的线程。当调用信号处理程序内的任何函数时,必须确保该函数是可重入的。主程序可能在信号处理程序之前进入它。如果它确实破坏了某些全局状态或可能持有锁,那么您就有麻烦了。

如果这是特定于 Linux 的解决方案,我建议您使用

signalfd
来编写它。您可以通过这样的文件描述符安全同步地接收信号。

© www.soinside.com 2019 - 2024. All rights reserved.