• Linux网络编程客户服务器设计范式


    1、前言

      网络编程分为客户端和服务端,服务器通常分为迭代服务器和并发服务器。并发服务器可以根据多进程或多线程进行细分,给每个连接创建一个独立的进程或线程,或者预先分配好多个进程或线程等待连接的请求。今天探讨三种设计范式

    (1)迭代服务器

    (2)并发服务器,为每个客户请求创建一个进程或线程

    (3)预先分配子进程或线程,每个子进程或线程调用accept

    3、测试用例:

    客户端代码:

     1 #include <sys/wait.h>
     2 #include <string.h>
     3 #include <errno.h>
     4 #include <netdb.h>
     5 #include <stdlib.h>
     6 
     7 #define IP   "127.0.0.1"
     8 #define PORT  8888
     9 #define WORKER 4
    10 #define MAXIN  4096
    11 #define MAXLINE  4096
    12 
    13 int tcp_connect(const char *host, const char *port)
    14 {
    15     if (host == NULL || port == NULL) {
    16         return -1;
    17     }
    18     int sockfd, n;
    19     struct addrinfo hints, *res, *ressave;
    20     bzero(&hints, sizeof(struct addrinfo));
    21     hints.ai_family = AF_UNSPEC;
    22     hints.ai_socktype = SOCK_STREAM;
    23     if ((n = getaddrinfo(host, port, &hints, &res)) != 0) {
    24         printf("tcp_connect error for %s,%s: %s
    ", host, port,  strerror(errno));
    25         return -1;
    26     }
    27     ressave = res;
    28     do {
    29         sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    30         if (sockfd < 0) {
    31             continue;
    32         }
    33         if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) {
    34             break;
    35         }
    36         close(sockfd);
    37     } while( (res = res->ai_next) != NULL);
    38     if (res == NULL) {
    39         printf("tcp_connect error for %s,%s: %s", host, port,  strerror(errno));
    40         return -1;
    41     }
    42     freeaddrinfo(ressave);
    43     return sockfd;
    44 }
    45 
    46 int main(int argc, char **argv)
    47 {
    48     if (argc != 6) {
    49         printf("usage: client <hostname or IPaddr> <port> <#children> <#loops/child> <#bytes/request>
    ");
    50         return -1;
    51     }
    52 
    53     int i, j, fd, nchildlen, nloops, nbytes;
    54     pid_t pid;
    55     ssize_t n;
    56     char request[MAXLINE], reply[MAXIN];
    57     nchildlen = atoi(argv[3]);
    58     nloops = atoi(argv[4]);
    59     nbytes = atoi(argv[5]);
    60     snprintf(request, sizeof(request), "%d
    ", nbytes);
    61     for (i = 0; i < nchildlen; i++) {
    62         if ((pid = fork()) == 0) {
    63             for (j = 0; j < nloops; j++) {
    64                 fd = tcp_connect(argv[1], argv[2]);
    65                 if (fd > 0) {
    66                     write(fd, request, strlen(request));
    67 
    68                     if ((n = read(fd, reply, nbytes)) != nbytes) {
    69                         printf("read from server is:%s
    ", reply);
    70                     }
    71                     close(fd);
    72                 } else {
    73                     break;
    74                 }
    75             }
    76             printf("child %d done
    ", i);
    77             exit(0);
    78         }
    79     }
    80     /*waits all child process*/
    81     while (wait(NULL) > 0)
    82         ;
    83     if (errno != ECHILD) {
    84         fprintf(stderr, "wait error");
    85         return -1;
    86     }
    87     return 0;
    88 }

    迭代服务器代码如下:

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <sys/types.h>  
     4 #include <sys/socket.h>  
     5 #include <netinet/in.h>  
     6 #include <arpa/inet.h>  
     7 #include <assert.h>  
     8 #include <string.h>
     9 #include <errno.h>
    10 
    11 #define IP   "127.0.0.1"
    12 #define PORT  8888
    13 #define MAXLINE   4096
    14 
    15 int main()
    16 {
    17     int listenfd, connfd;
    18     struct sockaddr_in address, client_addr;  
    19     socklen_t client_addrlen = sizeof(client_addr);  
    20     bzero(&address, sizeof(address));  
    21     address.sin_family = AF_INET;  
    22     inet_pton( AF_INET, IP, &address.sin_addr);  
    23     address.sin_port = htons(PORT);  
    24     listenfd = socket(PF_INET, SOCK_STREAM, 0);  
    25     assert(listenfd >= 0);  
    26     int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));  
    27     assert(ret != -1);  
    28     ret = listen(listenfd, 5);  
    29     assert(ret != -1);  
    30 
    31     char buffer[MAXLINE];
    32     while (1) {
    33         printf("begin to accept.
    ");
    34         int connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen );  
    35         if (connfd != -1) {
    36             printf("accept a connection success.ip :%s, port :%d
    ", inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
    37         } else {
    38             printf("accept a connection failed,error:%s", strerror(errno));
    39         }
    40 
    41         int nbytes = read(connfd, buffer, MAXLINE);
    42         printf("read from client is:%s
    ", buffer);
    43         write(connfd, buffer, nbytes);
    44 
    45         close(connfd);
    46     }
    47     return 0;
    48 }

    并发服务器,为每个客户请求创建一个进程测试代码如下:

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>  
    #include <sys/socket.h>  
    #include <netinet/in.h>  
    #include <arpa/inet.h>  
    #include <assert.h>  
    #include <sys/wait.h>
    #include <string.h>
    #include <errno.h>
    #include <stdlib.h>
    
    #define IP   "127.0.0.1"
    #define PORT  8888
    #define MAXLINE 4096
    
    int main()
    {
        int count = 0;
        struct sockaddr_in address, client_addr;  
        socklen_t client_addrlen = sizeof( client_addr );  
        bzero(&address, sizeof(address));  
        address.sin_family = AF_INET;  
        inet_pton( AF_INET, IP, &address.sin_addr);  
        address.sin_port = htons(PORT);  
        int listenfd,connfd;
        listenfd = socket(PF_INET, SOCK_STREAM, 0);  
        assert(listenfd >= 0);  
        int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));  
        assert(ret != -1);  
        ret = listen(listenfd, 5);  
        assert(ret != -1);  
        while(1) {
            connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen );  
            if (connfd == -1) {
                printf("accept a connection failed,error:%s", strerror(errno));
                break;
            } 
            printf("accept a connection success.ip: %s,prot: %d
    ",inet_ntoa(client_addr.sin_addr),client_addr.sin_port);
            pid_t pid = fork();
            count = count + 1;
            /*child  process */
            if (pid == 0) {
                printf("Create process %d handle a new connetcion.
    ", count);
                close(listenfd);
                char buffer[MAXLINE];
                int nbytes = read(connfd, buffer, MAXLINE);
                printf("read from client is:%s
    ", buffer);
                write(connfd, buffer, nbytes);
                exit(0);
            }
            if (pid < 0) {
                printf("fork error");
            }
            close(connfd);
        }
        return 0;
    }

    预先分配子进程,每个子进程调用accept测试代码如下:

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <sys/types.h>  
     4 #include <sys/socket.h>  
     5 #include <netinet/in.h>  
     6 #include <arpa/inet.h>  
     7 #include <assert.h>  
     8 #include <sys/wait.h>
     9 #include <string.h>
    10 #include <errno.h>
    11 #include <stdlib.h>
    12 
    13 #define IP   "127.0.0.1"
    14 #define PORT  8888
    15 #define WORKER 4
    16 #define MAXLINE   4096
    17 
    18 int worker(int listenfd, int i)
    19 {
    20     while (1) {
    21         printf("I am worker %d, begin to accept connection.
    ", i);
    22         struct sockaddr_in client_addr;  
    23         socklen_t client_addrlen = sizeof( client_addr );  
    24         int connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen );  
    25         if (connfd != -1) {
    26             printf("worker %d accept a connection success. ip:%s, prot:%d
    ", i, inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
    27         } else {
    28             printf("worker %d accept a connection failed,error:%s", i, strerror(errno));
    29         }
    30         char buffer[MAXLINE];
    31         int nbytes = read(connfd, buffer, MAXLINE);
    32         printf("read from client is:%s
    ", buffer);
    33         write(connfd, buffer, nbytes);
    34         close(connfd);
    35     }
    36     return 0;
    37 }
    38 
    39 int main()
    40 {
    41     int i = 0;
    42     struct sockaddr_in address;  
    43     bzero(&address, sizeof(address));  
    44     address.sin_family = AF_INET;  
    45     inet_pton( AF_INET, IP, &address.sin_addr);  
    46     address.sin_port = htons(PORT);  
    47     int listenfd = socket(PF_INET, SOCK_STREAM, 0);  
    48     assert(listenfd >= 0);  
    49     int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));  
    50     assert(ret != -1);  
    51     ret = listen(listenfd, 5);  
    52     assert(ret != -1);  
    53 
    54     for (i = 0; i < WORKER; i++) {
    55         printf("Create worker %d
    ", i+1);
    56         pid_t pid = fork();
    57         /*child  process */
    58         if (pid == 0) {
    59             worker(listenfd, i);
    60         }
    61         if (pid < 0) {
    62             printf("fork error");
    63         }
    64     }
    65     /*wait child process*/
    66     while (wait(NULL) != 0)
    67         ;
    68     if (errno == ECHILD) {
    69         fprintf(stderr, "wait error:%s
    ", strerror(errno));
    70     }
    71     return 0;
    72 }
  • 相关阅读:
    并行数据的并行转串行
    色彩空间转换仿真与模型搭建
    布隆过滤器介绍和在java中应用举例
    java9初探
    个人博客开通啦!
    MyBatis多租户隔离插件开发
    手动解析Excel获取文件元数据
    解决Shiro+SpringBoot自定义Filter不生效问题
    基于Redis的分布式锁实现
    解决tomcat同时部署两个SpringBoot应用提示InstanceAlreadyExistsException
  • 原文地址:https://www.cnblogs.com/Anker/p/7075141.html
Copyright © 2020-2023  润新知