zoukankan      html  css  js  c++  java
  • 系统编程-网络-服务器并发编程模型

    之前介绍的服务器端代码只是基础功能,在支持多客户端访问时将面临困局。因为,我们来介绍服务器并发编程模型。

    server.c

    #include <errno.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    
    #include <stdint.h>
    
    #include <string.h>
    #include "server.h"
    #include <assert.h>
    
    #include <sys/types.h>
    #include <unistd.h>
    #include <signal.h>
    
    #include <sys/types.h>
    #include <sys/wait.h>
    
    #include <errno.h>
    // 在Linux网络编程这块,,胡乱包含过多头文件会导致编译不过。
    //#include <linux/tcp.h>  // 包含下方这个头文件,就不能包含该头文件,否则编译报错。
    #include <netinet/tcp.h> // setsockopt 函数 需要包含此头文件
    
    
    int server_local_fd, new_client_fd;
    
    void sig_deal(int signum){
    
        if(signum == SIGINT){
            close(new_client_fd);
            close(server_local_fd);
            exit(0);
    
        }else if(signum == SIGCHLD){
            wait(NULL);
        }
    }
    
    int main(void)
    {
        struct sockaddr_in sin;
    
        signal(SIGINT,  sig_deal);
        signal(SIGCHLD, sig_deal);
    
        printf("pid = %d 
    ", getpid());
    
         /*1.创建IPV4的TCP套接字 */    
        server_local_fd = socket(AF_INET, SOCK_STREAM, 0);
        if(server_local_fd < 0) {
            perror("socket error!");
            exit(1);    
        }
    
         /* 2.绑定在服务器的IP地址和端口号上*/
         /* 2.1 填充struct sockaddr_in结构体*/
         bzero(&sin, sizeof(sin));
         sin.sin_family = AF_INET;
         sin.sin_port = htons(SERV_PORT);
    
        #if 0 
         // 方式一
         sin.sin_addr.s_addr = inet_addr(SERV_IPADDR); 
        #endif
    
        #if 0
         // 方式二: 
         sin.sin_addr.s_addr = INADDR_ANY; 
        #endif
    
        #if 1
         // 方式三: inet_pton函数来填充此sin.sin_addr.s_addr成员 
         if(inet_pton(AF_INET, "192.168.1.21", &sin.sin_addr.s_addr) >0 ){
             char buf[16] = {0};
             printf("s_addr=%s 
    ", inet_ntop(AF_INET, &sin.sin_addr.s_addr, buf, sizeof(buf)));
             printf("buf = %s 
    ", buf);
         }
        #endif
    
         /* 2.2 绑定*/
        if(bind(server_local_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
            perror("bind");
                   exit(1);    
        }    
    
        /*3.listen */
        listen(server_local_fd, 5);
            
        printf("client listen 5. 
    ");
    
    
        char sned_buf[] = "hello, i am server 
    ";
    
        struct sockaddr_in clientaddr;
        socklen_t clientaddrlen; 
    
        char client_commu_recv_data_buf[100]={0};
        char client_commu_send_data_buf[100]= {"I am server
    "};
    
        while(1){
    
        /*4. accept阻塞等待客户端连接请求 */
            /****获取连接上来的客户端的信息******/
            memset(&clientaddr, 0, sizeof(clientaddr));
            memset(&clientaddrlen, 0, sizeof(clientaddrlen));
    
            clientaddrlen = sizeof(clientaddr);
            /***
             * 由于cliaddr_len是一个传入传出参数(value-result argument), 
             * 传入的是调用者提供的缓冲区的长度以避免缓冲区溢出问题,  
             * 传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区).
             * 所以,每次调用accept()之前应该重新赋初值。
             * ******/
            if( (new_client_fd = accept(server_local_fd, (struct sockaddr*)&clientaddr, &clientaddrlen)) < 0) {  
                perror("accept");
                exit(1);    
            }
    
            printf("new client connected!  print the client info .... 
    ");
            int port = ntohs(clientaddr.sin_port);                    
            char ip[16] = {0};
            inet_ntop(AF_INET, &(clientaddr.sin_addr.s_addr), ip, sizeof(ip));
            printf("client: ip=%s, port=%d 
    ", ip, port);
    
            pid_t pid = fork();
            if(pid < 0){
                continue;
            
            }else if(0 == pid){ // child process
    
                close(server_local_fd); 
    
                printf("server goes to read... 
    ");
                int bytes_read_done = read(new_client_fd, client_commu_recv_data_buf, sizeof(client_commu_recv_data_buf));
                printf("bytes_read_done = %d 
    ", bytes_read_done);
    
                // sleep(10);
    
                printf("strlen(client_commu_send_data_buf) = %d 
    ", strlen(client_commu_send_data_buf));
                int bytes_write_done = write(new_client_fd, client_commu_send_data_buf, strlen(client_commu_send_data_buf));
                printf("bytes_write_done = %d 
    ", bytes_write_done);
                if(bytes_write_done < 0){
                    if(errno == EPIPE){
                        printf("server : write -> EPIPE 
    ");
                        close(new_client_fd);
                        exit(0);
                    }
                }
                printf("--Server deal this client over! 
    ");
                close(new_client_fd);
                exit(0);
    
            }else{ // parent process
    
                close(new_client_fd);
            }
        }
    
        // the following code will nerver run ....
        printf("server process end... 
    ");
        close(server_local_fd);
    
        return 0;
    }

    client.c

    #include <unistd.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<netdb.h>
    #include<string.h>
    #include<errno.h>
    #include<stdio.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <signal.h>
    
    #define PORT 5001
    #define SERVER_IP "192.168.1.21"
    
    void sig_handler(int signo){
        printf("sig_handler=> pid: %d, signo: %d 
    ", getpid(), signo);
    }
    
    int main()
    {
        int sockfd;
    
        struct sockaddr_in server_addr;
        struct hostent *host;
     
        if(signal(SIGPIPE, sig_handler) == SIG_ERR){
        //if(signal(SIGPIPE, SIG_DFL) == SIG_ERR){ // SIGPIPE信号的默认执行动作是terminate(终止、退出),所以本进程会退出。
            perror("signal error");
        }
    
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        {
            fprintf(stderr, "Socket Error is %s
    ", strerror(errno));
            exit(EXIT_FAILURE);
        }
        bzero(&server_addr, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(PORT);
        server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
    
        if (connect(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr)) == -1)
        {
            fprintf(stderr, "Connect failed
    ");
            exit(EXIT_FAILURE);
        }
    
        char sendbuf[1024] = {0};
        char recvbuf[2014] = {0};
    
        while (1)
        {
            fgets(sendbuf, sizeof(sendbuf), stdin);
            printf("strlen(sendbuf) = %d 
    ", strlen(sendbuf));
    
            if (strcmp(sendbuf, "exit
    ") == 0){
                printf("while(1) -> exit 
    ");
                break;  
            }
    
            send(sockfd, sendbuf, strlen(sendbuf), 0);
    
            recv(sockfd, recvbuf, sizeof(recvbuf), 0);
            fputs(recvbuf, stdout);
    
            memset(sendbuf, 0, sizeof(sendbuf));
            memset(recvbuf, 0, sizeof(recvbuf));
        }
    
        close(sockfd);
        printf(" client process end 
    ");
    
        return 0;
    }

    编译运行:

    服务器程序

    客户端1、客户端2程序

    .

    /************* 社会的有色眼光是:博士生、研究生、本科生、车间工人; 重点大学高材生、普通院校、二流院校、野鸡大学; 年薪百万、五十万、五万; 这些都只是帽子,可以失败千百次,但我和社会都觉得,人只要成功一次,就能换一顶帽子,只是社会看不见你之前的失败的帽子。 当然,换帽子决不是最终目的,走好自己的路就行。 杭州.大话西游 *******/
  • 相关阅读:
    异步请求模板和数据
    关于线上js报错问题的思考
    标准web浏览器的组件
    监控图片加载的方法
    图片轮播
    jQuery的一些小技巧()
    setTimeout/setInterval伪异步
    linux/windows java jdk环境配置
    iOS pod 第三方 unrecognized selector sent to instance
    上海4
  • 原文地址:https://www.cnblogs.com/happybirthdaytoyou/p/14674537.html
Copyright © 2011-2022 走看看