IO 多路转接 select

 

select多路IO转接:

    原理:  借助内核,select来监听,客户端连接、数据通信事件

    void FD_CLR(int fd, fd_set *set);   将一个文件描述符从监听集合中 移除
    
        FD_CLR(4,&rset)

        
       int  FD_ISSET(int fd, fd_set *set);  判断一个文件描述符是否在监听集合中

        FD_ISSET(4,&rset)

       void FD_SET(int fd, fd_set *set);    将待监听的文件描述符添加到 监听集合中

        FD_SET(4,&rset)

       void FD_ZERO(fd_set *set);           清空一个文件描述符集合


        FD_ZERO(4,&rset)


     int select(int nfds, fd_set *readfds, fd_set *writefds,

                  fd_set *exceptfds, struct timeval *timeout);
    


        nfds: 监听的所有文件描述符中,最大的文件描述符 +1

        readfds: 读 文件描述符监听集合     传入传出 参数

        writefds: 写 文件描述符监听集合      传入传出 参数


        exceptfds: 异常 文件描述符监听集合     传入传出 参数


        timeout:  》0 : 设置监听超时时长

              NULL  阻塞监听

              0    非阻塞 轮训监听


        返回值:    

            》0 所有监听集合(3个)中, 满足对应事件的总数

            0   没有满足条件 的文件描述符

            -1 : errno

思路分析:

    lfd = socket();         创建套接字

    bind();            绑定地址结构

    listen()            设置监听上限

    fd_set rset,allset;          创建监听集合

    FD_ZERO(&allset)        将监听集合清空

    FD_SET(lfd,&allset)        将lfd添加到读集合中

    while(1){

        rset = allset
    
        ret = select(lfd+1,&rset,NULL,NULL,NULL)  监听文件描述符集合对应事件

        if(ret>0){

        
            if(FD_ISSET(lfd,&rset)){

                cfd =accept()

                FD_SET(cfd,&allset)

            }

            for(i = lfd+1;i<=最大文件描述符;i++){


                FD_ISSET(I,&rset)

                read()

                小 -- 大

                write();

    
            }

        }        


    }


select优缺点:

    缺点: 监听上限受文件描述符限制 最大 1024

        检测满足条件的 fd, 自己添加业务逻辑  提高可编码难度

        返回满足监听条件的文件描述符个数 并没有直接告诉哪些文件描述符就绪了 需要从头遍历传出的事件集合

       每次调用 select, 都需要手动设置 fd 集合 , 从接口使用角度来说也非常不便 .
 
       每次调用 select ,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大
 
        同时每次调用 select 都需要在内核遍历传递进来的所有 fd ,这个开销在 fd 很多时也很大
 

    优点:跨平台。 

    

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<ctype.h>


#define SERV_PORT  6666

