我正在构建一个客户端服务器通信项目。 客户端将
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
不起作用,并且正在打印一些不可打印的字符
在信号处理程序中执行的任何操作都要小心。它可以随时中断您执行任何指令的线程。当调用信号处理程序内的任何函数时,必须确保该函数是可重入的。主程序可能在信号处理程序之前进入它。如果它确实破坏了某些全局状态或可能持有锁,那么您就有麻烦了。
signalfd
来编写它。您可以通过这样的文件描述符安全同步地接收信号。