十二、IO复用

采用多进程固然能很好的实现业务分离,但是开辟一个新进程需要的资源太多,而且一些简单的业务本不需要开辟一个新进程进行处理,因此出现严重的资源浪费,能不能有一种技术不仅仅能替代多进程的移步分离处理多客户端情况,而且会造成太大的资源浪费。于是I/O技术就被提出来了,I/O复用关键在于掌握select函数的使用。
本章主要任务是采用I/O改写之前的基于多进程回声服务端设计,使得服务器端开辟一个进程就能同时处理多个客户端之间的数据交换。

select函数

关于该函数的介绍,我觉得这篇博客讲得非常清楚,所以不再重复叙述。

select 函数测试程序

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
#define BUF_SIZE 30

int main(int argc, char const *argv[])
{
    fd_set reads, temps;
    int result,str_len;
    char buf[BUF_SIZE];
    struct timeval timeout;
    //初始化
    FD_ZERO(&reads);
    /**
     * @brief  将标准输入加入监听文件描述符集
     * @note   
     * @retval 
     */
    FD_SET(0,&reads);
    /**
     * @brief  timeout.tv_sec=5, 
     * @note   
     * @retval None
     */
    while (1)
    {
       //不断更新监听的文件描述符集
       temps=reads;

       timeout.tv_sec=5;
       timeout.tv_usec=0;
       /**
        * @brief  select函数的使用
        * @note   
        * @retval 
        */
       result=select(1,&temps,0,0,&timeout);
       if(result==-1)
       {
           puts("select () error!");
           break;
       }
       else if(result==0)
       {
           puts("Timeout!");
       }
       else{
           /**
            * @brief  fd=0代表标准输入文件描述符,在FD_ISSET参数设置里面第一个参数为0就是指标准输入,这个宏用于确定fd所指的文件描述符是否在监听文件描述符集里面。
            * @note   
            * @retval 
            */
           if(FD_ISSET(0,&temps))
           {
               str_len=read(0,buf,BUF_SIZE);
               buf[str_len]=0;
               printf("message from console :%s\n",buf);
           }
       }
    }
    
    return 0;
}

实现I/O复用服务器

  • 客户端实现代码(在之前章节里面随便找一个客户端代码即可)

    //client.c
    #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;
        char message[BUF_SIZE];
        int str_len;
        struct sockaddr_in serv_adr;
        if(argc!=3)
        {
            printf("Usage:%s<IP><port> \n",argv[0]);
            exit(1);
        }
        sock=socket(PF_INET,SOCK_STREAM,0);
        if(sock==-1)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(connect(sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1)
        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\n") || !strcmp(message,"Q\n"))
            {
                printf("testing......\n");
                   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('\n',stderr);
      exit(1);
    }
    
    
  • 服务端实现代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <sys/time.h>
    #include <sys/select.h>
    
    #define BUF_SIZE 100
    void error_handling(char* message);
    
    int main(int argc, char const *argv[])
    {
        int serv_sock,clnt_sock;
        struct sockaddr_in serv_adr,clnt_adr;
        struct timeval timeout;
        fd_set reads,cpy_reads;
    
        socklen_t adr_sz;
        int fd_max,str_len,fd_num,i;
        char buf[BUF_SIZE];
        if(argc!=2)
        {
            printf("Usage : %s <port> \n",argv[0]);
            exit(1);
        }
        
        serv_sock=socket(PF_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]));
    
    
        /**
         * @brief  绑定接口与地址
         * @note   
         * @retval None
         */
        if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1) error_handling("bind() error");
       
        /**
         * @brief  监听请求连接
         * @note   
         * @retval None
         */
        if(listen(serv_sock,5)==-1)error_handling("listen() error");
    
    
        /**
         * @brief  初始化监听套接字集
         * @note   
         * @retval None
         */
        FD_ZERO(&reads);
        FD_SET(serv_sock,&reads);
        /**
         * @brief  注意理解这行代码
         * @note   
         * @retval None
         */
        fd_max=serv_sock;
        while(1)
        {
            cpy_reads=reads;
    
            timeout.tv_sec=5;
            timeout.tv_usec=5000;
    
            if((fd_num=select(fd_max+1,&cpy_reads,0,0,&timeout))==-1)break;
            if(fd_num==0)continue;
    
            for(i=0;i<fd_max+1;i++)
            {
                if(FD_ISSET(i,&cpy_reads))
                {
                    if(i==serv_sock)//处理连接
                    {
                        adr_sz=sizeof(clnt_adr);
                        clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_adr,&adr_sz);
                        FD_SET(clnt_sock,&reads);
    
                        if(fd_max<clnt_sock)fd_max=clnt_sock;
                        printf("connected client: %d\n",clnt_sock);
                    }
                    else{   //读取message 
                        str_len=read(i,buf,BUF_SIZE);
                        if(str_len==0)
                        {
                            FD_CLR(i,&reads);
                            close(i);
                            printf("closed client : %d\n",i);
                        }else{
                            write(i,buf,str_len);
                        }
                        
                    }
                }
            }
        }
       close(serv_sock);
    
        return 0;
    }
    void error_handling(char *message)
    {
       fputs(message,stderr);
       fputc('\n',stderr);
       exit(1);
    }
    
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值