美文网首页程序员
Linux网络编程客户端和服务器设计模式

Linux网络编程客户端和服务器设计模式

作者: 贰爷 | 来源:发表于2020-10-01 11:57 被阅读0次

    1、前言

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

    (1)迭代服务器

    image

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

    image

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

    image

    3、测试用例:

    客户端代码:

    #include <sys/wait.h>
    #include <string.h>
    #include <errno.h>
    #include <netdb.h>
    #include <stdlib.h>
    
    #define IP   "127.0.0.1"
    #define PORT  8888
    #define WORKER 4
    #define MAXIN  4096
    #define MAXLINE  4096
    
    int tcp_connect(const char *host, const char *port)
    {
        if (host == NULL || port == NULL) {
            return -1;
        }
        int sockfd, n;
        struct addrinfo hints, *res, *ressave;
        bzero(&hints, sizeof(struct addrinfo));
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        if ((n = getaddrinfo(host, port, &hints, &res)) != 0) {
            printf("tcp_connect error for %s,%s: %s\n", host, port,  strerror(errno));
            return -1;
        }
        ressave = res;
        do {
            sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
            if (sockfd < 0) {
                continue;
            }
            if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) {
                break;
            }
            close(sockfd);
        } while( (res = res->ai_next) != NULL);
        if (res == NULL) {
            printf("tcp_connect error for %s,%s: %s", host, port,  strerror(errno));
            return -1;
        }
        freeaddrinfo(ressave);
        return sockfd;
    }
    
    int main(int argc, char **argv)
    {
        if (argc != 6) {
            printf("usage: client <hostname or IPaddr> <port> <#children> <#loops/child> <#bytes/request>\n");
            return -1;
        }
    
        int i, j, fd, nchildlen, nloops, nbytes;
        pid_t pid;
        ssize_t n;
        char request[MAXLINE], reply[MAXIN];
        nchildlen = atoi(argv[3]);
        nloops = atoi(argv[4]);
        nbytes = atoi(argv[5]);
        snprintf(request, sizeof(request), "%d\n", nbytes);
        for (i = 0; i < nchildlen; i++) {
            if ((pid = fork()) == 0) {
                for (j = 0; j < nloops; j++) {
                    fd = tcp_connect(argv[1], argv[2]);
                    if (fd > 0) {
                        write(fd, request, strlen(request));
    
                        if ((n = read(fd, reply, nbytes)) != nbytes) {
                            printf("read from server is:%s\n", reply);
                        }
                        close(fd);
                    } else {
                        break;
                    }
                }
                printf("child %d done\n", i);
                exit(0);
            }
        }
        /*waits all child process*/
        while (wait(NULL) > 0)
            ;
        if (errno != ECHILD) {
            fprintf(stderr, "wait error");
            return -1;
        }
        return 0;
    }
    

    迭代服务器代码如下:

    #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 <string.h>
    #include <errno.h>
    
    #define IP   "127.0.0.1"
    #define PORT  8888
    #define MAXLINE   4096
    
    int main()
    {
        int listenfd, connfd;
        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);
        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);
    
        char buffer[MAXLINE];
        while (1) {
            printf("begin to accept.\n");
            int connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen );
            if (connfd != -1) {
                printf("accept a connection success.ip :%s, port :%d\n", inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
            } else {
                printf("accept a connection failed,error:%s", strerror(errno));
            }
    
            int nbytes = read(connfd, buffer, MAXLINE);
            printf("read from client is:%s\n", buffer);
            write(connfd, buffer, nbytes);
    
            close(connfd);
        }
        return 0;
    }
    

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

    #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\n",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.\n", count);
                close(listenfd);
                char buffer[MAXLINE];
                int nbytes = read(connfd, buffer, MAXLINE);
                printf("read from client is:%s\n", buffer);
                write(connfd, buffer, nbytes);
                exit(0);
            }
            if (pid < 0) {
                printf("fork error");
            }
            close(connfd);
        }
        return 0;
    }
    

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

    #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 WORKER 4
    #define MAXLINE   4096
    
    int worker(int listenfd, int i)
    {
        while (1) {
            printf("I am worker %d, begin to accept connection.\n", i);
            struct sockaddr_in client_addr;
            socklen_t client_addrlen = sizeof( client_addr );
            int connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen );
            if (connfd != -1) {
                printf("worker %d accept a connection success. ip:%s, prot:%d\n", i, inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
            } else {
                printf("worker %d accept a connection failed,error:%s", i, strerror(errno));
            }
            char buffer[MAXLINE];
            int nbytes = read(connfd, buffer, MAXLINE);
            printf("read from client is:%s\n", buffer);
            write(connfd, buffer, nbytes);
            close(connfd);
        }
        return 0;
    }
    
    int main()
    {
        int i = 0;
        struct sockaddr_in address;
        bzero(&address, sizeof(address));
        address.sin_family = AF_INET;
        inet_pton( AF_INET, IP, &address.sin_addr);
        address.sin_port = htons(PORT);
        int 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);
    
        for (i = 0; i < WORKER; i++) {
            printf("Create worker %d\n", i+1);
            pid_t pid = fork();
            /*child  process */
            if (pid == 0) {
                worker(listenfd, i);
            }
            if (pid < 0) {
                printf("fork error");
            }
        }
        /*wait child process*/
        while (wait(NULL) != 0)
            ;
        if (errno == ECHILD) {
            fprintf(stderr, "wait error:%s\n", strerror(errno));
        }
        return 0;
    }
    

    相关文章

      网友评论

        本文标题:Linux网络编程客户端和服务器设计模式

        本文链接:https://www.haomeiwen.com/subject/qdpmuktx.html