以下內容為視頻學習記錄。
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 8000void 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);} elseperr_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;
}