以下内容为视频学习记录。
1、父进程accept后返回的文件描述符为cfd以及用于创建连接的lfd;
调用fork()创建子进程后,子进程继承cfd,lfd,通过该cfd与连接过来的客户端通信,lfd对子进程来说没用,可以直接close(lfd);
对于父进程来说,只是用来建立连接的,故父进程中的cfd没有用,直接close(cfd);
2、子进程完成数据交互后,close(cfd);exit(1);此时成为僵尸进程,所以需要在父进程中收尸,回收进程描述符等资源。否则的话,当父进程一直没有退出的时候,僵尸进程会导致资源消耗殆尽。
但此时父进程在accept,无法waitpid(),此时使用signal的方式SIGCHILD来解决收尸问题,当子进程成僵尸进程后,由内核自动回收。
注意:进程只能由父进程来回收,兄弟进程、叔叔进程都不行。
- #include <stdio.h>
- #include <string.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <signal.h>
- #include <sys/wait.h>
- #include <ctype.h>
- #include <unistd.h>
-
- #include "wrap.h"
-
- #define MAXLINE 8192
- #define SERV_PORT 8000
-
- void do_sigchild(int num)
- {
- while (waitpid(0, NULL, WNOHANG) > 0)
- ;
- }
-
- int main(void)
- {
- struct sockaddr_in servaddr, cliaddr;
- socklen_t cliaddr_len;
- int listenfd, connfd;
- char buf[MAXLINE];
- char str[INET_ADDRSTRLEN];
- int i, n;
- pid_t pid;
- struct sigaction newact;
-
- newact.sa_handler = do_sigchild;
- sigemptyset(&newact.sa_mask);
- newact.sa_flags = 0;
- sigaction(SIGCHLD, &newact, NULL);
-
- listenfd = Socket(AF_INET, SOCK_STREAM, 0);
-
- int opt = 1;
- setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
-
- bzero(&servaddr, sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
- servaddr.sin_port = htons(SERV_PORT);
-
- Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
-
- Listen(listenfd, 20);
-
- printf("Accepting connections ...\n");
- while (1) {
- cliaddr_len = sizeof(cliaddr);
- connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
- pid = fork();
- if (pid == 0) {
- Close(listenfd);
- while (1) {
- n = Read(connfd, buf, MAXLINE);
- if (n == 0) {
- printf("the other side has been closed.\n");
- break;
- }
- printf("received from %s at PORT %d\n",
- inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
- ntohs(cliaddr.sin_port));
-
- for (i = 0; i < n; i++)
- buf[i] = toupper(buf[i]);
-
- Write(STDOUT_FILENO, buf, n);
- Write(connfd, buf, n);
- }
- Close(connfd);
- return 0;
- } else if (pid > 0) {
- Close(connfd);
- } else
- perr_exit("fork");
- }
- return 0;
- }
总结:子进程负责与客户端通信,父进程负责接收客户端连接,但因为父进程还需要回收子进程,所以通过信号的方式来回收,
信号部分的代码也可以放在fork之后判断父进程位置。
- else if (pid > 0) {
- Close(connfd);
-
- signal(SIGCHLD,wait_child);
- }
-
- main函数外定义回调函数
-
- void wait_child(int signo)
- {
- while(watipid(0,NULL,WNOHANG)>0); //大于0则继续回收
- return;
- }