在研究TCP IP的11种状态之前,我们先看一下服务器中存在僵尸进程的情况。
服务器是多进程模型,客户端是单进程。
服务器程序如下:
1 #include <sys/types.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <errno.h> 7 #include <arpa/inet.h> 8 #include <sys/socket.h> 9 #include <netinet/in.h> 10 #include <sys/socket.h> 11 #include <netinet/ip.h> /* superset of previous */ 12 13 14 int main() 15 { 16 int sockfd = 0; 17 sockfd = socket(AF_INET, SOCK_STREAM, 0); 18 19 if(sockfd == -1) 20 { 21 perror("socket error"); 22 exit(0); 23 } 24 25 struct sockaddr_in addr; 26 addr.sin_family = AF_INET; 27 addr.sin_port = htons(8001); 28 inet_aton("192.168.31.128", &addr.sin_addr); 29 //addr.sin_addr.s_addr = inet_addr("192.168.6.249"); 30 //addr.sin_addr.s_addr = INADDR_ANY; 31 32 int optval = 1; 33 if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) 34 { 35 perror("setsockopt error"); 36 exit(0); 37 } 38 39 if( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 40 { 41 perror("bind error"); 42 exit(0); 43 } 44 45 if(listen(sockfd, SOMAXCONN) < 0) 46 { 47 perror("listen error"); 48 exit(0); 49 } 50 51 struct sockaddr_in peeraddr; 52 socklen_t peerlen; 53 54 int conn = 0; 55 56 while(1) 57 { 58 conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen); 59 if(conn == -1) 60 { 61 perror("accept error"); 62 exit(0); 63 } 64 65 char *p = NULL; 66 int peerport = 0; 67 p = inet_ntoa(peeraddr.sin_addr); 68 peerport = ntohs(peeraddr.sin_port); 69 printf("peeraddr = %s\n peerport = %d\n", p, peerport); 70 71 pid_t pid = fork(); 72 73 if(pid == -1) 74 { 75 perror("fork error"); 76 exit(0); 77 } 78 79 if(pid == 0) 80 { 81 char recvbuf[1024] = {0}; 82 int ret = 0; 83 while(1) 84 { 85 ret = read(conn, recvbuf, sizeof(recvbuf)); 86 87 if(ret == 0) 88 { 89 printf("peer closed \n"); 90 exit(0); 91 } 92 else if(ret < 0) 93 { 94 perror("read error"); 95 exit(0); 96 } 97 98 fputs(recvbuf, stdout); 99 100 write(conn, recvbuf, ret); 101 } 102 } 103 } 104 105 close(conn); 106 close(sockfd); 107 108 return 0; 109 }
客户端程序如下:
1 #include <sys/types.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <errno.h> 7 #include <arpa/inet.h> 8 #include <sys/socket.h> 9 #include <netinet/in.h> 10 #include <sys/socket.h> 11 #include <netinet/ip.h> /* superset of previous */ 12 13 int main() 14 { 15 int sockfd = 0; 16 sockfd = socket(AF_INET, SOCK_STREAM, 0); 17 18 struct sockaddr_in addr; 19 addr.sin_family = AF_INET; 20 addr.sin_port = htons(8001); 21 inet_aton("192.168.31.128", &addr.sin_addr); 22 //addr.sin_addr.s_addr = inet_addr("192.168.31.128"); 23 24 if( connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 ) 25 { 26 perror("connect error"); 27 exit(0); 28 } 29 30 char recvbuf[1024] = {0}; 31 char sendbuf[1024] = {0}; 32 int ret = 0; 33 34 while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) 35 { 36 write(sockfd, sendbuf, strlen(sendbuf)); 37 38 ret = read(sockfd, recvbuf, sizeof(recvbuf)); 39 40 fputs(recvbuf, stdout); 41 memset(recvbuf, 0, sizeof(recvbuf)); 42 memset(sendbuf, 0, sizeof(sendbuf)); 43 44 } 45 46 close(sockfd); 47 48 return 0; 49 }
执行结果如下:
可以看到,当客户端使用ctrl+c关闭时,服务器中的子进程成了僵尸进程。这是因为,子进程死了,但是没有进程给它收尸,我们可以调用signal(SIGCHLD, SIG_IGN)函数告诉内核,忽略SIGCHLD信号,让内核来处理SIGCHLD信号,也即让内核收尸,但是工程中一般不这样做。一般是注册信号让父进程调用wait来收尸。
修改服务器程序,加入信号处理函数,如下所示:
1 #include <sys/types.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <errno.h> 7 #include <signal.h> 8 #include <arpa/inet.h> 9 #include <sys/socket.h> 10 #include <netinet/in.h> 11 #include <sys/socket.h> 12 #include <netinet/ip.h> /* superset of previous */ 13 14 void handler(int num) 15 { 16 printf("child die\n"); 17 wait(NULL); 18 } 19 20 int main() 21 { 22 int sockfd = 0; 23 signal(SIGCHLD, handler); 24 sockfd = socket(AF_INET, SOCK_STREAM, 0); 25 26 if(sockfd == -1) 27 { 28 perror("socket error"); 29 exit(0); 30 } 31 32 struct sockaddr_in addr; 33 addr.sin_family = AF_INET; 34 addr.sin_port = htons(8001); 35 inet_aton("192.168.31.128", &addr.sin_addr); 36 //addr.sin_addr.s_addr = inet_addr("192.168.6.249"); 37 //addr.sin_addr.s_addr = INADDR_ANY; 38 39 int optval = 1; 40 if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) 41 { 42 perror("setsockopt error"); 43 exit(0); 44 } 45 46 if( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 47 { 48 perror("bind error"); 49 exit(0); 50 } 51 52 if(listen(sockfd, SOMAXCONN) < 0) 53 { 54 perror("listen error"); 55 exit(0); 56 } 57 58 struct sockaddr_in peeraddr; 59 socklen_t peerlen; 60 61 int conn = 0; 62 63 while(1) 64 { 65 conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen); 66 if(conn == -1) 67 { 68 perror("accept error"); 69 exit(0); 70 } 71 72 char *p = NULL; 73 int peerport = 0; 74 p = inet_ntoa(peeraddr.sin_addr); 75 peerport = ntohs(peeraddr.sin_port); 76 printf("peeraddr = %s\n peerport = %d\n", p, peerport); 77 78 pid_t pid = fork(); 79 80 if(pid == -1) 81 { 82 perror("fork error"); 83 exit(0); 84 } 85 86 if(pid == 0) 87 { 88 char recvbuf[1024] = {0}; 89 int ret = 0; 90 while(1) 91 { 92 ret = read(conn, recvbuf, sizeof(recvbuf)); 93 94 if(ret == 0) 95 { 96 printf("peer closed \n"); 97 exit(0); 98 } 99 else if(ret < 0) 100 { 101 perror("read error"); 102 exit(0); 103 } 104 105 fputs(recvbuf, stdout); 106 107 write(conn, recvbuf, ret); 108 } 109 } 110 } 111 112 close(conn); 113 close(sockfd); 114 115 return 0; 116 }
执行结果如下:
由上图可以看到服务器中子进程死掉后,不存在僵尸进程。而且服务器还正常工作,我们重启客户端,依然可以正常发送数据。
原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9403359.html
时间: 2024-10-10 21:55:44