• 10.4 基于多任务的并发服务器


    基于进程的并发服务器模型


    从图中可以看出,每当有客户端请求时(连接请求),回声服务器都创建子进程以提供服务。如果请求
    的客户端有 5 个,则将创建 5 个子进程来提供服务,为了完成这些任务,需要经过如下过程:

    • 第一阶段:回声服务器端(父进程)通过调用 accept 函数受理连接请求
    • 第二阶段:此时获取的套接字文件描述符创建并传递给子进程
    • 第三阶段:进程利用传递来的文件描述符提供服务

    下面是基于多进程实现的并发的回声服务器的服务端:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <signal.h>
    #include <sys/wait.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    
    #define BUF_SIZE 30
    
    void error_handling(char *message);
    void read_childproc(int sig);
    
    int main(int argc, char *argv[])
    {
        int serv_sock = 0;
        int clnt_sock = 0;
        struct sockaddr_in serv_adr;
        struct sockaddr_in clnt_adr;
        socklen_t adr_sz;
        int str_len = 0;
    
        char buf[BUF_SIZE];
        pid_t pid;
        int state = 0;
        struct sigaction act;
    
        if (argc != 2)
        {
            printf("Usgae : %s <port>
    ", argv[0]);
            exit(1);
        }
        act.sa_handler = read_childproc; //防止僵尸进程
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        state = sigaction(SIGCHLD, &act, 0); //注册信号处理器,把成功的返回值给state
    
        serv_sock = socket(AF_INET, SOCK_STREAM, 0); //创建服务端套接字
        memset(&serv_adr, 0, sizeof(serv_adr));
        serv_adr.sin_family = AF_INET;
        serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_adr.sin_port = htons(atoi(argv[1]));
    
        if (-1 == bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr))) //分配IP地址和端口号
        {
            error_handling("bind() error");
        }
        if (-1 == listen(serv_sock, 5)) //进入等待连接请求状态
        {
            error_handling("listen() error");
        }
        while (1)
        {
            adr_sz = sizeof(clnt_adr);
            clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &adr_sz);
            if (clnt_sock == -1)
            {
                continue;
            }
            else
            {
                puts("new client connected...");
            }
            pid = fork(); //此时,父子进程分别带有一个套接字
            if (pid == -1)
            {
                close(clnt_sock);
                continue;
            }
            if (pid == 0) //子进程运行区域,此部分向客户端提供回声服务
            {
                close(serv_sock); //关闭服务器套接字,因为从父进程传递到了子进程
                while ((str_len = read(clnt_sock, buf, BUFSIZ)) != 0)
                {
                    write(clnt_sock, buf, str_len);
                }
                close(clnt_sock);
                puts("client disconnected...");
                return 0;
            }
            else
            {
                close(clnt_sock); //通过 accept 函数创建的套接字文件描述符已经复制给子进程,因为服务器端要销毁自己拥有的
            }
        }
        close(serv_sock);
        return 0;
    }
    
    void error_handling(char *message)
    {
        fputs(message, stderr);
        fputc('
    ', stderr);
        exit(1);
    }
    
    void read_childproc(int sig)
    {
        pid_t pid;
        int status;
        pid = waitpid(-1, &status, WNOHANG);
        printf("removed proc id: %d 
    ", pid);
    }
    

    下面是基于多进程实现的并发的回声服务器的客户端:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    
    #define BUF_SIZE 1024
    void error_handling(char *message);
    
    int main(int argc, char *argv[])
    {
        int sock = 0;
        int str_len = 0;
        char message[BUF_SIZE];
    
        struct sockaddr_in serv_adr;
    
        if (argc != 3)
        {
            printf("Usage : %s <IP> <port>
    ", argv[0]);
            exit(1);
        }
    
        sock = socket(AF_INET, SOCK_STREAM, 0);
        if (-1 == sock)
        {
            error_handling("socket() error");
        }
        memset(&serv_adr, 0, sizeof(serv_adr));
        serv_adr.sin_family = AF_INET;
        serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
        serv_adr.sin_port = htons(atoi(argv[2]));
    
        if (-1 == connect(sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)))
        {
            error_handling("connect() error!");
        }
        else
        {
            puts("Connected...........");
        }
        while (1)
        {
            fputs("Input message(Q to quit): ", stdout);
            fgets(message, BUF_SIZE, stdin);
    
            if (!strcmp(message, "q
    ") || !strcmp(message, "Q
    "))
            {
                break;
            }
            write(sock, message, strlen(message));
            str_len = read(sock, message, BUF_SIZE - 1);
            message[str_len] = 0;
            printf("Message from server: %s", message);
        }
        close(sock);
        return 0;
    }
    
    void error_handling(char *message)
    {
        fputs(message, stderr);
        fputc('
    ', stderr);
        exit(1);
    }
    

    运行结果:


    关闭两个客户端之后:

  • 相关阅读:
    mqtt 服务器与客户端通讯
    MQTT--单片机实现即时通信
    source insight 如何建工程--以及快捷方式查找调用函数方法
    在联网时,两台linux服务器传输文件方法
    字符串截取函数-c语言
    strcpy(),strcat()的用法
    如何将红色区域数据调用解密函数直接打印到输出控制台(例如:crt控制台)
    如何把apdu[decode_len]打印出来
    SecureCRT中文乱码解决已设置UTF-8
    RobotFrameWork(五)控制流之if语句——Run Keyword If
  • 原文地址:https://www.cnblogs.com/wsl540/p/14713417.html
Copyright © 2020-2023  润新知