zoukankan      html  css  js  c++  java
  • 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,会产生"惊群"问题:

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

    还有一点需要补充的是:

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

  • 相关阅读:
    git线上操作
    IDEA快捷方式
    Java 四种线程池
    java 获取当前天之后或之前7天日期
    如何理解AWS 网络,如何创建一个多层安全网络架构
    申请 Let's Encrypt 通配符 HTTPS 证书
    GCE 部署 ELK 7.1可视化分析 nginx
    使用 bash 脚本把 AWS EC2 数据备份到 S3
    使用 bash 脚本把 GCE 的数据备份到 GCS
    nginx 配置 https 并强制跳转(lnmp一键安装包)
  • 原文地址:https://www.cnblogs.com/gattaca/p/6421290.html
Copyright © 2011-2022 走看看