Linux客戶/伺服器程式設計範式1——並發伺服器(進程)

來源:互聯網
上載者:User

標籤:style   blog   http   color   os   使用   ar   for   2014   

引言

本文會寫一個並發伺服器(concurrent server)程式,它為每個客戶請求fork出一個子進程。

注意

1. 訊號處理問題

對於相同訊號,按訊號的先後順序依次處理。可能會產生的問題是,正在處理sig1訊號時,又來了2個或更多的sig1訊號,此sig1時只會在處理完原來的sig1訊號後,再處理1個sig1訊號。因此對於相同訊號,會產生訊號掉包的問題。 一個兒子退了之後,程式在處理handler(),如果此時又退了兩個兒子,那麼必然有一個兒子的資源回收不到,稱為殭屍進程。

對於不同訊號,優先處理後者,處理完後者在回頭處理上一個。例如正在處理sig1時,來了sig2,則會先處理sig2,等處理完sig2後,回過頭繼續處理sig1。

2. 解決方案

在註冊的訊號處理函數中,使用迴圈,並在迴圈中用waitpid來回收子進程資源。只要進入訊號處理函數,那麼該訊號處理函數就可以把所有fork出的兒子的資源都回收掉。注意:waitpid要設定成非阻塞模式,不然當進入迴圈後,如果沒有子進程退出時,會阻塞在訊號處理函數中。

3. wait與waitpid

wait一定是阻塞模式。因此在訊號處理函數中,用while1{wait(NULL)}有問題,如果進入訊號處理函數後,只要有兒子不退,就會一直阻塞在這裡。

waitpid可以是阻塞模式,也可以是非阻塞模式。

4. select與accept

兩者在收到訊號時,均會返回-1。

對於系統預設不處理的訊號,程式收不到,兩者當然也不會返回-1。換句話說,SIGCHLD是系統預設不處理的訊號,如果不對其註冊訊號處理函數,當子進程退出時,select與accept也是不會返回-1的。

5. 本代碼用到了Linux網路編程9——對TCP與UDP的簡易封裝2.0中的動態庫。

代碼

server.c

/*************************************************************************  > File Name: server.c  > Author: KrisChou  > Mail:[email protected]  > Created Time: Fri 05 Sep 2014 03:31:12 PM CST ************************************************************************/#include "my_socket.h"#include <sys/wait.h>#include <signal.h>#include <errno.h>#define MY_IP "192.168.1.100"#define MY_PORT 8888#define SIZE 192#define MSG_SIZE (SIZE - 4)extern int errno ;typedef struct tag_mag{    int  msg_len;//記錄msg_buf的真實大小    char msg_buf[MSG_SIZE];//msg_buf所佔空間為188byte}MSG, *pMSG;void my_handle(int num){    /*waitpid參數:     * -1表示回收每一個兒子     * NULL表示不關心子進程的exit的傳回值     * WNOHANG:wait no hang 非阻塞模式     *waitpid傳回值:     * -1表示沒有建立任何兒子     *  0表示沒有兒子退出     *大於0表示有兒子退出     * */    while(waitpid(-1, NULL, WNOHANG ) > 0) ;}int main(int argc, char* argv[]){    int fd_listen , fd_client ;    signal(SIGCHLD, my_handle);    my_socket(&fd_listen, MY_TCP, MY_IP ,MY_PORT);    my_listen(fd_listen, 10);    while( fd_client = accept(fd_listen, NULL, NULL))    {        /* 只要不是程式預設忽略的訊號,accept都能收到,並返回-1 */        if(fd_client == -1)        {            if(errno == EINTR)            {                continue ;            }else             {                break ; //break退出後,父親就退了。下來兒子會由init接管。            }        }else         {            if(fork() == 0) //fork兒子用於與用戶端通訊            {                MSG recv_msg ;                int recvn;                    while(1 )                    {                        memset(&recv_msg, 0, sizeof(MSG));                        /*在my_socket.c中,my_recv接收的長度 與 my_send 發送的長度必須是精確值                          * my_recv中填的長度小於等於實際要收的,是可以的,大於的話就永遠退不出迴圈了*/                        my_recv(&recvn, fd_client, &recv_msg, 4);                        if(recvn == 0) //當對面用戶端退出(關閉socket),系統調用recv的傳回值為0                        {                            break ;                        }else                        {                            my_recv(NULL,fd_client, &recv_msg.msg_buf, recv_msg.msg_len);                            my_send(NULL, fd_client, &recv_msg, 4 + recv_msg.msg_len);                        }                    }                close(fd_client);                exit(0);                }            close(fd_client);        }    }    return 0 ;}

client.c

#include "my_socket.h"#define MY_IP "192.168.1.100"#define MY_PORT 6666#define SER_IP "192.168.1.100"#define SER_PORT 8888#define SIZE 192#define MSG_SIZE (SIZE - 4)typedef struct tag_mag {    int msg_len ;    char msg_buf[MSG_SIZE];//188}MSG, *pMSG;int main(int argc, char* argv[]){    int sfd ;    my_socket(&sfd, MY_TCP, MY_IP, MY_PORT);    my_connect(sfd, SER_IP, SER_PORT);    MSG my_msg ;    while(memset(&my_msg, 0, sizeof(MSG)), fgets(my_msg.msg_buf, MSG_SIZE, stdin)!= NULL)    {        my_msg.msg_len = strlen(my_msg.msg_buf);        my_send(NULL, sfd, &my_msg, 4 + my_msg.msg_len );        memset(&my_msg, 0, sizeof(MSG));        my_recv(NULL, sfd, &my_msg, 4);        my_recv(NULL, sfd, &my_msg.msg_buf, my_msg.msg_len);        printf("recv from server : %s \n", my_msg.msg_buf);        }    close(sfd);}

注意:本代碼由於client.c中綁定了連接埠,因此只能連一個用戶端,讀者可以自己從命令列中輸入連接埠號碼或者讓系統自行分配。

本範式的缺陷在於,服務端在每次收到一個用戶端串連請求後,才會fork兒子進行處理,fork有時間開銷。更好的方法是,服務端提前fork好兒子,下篇博文會講進程池。

Linux客戶/伺服器程式設計範式1——並發伺服器(進程)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.