在使用多进程 TCP 通信过程中 , 父进程接收不到子进程退出的信号SIGCHLD,没有调用
recycleChild函数,想知道如何才能让父进程接收到信号。
子进程使用ctrl+c 结束进程时可以接收到SIGINT信号 ,并调用exit(-1) 退出
如图:子进程结束时 父进程没有调用recycleChild 函数处理信号
Server
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <wait.h>
#define SERVERIP "127.0.0.1"
#define PORT 6789
void recycleChild(int arg)
{
// 写 while 是为了处理多个信号
printf("接收信号\n");
while (1)
{
int ret = waitpid(-1, NULL, WNOHANG);
if (ret == -1)
{
// 所有子进程都回收了
break;
}
else if (ret == 0)
{
// 还有子进程活着
break;
}
else
{
// 回收子进程
printf("子进程 %d 被回收了\n", ret);
}
}
}
int main()
{
// 注册信号捕捉
struct sigaction act;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
act.sa_handler = recycleChild;
sigaction(SIGCHLD, &act, NULL);
// 1. 创建 socket(用于监听的套接字)
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd == -1)
{
perror("socket");
exit(-1);
}
// 2. 绑定
struct sockaddr_in server_addr;
server_addr.sin_family = PF_INET;
// 点分十进制转换为网络字节序
inet_pton(AF_INET, SERVERIP, &server_addr.sin_addr.s_addr);
// 服务端也可以绑定 0.0.0.0 即任意地址
// server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
int ret = bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (ret == -1)
{
perror("bind");
exit(-1);
}
// 3. 监听
ret = listen(listenfd, 8);
if (ret == -1)
{
perror("listen");
exit(-1);
}
// 不断循环等待客户端连接
while (1)
{
// 4. 接收客户端连接
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int connfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_addr_len);
if (connfd == -1)
{
// 用于处理信号捕捉导致的 accept: Interrupted system call
if (errno == EINTR)
{
continue;
}
perror("accept");
exit(-1);
}
pid_t pid = fork();
if (pid == 0)
{
// 子进程
// 输出客户端信息,IP 组成至少 16 个字符(包含结束符)
char client_ip[16] = {0};
inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, client_ip, sizeof(client_ip));
unsigned short client_port = ntohs(client_addr.sin_port);
printf("ip:%s, port:%d\n", client_ip, client_port);
// 5. 开始通信
// 服务端先接收客户端信息,再向客户端发送数据
// 接收数据
char recv_buf[1024] = {0};
while (1)
{
ret = read(connfd, recv_buf, sizeof(recv_buf));
if (ret == -1)
{
perror("read");
exit(-1);
}
else if (ret > 0)
{
printf("recv client data : %s\n", recv_buf);
}
else
{
// 表示客户端断开连接
printf("client closed...\n");
// 退出循环,用来解决 出现两次 client closed...
break;
}
// 发送数据
char *send_buf = "hello, i am server";
// 粗心写成 sizeof,那么就会导致遇到空格终止
write(connfd, send_buf, strlen(send_buf));
}
// 关闭文件描述符
close(connfd);
}
}
close(listenfd);
return 0;
}
Clinet
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <wait.h>
#define SERVERIP "127.0.0.1"
#define PORT 6789
void recycleChild(int arg)
{
printf("子进程exit 退出\n");
exit(-1);
}
int main()
{
struct sigaction act;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
act.sa_handler = recycleChild;
sigaction(SIGINT, &act, NULL);
// 1. 创建 socket(用于通信的套接字)
int connfd = socket(AF_INET, SOCK_STREAM, 0);
if (connfd == -1)
{
perror("socket");
exit(-1);
}
// 2. 连接服务器端
struct sockaddr_in server_addr;
server_addr.sin_family = PF_INET;
inet_pton(AF_INET, SERVERIP, &server_addr.sin_addr.s_addr);
server_addr.sin_port = htons(PORT);
int ret = connect(connfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (ret == -1)
{
perror("connect");
exit(-1);
}
// 3. 通信
char recv_buf[1024] = {0};
while (1)
{
// 发送数据
char *send_buf = "client message";
// 粗心写成 sizeof,那么就会导致遇到空格终止
write(connfd, send_buf, strlen(send_buf));
// 休眠的目的是为了更好的观察,此处使用 sleep 语句会导致 read: Connection reset by peer
// sleep (1);
// 接收数据
ret = read(connfd, recv_buf, sizeof(recv_buf));
if (ret == -1)
{
perror("read");
exit(-1);
}
else if (ret > 0)
{
printf("recv server data : %s\n", recv_buf);
}
else
{
// 表示服务器端断开连接
printf("client closed...\n");
break;
}
// 休眠的目的是为了更好的观察,放在此处可以解决 read: Connection reset by peer 问题
sleep(1);
}
// 关闭连接
close(connfd);
return 0;
}