系统编程-网络-服务器并发编程模型

 

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

 

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 \n", 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 \n", inet_ntop(AF_INET, &sin.sin_addr.s_addr, buf, sizeof(buf)));
         printf("buf = %s \n", 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. \n");


    char sned_buf[] = "hello, i am server \n";

    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\n"};

    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 .... \n");
        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 \n", 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... \n");
            int bytes_read_done = read(new_client_fd, client_commu_recv_data_buf, sizeof(client_commu_recv_data_buf));
            printf("bytes_read_done = %d \n", bytes_read_done);

            // sleep(10);

            printf("strlen(client_commu_send_data_buf) = %d \n", 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 \n", bytes_write_done);
            if(bytes_write_done < 0){
                if(errno == EPIPE){
                    printf("server : write -> EPIPE \n");
                    close(new_client_fd);
                    exit(0);
                }
            }
            printf("--Server deal this client over! \n");
            close(new_client_fd);
            exit(0);

        }else{ // parent process

            close(new_client_fd);
        }
    }

    // the following code will nerver run ....
    printf("server process end... \n");
    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 \n", 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\n", 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\n");
        exit(EXIT_FAILURE);
    }

    char sendbuf[1024] = {0};
    char recvbuf[2014] = {0};

    while (1)
    {
        fgets(sendbuf, sizeof(sendbuf), stdin);
        printf("strlen(sendbuf) = %d \n", strlen(sendbuf));

        if (strcmp(sendbuf, "exit\n") == 0){
            printf("while(1) -> exit \n");
            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 \n");

    return 0;
}

编译运行:

服务器程序

 

 

客户端1、客户端2程序

 

 

 

.

/************* 社会的有色眼光是:博士生、研究生、本科生、车间工人; 重点大学高材生、普通院校、二流院校、野鸡大学; 年薪百万、五十万、五万; 这些都只是帽子,可以失败千百次,但我和社会都觉得,人只要成功一次,就能换一顶帽子,只是社会看不见你之前的失败的帽子。 当然,换帽子决不是最终目的,走好自己的路就行。 杭州.大话西游 *******/
posted @ 2021-05-06 16:59  一匹夫  阅读(29)  评论(0编辑  收藏  举报