第十二篇:併發回射伺服器的具體實現及其中殭屍子程式的清理( 上 )

穆晨發表於2017-01-29

前言

       本文將分為兩個部分,第一部分具體實現一對併發回射伺服器/客戶程式( 看過前面那篇文章的這部分可不看 重複了 );第二部分為伺服器新增殭屍子程式自動清理機制。

       那麼伺服器具體怎麼實現併發?怎麼會有殭屍程式?殭屍程式又是什麼?如何處理這些殭屍程式 ... 本文將為你一一解惑。

回射併發伺服器

       功能:接收使用者傳送過來的資料後再傳送回使用者,且能同時處理多個使用者請求。

       大體思路:每當收到使用者請求,伺服器就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不難發現,伺服器中處理客戶端的程式並沒有“ 死去 ”,而是變成了“ 殭屍 ”

什麼是殭屍程式

       殭屍程式就是已經撤銷但依然佔著資源的程式。

為什麼要有殭屍程式

       設定僵死狀態的目的是為了維護子程式的資訊。父程式有時需要獲取到一些已經撤銷的子程式的資訊。

如何處理殭屍程式

       對於本例中的殭屍程式,我們當然要將其清理掉並釋放其佔用的資源。

相關文章