• C/S程序设计范式


    socket编程之并发回射服务器3篇文章中,提到了3种设计范式:

    多进程

        父进程阻塞于accept调用,然后为每个连接创建一个子进程。

    多线程

        主线程阻塞于accept调用,然后为每个连接创建一个子线程。

    I/O复用

        主进程阻塞于select调用,select负责监听listenfd和connfd。

    本文描述第4种设计范式:prefork

        prefork是指父进程预先派生若干个子进程,然后每个子进程阻塞于accept调用。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <signal.h>
    #include <sys/wait.h>
    #include <sys/errno.h>
    
    #define MAXLINE 4096
    #define LISTENQ 10
    #define PORT 8888
    
    int tcp_listen(const char *port);
    pid_t child_make(int i, int listenfd);
    void child_main(int i, int listenfd);
    void doEcho(int sockfd);
    void sig_int(int signo);
    
    
    static int        nchildren;
    static pid_t    *pids;
    
    int main(int argc, char **argv) {
        
        if (argc != 2) {
            printf("Usage: a.out nchildren
    ");
            exit(1);
        }
        int listenfd = tcp_listen("8888");
        nchildren = atoi(argv[1]);
        pids = (pid_t*)calloc(nchildren, sizeof(pid_t));
        for (int i = 0; i < nchildren; i++) {
            pids[i] = child_make(i, listenfd);
        }
        signal(SIGINT, sig_int);
        for (; ; ) {
            pause();
        }
    }
    
    int tcp_listen(const char *port) {
        int listenfd;
        struct sockaddr_in servaddr;
        
        if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("socket error");
            return -1;
        }
        
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(PORT);
        
        if ( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
            perror("bind error");
            return -1;
        }
        
        if ( listen(listenfd, LISTENQ) < 0) {
            perror("listen error");
            return -1;
        }
        return listenfd;
    }
    
    pid_t child_make(int i, int listenfd) {
        pid_t pid;
        if ( (pid = fork()) > 0) {
            return pid;
        }
        child_main(i, listenfd);
    }
    
    void child_main(int i, int listenfd) {
        int connfd;
        struct sockaddr cliaddr;
        socklen_t clilen;
        
        printf("child %ld starting
    ", (long)getpid());
        
        for (; ; ) {
            clilen = sizeof(cliaddr);
            if ( (connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen)) < 0) {
                if (errno == EINTR) {
                    continue;
                } else {
                    perror("accept error");
                    exit(1);
                }
            }
            doEcho(connfd);
            close(connfd);
        }
    }
    
    void doEcho(int sockfd) {
        char buff[MAXLINE];
        while (true) {
            memset(buff, 0, sizeof(buff));
            int n = read(sockfd, buff, MAXLINE);
            if (n < 0) {
                perror("read error");
                exit(1);
            } else if (n == 0) {
                printf("client closed
    ");
                break;
            }
            fputs(buff, stdout);
            write(sockfd, buff, n);
        }
    }
    
    void sig_int(int signo) {
        for (int i = 0; i < nchildren; i++) {
            kill(pids[i], SIGTERM);
        }
        while (wait(NULL) > 0)
            ;
        if (errno != ECHILD) {
            perror("wait error");
            exit(1);
        }
        exit(0);
    }
    View Code

    多个进程在同一个listenfd上调用accept,会产生"惊群"问题:

        一个连接到来,所有进程都被唤醒,但只有一个进程能够成功获得连接。

    还有一点需要补充的是:

        父进程应该监视闲置子进程个数,随着所服务客户数的变化动态增减子进程个数。

  • 相关阅读:
    VirtualBox中的网络连接方式详解
    DRUID连接池的实用 配置详解
    redis之如何配置jedisPool参数
    怎么把myeclipse项目导入IDEA中
    最新Hadoop大数据开发学习路线图
    编程能力七段论(下)
    编程能力七段论(上)
    移动无线测试技能树
    WebView加载网页不显示图片解决办法
    编程能力七段论
  • 原文地址:https://www.cnblogs.com/gattaca/p/6421290.html
Copyright © 2020-2023  润新知