int main(){


  int i,j,n,nready;
  int maxfd =0 ;

  int listenfd,connfd;
  char buf[BUFSIZ];

  struct sockaddr_in clie_addr,serv_addr;
  socklen_t clie_addr_len;

  listenfd = socket(AF_INET,SOCK_STREAM,0);
  int opt =1;
  setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
  bzero(&serv_addr,sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(SERV_PORT);
  serv_addr.sin_addr.s_addr= htonl(INADDR_ANY);
  bind(listenfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
  listen(listenfd,128);
  fd_set rset,allset;             //定义读集合,备份集合allset
  maxfd = listenfd;               //最大文件描述符
  FD_ZERO(&allset);               //清空监听集合
  FD_SET(listenfd,&allset);       //将fd 添加到集合
  while(1){
    rset = allset;                
    nready = select (maxfd+1,&rset,NULL,NULL,NULL);   //使用select监听
    if(nready<0){
      perror("select error");
      exit(1);
    }
    if(FD_ISSET(listenfd,&rset)){         //listenfd 满足监听的 读事件
      clie_addr_len = sizeof(clie_addr);
      connfd = accept(listenfd,(struct sockaddr*)&clie_addr,&clie_addr_len);//建立连接 不阻塞

      FD_SET(connfd,&allset);       //将产生的fd 添加到监听集合中,监听数据读事件
      if(maxfd<connfd)          //修改 maxfd
        maxfd= connfd;
      if(nready == 1)         //说明select只 返回一个 ,并且是listenfd 后续执行无需执行
        continue;

    }
    for(i=listenfd+1;i<=maxfd;++i){   //处理满足读事件的 fd
      if(FD_ISSET(i,&rset)){          //找到满足读事件的那个fd
        if((n=read(i,buf,sizeof(buf)))==0){ //检测到客户端已经关闭连接
          close(i);                 
          FD_CLR(i,&allset);        //将关闭的fd 移除监听集合
        }else if(n>0){
          for(j=0;j<n;j++)
            buf[j]=toupper(buf[j]);
          write(i,buf,n);
          write(STDOUT_FILENO,buf,n);

        }
      }
    }

  }
  close(listenfd);
  return 0;
}
// 优化后 select

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<ctype.h>


#define SERV_PORT  6666

int main(){


  int i,j,n,nready,maxi;
  
  int client[FD_SETSIZE];  //自定义数组client 防止遍历 1024个文件描述符 FD_SETSIZE 为1024
  int maxfd =0 ;

  int listenfd,connfd;
  char buf[BUFSIZ];

  struct sockaddr_in clie_addr,serv_addr;
  socklen_t clie_addr_len;

  listenfd = socket(AF_INET,SOCK_STREAM,0);
  int opt =1;
  setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
  bzero(&serv_addr,sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(SERV_PORT);
  serv_addr.sin_addr.s_addr= htonl(INADDR_ANY);
  bind(listenfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
  listen(listenfd,128);

  fd_set rset,allset;             //定 读 集和,备份集和allset
  maxfd = listenfd;               //最大文件描述符
  

  maxi = -1;
  for(i=0;i<FD_SETSIZE;++i){      //用-1 初始化 client【】
    client[i]=-1;
  }


  FD_ZERO(&allset);               //清空监听集合
  FD_SET(listenfd,&allset);       //将fd 添加到集合
  while(1){
    rset = allset;                
    nready = select (maxfd+1,&rset,NULL,NULL,NULL);   //使用select监听
    if(nready<0){
      perror("select error");
      exit(1);
    }
    if(FD_ISSET(listenfd,&rset)){         //listenfd 满足监听的 读事件
      clie_addr_len = sizeof(clie_addr);
      connfd = accept(listenfd,(struct sockaddr*)&clie_addr,&clie_addr_len);//建立连接 不阻塞


      for(i=0;i<FD_SETSIZE;i++){
        if(client[i]<0){
          client[i]=connfd;
          break;
        }
      }
      if(i==FD_SETSIZE){
        fputs("too many clients\n",stderr);//达到select能监控的 上限 1024
        exit(1);
      }




      FD_SET(connfd,&allset);       //将产生的fd 添加到监听集合中,监听数据读事件
      if(maxfd<connfd)          //修改 maxfd
        maxfd= connfd;

      if(i>maxi)
        maxi =i;     //保证maxi总是存的 client【】最后一个元素下表



      if(nready == 1)         //说明select只 返回一个 ,并且是listenfd 后续执行无需执行
        continue;

    }
    for(i=0;i<=maxi;++i){   //处理满足读事件的 fd
      if((listenfd=client[i])<0)
        continue;

      if(FD_ISSET(client[i],&rset)){          //找到满足读事件的那个fd
        if((n=read(client[i],buf,sizeof(buf)))==0){ //检测到客户端已经关闭连接服务器也关闭
          close(client[i]);                 
          FD_CLR(client[i],&allset);        //将关闭的fd 移除监听集合
          client[i]=-1;
        }else if(n>0){
          for(j=0;j<n;j++)
            buf[j]=toupper(buf[j]);
          write(client[i],buf,n);
          write(STDOUT_FILENO,buf,n);

        }
        
      }
    }

  }
  close(listenfd);
  return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值