前言
本文將分為兩個部分,第一部分具體實現一對併發回射伺服器/客戶程式( 看過前面那篇文章的這部分可不看 重複了 );第二部分為伺服器新增殭屍子程式自動清理機制。
那麼伺服器具體怎麼實現併發?怎麼會有殭屍程式?殭屍程式又是什麼?如何處理這些殭屍程式 ... 本文將為你一一解惑。
回射併發伺服器
功能:接收使用者傳送過來的資料後再傳送回使用者,且能同時處理多個使用者請求。
大體思路:每當收到使用者請求,伺服器就fork一個子程式,讓子程式去處理客戶請求。
實現程式碼:
1 #include "unp.h" 2 3 int 4 main(int argc, char **argv) 5 { 6 int listenfd, connfd; 7 pid_t childpid; 8 socklen_t clilen; 9 struct sockaddr_in cliaddr, servaddr; 10 11 listenfd = Socket(AF_INET, SOCK_STREAM, 0); 12 13 bzero(&servaddr, sizeof(servaddr)); 14 servaddr.sin_family = AF_INET; 15 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 16 servaddr.sin_port = htons(SERV_PORT); 17 18 Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); 19 20 Listen(listenfd, LISTENQ); 21 22 for ( ; ; ) { 23 clilen = sizeof(cliaddr); 24 connfd = Accept(listenfd, (SA *) &cliaddr, &clilen); 25 26 /* 27 * 子程式程式碼 28 */ 29 if ( (childpid = Fork()) == 0) { 30 // 子程式啟動後首先關閉監聽套接字 31 Close(listenfd); 32 // 回射處理 33 str_echo(connfd); 34 // 退出子程式 35 exit(0); 36 } 37 // 關閉連線套接字 38 Close(connfd); 39 } 40 }
1 #include "unp.h" 2 3 void 4 str_echo(int sockfd) 5 { 6 ssize_t n; 7 char buf[MAXLINE]; 8 9 again: 10 while ( (n = read(sockfd, buf, MAXLINE)) > 0) 11 Writen(sockfd, buf, n); 12 13 if (n < 0 && errno == EINTR) 14 goto again; 15 else if (n < 0) 16 err_sys("str_echo: read error"); 17 }
回射併發客戶端
功能:向伺服器傳送資料,接收伺服器反射回的資訊並列印。
大體思路:略。
實現程式碼:
1 #include "unp.h" 2 3 int 4 main(int argc, char **argv) 5 { 6 int sockfd; 7 struct sockaddr_in servaddr; 8 9 if (argc != 2) 10 err_quit("usage: tcpcli <IPaddress>"); 11 12 sockfd = Socket(AF_INET, SOCK_STREAM, 0); 13 14 bzero(&servaddr, sizeof(servaddr)); 15 servaddr.sin_family = AF_INET; 16 servaddr.sin_port = htons(SERV_PORT); 17 Inet_pton(AF_INET, argv[1], &servaddr.sin_addr); 18 19 Connect(sockfd, (SA *) &servaddr, sizeof(servaddr)); 20 21 str_cli(stdin, sockfd); // 與伺服器通訊 22 23 exit(0); 24 }
1 #include "unp.h" 2 3 void 4 str_echo(int sockfd) 5 { 6 ssize_t n; 7 char buf[MAXLINE]; 8 9 again: 10 while ( (n = read(sockfd, buf, MAXLINE)) > 0) 11 Writen(sockfd, buf, n); 12 13 if (n < 0 && errno == EINTR) 14 goto again; 15 else if (n < 0) 16 err_sys("str_echo: read error"); 17 }
執行測試
1. 在一個終端以超級使用者許可權啟動伺服器
2. 在另幾個終端開啟客戶端並輸入IP地址引數127.0.0.1
3. 執行回射測試,執行情況如下( 幾個客戶終端都執行正常 ):
問題發現
看似這個併發回射程式已經做好了。但,很遺憾,不是的。我們接下來的觀察將會帶我們進入本文真正的主題:殭屍子程式。
首先退出這幾個客戶終端。按照我們最初的設想,這幾個子程式應該已經結束了。但事實真是這樣嗎?我們在終端執行以下命令進行檢視:
圖中,STAT顯示錶示程式狀態,Z+表示程式正處在“ 殭屍 ”狀態。通過比對其PID與PPID不難發現,伺服器中處理客戶端的程式並沒有“ 死去 ”,而是變成了“ 殭屍 ”。
什麼是殭屍程式
殭屍程式就是已經撤銷但依然佔著資源的程式。
為什麼要有殭屍程式
設定僵死狀態的目的是為了維護子程式的資訊。父程式有時需要獲取到一些已經撤銷的子程式的資訊。
如何處理殭屍程式
對於本例中的殭屍程式,我們當然要將其清理掉並釋放其佔用的資源。