国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Linux > 正文

Linux客戶/服務器程序設計范式2——并發服務器(進程池)

2024-06-28 13:24:13
字體:
來源:轉載
供稿:網友
linux客戶/服務器程序設計范式2——并發服務器(進程池)引言

讓服務器在啟動階段調用fork創建一個子進程池,通過子進程來處理客戶端請求。子進程與父進程之間使用socketpair進行通信(為了方便使用sendmsg與recvmsg,如果使用匿名管道,則無法使用以上兩個函數)。以下針對TCP進行分析。

server端使用select輪詢用于監聽客戶端請求的被動套接字fd_listen以及用于父子之間通信的socketpair。每當客戶端有請求時,server端會將由accept返回的用于與客戶端通信的socket描述符通過socketpair發送給一個空閑的子進程,由子進程與客戶端進行通信(處理請求)。因此服務器端需要維護一個子進程隊列,隊列中的每個元素存放著與子進程通信的socketpair以及標記子進程是否空閑的標志位,如下:

typedef struct tag_chd{    int s_sfd ;     //與子進程通信的socketpair描述符    int s_state ;   //標記子進程是否空閑}NODE, *pNODE;

每當子進程處理完客戶端請求時,會通過socketpair向server端發送消息,server端select到該socketpair后,會將對應子進程標志位設置為空閑。

注意

1. 由于父進程是先創建子進程,之后才accept用于與客戶端通信的socket描述符fd_client,因此子進程的pcb中并沒有fd_client的信息。server端需要將fd_client發送子進程。如果只是用send來發送fd_client信息的話,子進程只會將其當成一個整型數。我們需要用sendmsg將fd_client連同其輔助(控制)信息一并發送,這樣子進程才會將其當成一個socket描述符。

2. 父進程預先創建子進程池,該子進程如同server端一樣是永遠不會退出的。子進程中使用while死循環,如下:

while(1)    {        readn = read(sfd, &flag, 4);                // 服務器分配的子進程在子進程隊列中的下標        PRintf("readn: %d /n", readn);                     printf("read from father: %d /n", flag);        recv_fd(sfd, &fd_client);                   // recv_fd中封裝了recvmsg,接收與客戶端通信的socket描述符        handle_request(fd_client);                  // 處理客戶端請求        write(sfd, &pid, sizeof(pid));              // 處理完請求后通過socketpair通知服務器,服務器將該子進程狀態設置為空閑    }

每當子進程處理完一個客戶端請求后(也就是客戶端退出了),子進程會阻塞在 read 處,等待接收下一個客戶端請求。

由于是while死循環,且死循環中沒有break語句,因此子進程不可能跳出這個while循環,也就不會執行while循環以下的內容了,這樣可以保證子進程結尾沒有exit也不會執行之后的內容。

3. 編譯用到的動態庫見Linux網絡編程9——對TCP與UDP的簡易封裝2.0。

函數原型
 #include <sys/types.h> #include <sys/socket.h> ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); struct msghdr {               void         *msg_name;       /* optional address */               socklen_t     msg_namelen;    /* size of address */               struct iovec *msg_iov;        /* scatter/gather array */               size_t        msg_iovlen;     /* # elements in msg_iov */               void         *msg_control;    /* ancillary data, see below */               socklen_t     msg_controllen; /* ancillary data buffer len */               int           msg_flags;      /* flags on received message */           };   fields:  struct iovec {                    /* Scatter/gather array items */               void  *iov_base;              /* Starting address */               size_t iov_len;               /* Number of bytes to transfer */           };            struct cmsghdr {           socklen_t cmsg_len;    /* data byte count, including header */           int       cmsg_level;  /* originating protocol */ /* 如果是文件描述符,填SOL_SOCKET */           int       cmsg_type;   /* protocol-specific type */ /* 如果是文件描述符,填SCM_RIGHTS */           /* followed by unsigned char cmsg_data[]; */       };  /* 返回cmsghdr結構的cmsg_len成員的值,考慮到對齊,使用數據部分的長度作為參數。*/       size_t CMSG_LEN(size_t length);   /* 返回cmsghdr的數據部分指針。*/ unsigned char *CMSG_DATA(struct cmsghdr *cmsg);  CMSG_DATA() returns a pointer to the data portion of a cmsghdr. CMSG_LEN()  returns  the  value  to store in the cmsg_len member of the cmsghdr structure, taking into             account any necessary alignment.  It takes the data length as an argument.              This is a constant expression.
NAME       socketpair - create a pair of connected socketsSYNOPSIS       #include <sys/types.h>          /* See NOTES */       #include <sys/socket.h>       int socketpair(int domain, int type, int protocol, int sv[2]);

RETURN VALUE On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

代碼

server.h

#ifndef __SERVER_H__#define __SERVER_H__#include "my_socket.h"#include <sys/stat.h>#include <sys/types.h>#include <fcntl.h>#include <sys/time.h>#include <sys/select.h>#include <sys/uio.h>#include <sys/wait.h>#include <errno.h>#define SER_ip "127.0.0.1"#define SER_PORT 8888#define ST_BUSY 1#define ST_IDLE 2#define SIZE 8192#define MSG_SIZE (SIZE - 4)typedef struct tag_mag{    int msg_len ;    char msg_buf[MSG_SIZE];//8188}MSG, *pMSG;typedef struct tag_chd{    int s_sfd ;    int s_state ;}NODE, *pNODE;extern int errno ;void make_child(pNODE arr, int cnt);void child_main(int sfd) ;void handle_request(int sfd);void send_fd(int sfd, int fd_file) ;void recv_fd(int sfd, int* fd_file) ;void dispatch(pNODE arr, int cnt, int fd_client);#endif

main.c

/*************************************************************************    > File Name: main.c    > Author: KrisChou    > Mail:zhoujx0219@163.com    > Created Time: Fri 05 Sep 2014 11:19:13 PM CST ************************************************************************/#include "server.h"int main(int argc, char* argv[])//exe chld_cnt{    if(argc != 2)    {        printf("Usage: exe , child_cnt! /n");        exit(1);    }    int child_cnt = atoi(argv[1]);    pNODE arr_child = (pNODE)calloc(child_cnt, sizeof(NODE)) ; /* 動態數組維護子進程池 */    make_child(arr_child, child_cnt);        int fd_listen, fd_client ;    my_socket(&fd_listen, MY_TCP, SER_IP, SER_PORT);    my_listen(fd_listen, 10);        fd_set readset, readyset ;    FD_ZERO(&readset);    FD_ZERO(&readyset);    FD_SET(fd_listen, &readset);    int index ;    for(index = 0; index < child_cnt; index ++)    {        FD_SET(arr_child[index].s_sfd, &readset);    }        int select_ret ;    struct timeval tm ;    while(1)    {        tm.tv_sec = 0 ;        tm.tv_usec = 1000 ;        readyset = readset ;        select_ret = select(1024, &readyset, NULL, NULL, &tm);        if(select_ret == 0)        /* 輪詢時間內,所有描述符均沒有活動,返回0,繼續輪詢 */        {            continue ;        }else if(select_ret == -1) /* 信號 */        {            if(errno == EINTR)            {                continue ;            }else             {                exit(1);            }        }else         {            if(FD_ISSET(fd_listen, &readyset))            {            fd_client = accept(fd_listen, NULL, NULL) ;                dispatch(arr_child, child_cnt ,fd_client);            close(fd_client);            }            for(index = 0; index < child_cnt; index ++)            {                if(FD_ISSET(arr_child[index].s_sfd, &readyset))                {                    int val ;                    read(arr_child[index].s_sfd, &val, 4);                    arr_child[index].s_state = ST_IDLE ;                }            }                    }            }   }

server.c

/*************************************************************************  > File Name: server.c  > Author: KrisChou  > Mail:zhoujx0219@163.com  > Created Time: Fri 05 Sep 2014 11:17:56 PM CST ************************************************************************/#include "server.h"void make_child(pNODE arr, int cnt){    int index ;     for(index = 0; index < cnt; index ++)    {        pid_t pid ;        int fds[2] ;//fds[0] - c  fds[1] - p        socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);        pid = fork() ;        if(pid == 0)// child        {            close(fds[1]);         /* 子進程用fds[0],關閉fds[1] */            child_main(fds[0]) ;   /* 每創建一個子進程,子進程就進入該函數中(死循環),接收請求,處理請求,如此循環。*/        }else         {            /* 初始化進程池隊列中的每一個子進程 */            arr[index].s_sfd = fds[1] ;            arr[index].s_state = ST_IDLE ;            close(fds[0]);         /* 父進程用fds[1], 關閉fds[0] */        }    }}void child_main(int sfd){    int fd_client ;    int flag ;    int readn ;    pid_t pid = getpid();    while(1)    {        readn = read(sfd, &flag, 4);        printf("readn: %d /n", readn);        printf("read from father: %d /n", flag);        recv_fd(sfd, &fd_client);        handle_request(fd_client);        write(sfd, &pid, sizeof(pid));    }}void handle_request(int sfd){        MSG my_msg ;    int recvn ;    while(1)    {        memset(&my_msg, 0, sizeof(MSG));        my_recv(&recvn, sfd, &my_msg, 4);        if(my_msg.msg_len  == 0)        {            break ;        }        my_recv(NULL, sfd, my_msg.msg_buf, my_msg.msg_len);        my_send(NULL, sfd, &my_msg, my_msg.msg_len + 4);    }}void send_fd(int sfd, int fd_file) {    struct msghdr my_msg ;    memset(&my_msg, 0, sizeof(my_msg));        struct iovec bufs[1] ;    char buf[32] = "hello world ! /n";    bufs[0].iov_base = buf ;    bufs[0].iov_len = strlen(buf) ;        my_msg.msg_name = NULL ;    my_msg.msg_namelen = 0 ;    my_msg.msg_iov = bufs ;    my_msg.msg_iovlen = 1 ;    my_msg.msg_flags = 0 ;    struct cmsghdr *p  ;    int cmsg_len = CMSG_LEN(sizeof(int)) ;     /* 所傳為文件描述符,因此sizeof(int) */    p = (struct cmsghdr*)calloc(1, cmsg_len) ;    p -> cmsg_len = cmsg_len ;    p -> cmsg_level = SOL_SOCKET ;    p -> cmsg_type = SCM_RIGHTS ;    *(int*)CMSG_DATA(p) = fd_file ;        my_msg.msg_control = p ;    my_msg.msg_controllen = cmsg_len ;        int sendn ;    sendn = sendmsg(sfd, &my_msg, 0);    printf("send masg len : %d /n", sendn);}void recv_fd(int sfd, int* fd_file) {    struct msghdr my_msg ;        struct iovec bufs[1] ;    char buf1[32]="" ;    bufs[0].iov_base = buf1 ;    bufs[0].iov_len = 31 ;    my_msg.msg_name = NULL ;    my_msg.msg_namelen = 0 ;    my_msg.msg_iov = bufs ;    my_msg.msg_iovlen = 2 ;    my_msg.msg_flags = 0 ;        struct cmsghdr *p  ;    int cmsg_len = CMSG_LEN(sizeof(int)) ;    p = (struct cmsghdr*)calloc(1, cmsg_len) ;    my_msg.msg_control = p ;    my_msg.msg_controllen = cmsg_len ;        int recvn ;    recvn = recvmsg(sfd, &my_msg, 0);        *fd_file = *(int*)CMSG_DATA((struct cmsghdr*)my_msg.msg_control); //寫成*(int*)CMSG_DATA(P)也可        printf("buf1: %s, recv msg len : %d   /n", buf1, recvn);}void dispatch(pNODE arr, int cnt, int fd_client){    int index ;    for(index = 0 ; index < cnt; index ++)    {        if(arr[index].s_state == ST_IDLE)        {            write(arr[index].s_sfd, &index, 4);            send_fd(arr[index].s_sfd, fd_client); /* 向空閑的子進程分配任務,將服務器accept返回的socket描述符發送給子進程*/            arr[index].s_state = ST_BUSY ;            break ;        }    }}

client.c

/*************************************************************************    > File Name: client.c    > Author: KrisChou    > Mail:zhoujx0219@163.com    > Created Time: Fri 05 Sep 2014 03:48:27 PM CST ************************************************************************/#include "my_socket.h"#define MY_IP "127.0.0.1"#define MY_PORT 6666#define SER_IP "127.0.0.1"#define SER_PORT 8888#define SIZE 8192#define MSG_SIZE (SIZE - 4)typedef struct tag_mag// {    int msg_len ;    char msg_buf[MSG_SIZE];//8188}MSG, *pMSG;int main(int argc, char* argv[]){    int sfd ;    my_socket(&sfd, MY_TCP, MY_IP, atoi(argv[1]));    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);        }    /* 客戶端退出時,向服務器發送一個長度為0的消息 ,用于通知服務器退出 */    memset(&my_msg, 0, sizeof(MSG));    my_send(NULL, sfd, &my_msg, 4 + my_msg.msg_len);    close(sfd);}

編譯如下:

gcc -o s server.c main.c -lmy_socket -I/home/purple/includegcc -o c client.c -lmy_socket -I/home/purple/include

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 北碚区| 万载县| 济阳县| 南昌市| 邵东县| 吴忠市| 安溪县| 丹巴县| 永济市| 闽清县| 淮北市| 海原县| 广汉市| 富阳市| 赤水市| 商河县| 合川市| 望都县| 钟山县| 香港| 儋州市| 卢氏县| 疏附县| 峡江县| 兴仁县| 军事| 梨树县| 武义县| 东辽县| 饶阳县| 阳原县| 绥德县| 凤庆县| 资阳市| 屯昌县| 开鲁县| 汶川县| 固原市| 九台市| 宁明县| 麻栗坡县|