server.c
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <signal.h>
#define MAXLINE 1024
// 1:
static void sig_child(int signo)
{
pid_t pid;
int status;
while( (pid=waitpid(-1, &status,WNOHANG)) > 0) //2: W NO HANG
{
printf("child terminated,pid:%d\n", pid);
if (WIFEXITED(status)) // W IF EXIT ED // 3:
{
printf("exit code:%d\n",WEXITSTATUS(status)); //4: W EXIT STATUS
}
}
}
int main()
{
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd == -1)
{
printf("socket error[%d]:%s\n", errno, strerror(errno));
exit(errno);
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(55555);
if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
{
printf("bind error[%d]:%s\n", errno, strerror(errno));
exit(errno);
}
if (listen(listen_fd, 3) == -1)
{
printf("listen error[%d]:%s\n", errno, strerror(errno));
exit(errno);
}
//5: catch child terminate signal
signal(SIGCHLD,sig_child);
while (1)
{
struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(client_addr));
int len = sizeof(client_addr);
int client_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &len);
if (client_fd == -1)
{
printf("accept error[%d]:%s\n", errno, strerror(errno));
sleep(1);
continue;
}
pid_t pid = fork();
if (pid < 0)
{
printf("fork error[%d]:%s\n", errno, strerror(errno));
continue;
}else if (pid == 0) // child process
{
close(listen_fd); //6: close no use parent fd in child
printf("child process[%d],ppid:%d\n", getpid(), getppid());
char rb[100] = {0};
int recv_len = recv(client_fd, rb, MAXLINE, 0);
if (recv_len == -1)
{
printf("recv error[%d]:%s\n", errno, strerror(errno));
exit(errno);
}
printf("recv[%d]:%s\n", recv_len, rb);
strcat(rb, ", has been received by server, the msg from server");
if (send(client_fd, rb, strlen(rb), 0) == -1)
{
printf("send error[%d]:%s\n", errno, strerror(errno));
exit(errno);
}
printf("send success\n");
close(client_fd);
exit(0); // 8
}
close(client_fd); //7: close no use child fd in parent
}
close(listen_fd);
}
1. 为注册信号SIGCHLD 提供的的处理函数
SIGCHLD:子进程结束时, 父进程会收到这个信号。如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情 况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程 来接管)。
2. 3 4
函数原型: pid_t waitpid(pid_t pid,int * status,int options);
函数说明:
如果在调用 waitpid()时子进程已经结束,则 waitpid()会立即返回子进程结束状态值。 子进程的结束状态值会由参数 status 返回。
子进程的进程识别码也会一起返回。如果不在意结束状态值,则参数 status 可以设成 NULL。
参数 pid:
为欲等待的子进程识别码,其数值意义如下:
pid<-1 等待进程组识别码为 pid 绝对值的任何子进程
pid=-1 等待任何子进程,相当于 wait()
pid=0 等待进程组识别码与目前进程相同的任何子进程
pid>0 等待任何子进程识别码为 pid 的子进程
参数status:
为返回的状态
WIFEXITED(status)如果若为正常结束子进程返回的状态,则为真;对于这种情况可执行WEXITSTATUS(status),取子进程传给exit或_eixt的低8位。
WEXITSTATUS(status)取得子进程 exit()返回的结束代码,一般会先用 WIFEXITED 来判断是否正常结束才能使用此宏。
参数options:
提供了一些额外的选项来控制waitpid,参数 option 可以为 0 或可以用"|"运算符把它们连接起来使用,比如:
WNOHANG 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID
5 signal(SIGCHLD,sig_child); 注册子进程退出信号的处理函数
如果父进程为注册处理这个信号, 则子进程执行exit后产生僵尸进程,如下
zxnms 18719 18717 0 15:53 pts/0 00:00:00 [server] <defunct>
zxnms 18721 18717 0 15:53 pts/0 00:00:00 [server] <defunct>
zxnms 18723 18717 0 15:53 pts/0 00:00:00 [server] <defunct>
父进程执行wait或waitpid处理僵尸进程。
或者只有父进程结束,子进程的僵尸进程变成孤儿进程,这些僵尸进程才会被init回收。
在信号处理函数时处理,可以不阻塞父进程的当前任务,只有子进程发出信号时才去处理。
6 子进程中关闭没用的父进程的监听socket fd
7 父进程中没用到子进程的socket fd,关闭,因为在多进程环境中,这里的close只是将引用计数减1,直到所有进程都close, 引用计数变为0时,内核才发送FIN
8 这里要直接退出了,否则子进程后面的语句也是while循环,也会执行到fork,那就不止两层了,会不停嵌套下去,变成子进程的子进程的子进程的子进程......