千家信息网

用select实现IO的复用

发表于:2025-02-02 作者:千家信息网编辑
千家信息网最后更新 2025年02月02日,select系统调用用于一次监控多个句柄(文件描述符)的状态变化的。程序会停在select处等待,直到被监视的句柄有一个或多个发生了状态改变。select函数原型:int select(int nfd
千家信息网最后更新 2025年02月02日用select实现IO的复用

select系统调用用于一次监控多个句柄(文件描述符)的状态变化的。程序会停在select处等待,直到被监视的句柄有一个或多个发生了状态改变。

select函数原型:

int select(int nfds, fd_set *readfds, fd_set *writefd,fd_set *exceptfds, struct timeval *timeout);

nfds:表示文件描述符集的个数

readfds:当前有多少个读事件(有数据到了这个状态称之为读事件)

writefd:当前有多少个写事件(关心输出缓冲区是否已满)
最后一个结构体表示每个几秒钟醒来做其他事件,用来设置select等待时间

函数返回值:

执行成功返回描述符集当中状态已改变的个数

返回0表示在描述符状态改变前已经timeout

返回-1表示有错误发生,可能为某个套接字的带有外数据到达

关于select函数的相关函数操作

FD_CLR(inr fd,fd_set* set);来清除描述词组set中相关fd 的位

FD_ISSET(int fd,fd_set *set);判断该文件描述符是否在当前文件描述符集中

FD_SET(int fd,fd_set*set);将该描述符设置到当前文件描述符集中

FD_ZERO(fd_set *set);可以用来初始化文件描述符集

select模型中的fd_set,例:若fd_set为1字节,共有8位,则它的每一个位都可以对应一个文件描述符,若fd=1则应为0000 0001

将fd加入select监控集时,要用一个数组保存放到select监控集中的文件描述符,为了在select 返回后,该数组中保存的数据可以和fd_set进行FD_ISSET判断,select在返回后会把以前加入的但并无任何事件发生的fd清空,每次开始select前都要重新从数组取得fd

逐一加入。

使用select实现TCP通信

服务器端:

1 #include  2 #include  3 #include  4 #include  5 #include  6 #include  7 #include  8 #include  9 #include 10 int fileds[64]; //用一个数组保存select监控集中的文件描述符 11 void Usage(const char* proc) 12 { 13     printf("Usage:%s [ip] [port]\n",proc); 14 } 15  16 int start(int port,char* ip) 17 { 18     int listen_sock=socket(AF_INET,SOCK_STREAM,0); 19     if(listen_sock<0) 20     { 21         perror("socket"); 22         exit(1); 23     }  24     struct sockaddr_in local; 25     local.sin_family=AF_INET; 26     local.sin_port=htons(port); 27     local.sin_addr.s_addr=inet_addr(ip); 28     socklen_t len=sizeof(local); 29     if(bind(listen_sock,(struct sockaddr*)&local,len)<0) 30     { 31         perror("bind"); 32         exit(2); 33     } 34     if(listen(listen_sock,5)<0) 35     { 36         perror("listen"); 37         exit(3); 38     } 39     return listen_sock; 40 } 41 int main(int argc,char* argv[]) 42 { 43     if(argc!=3)  44     { 45         Usage(argv[0]); 46         exit(1); 47     } 48     char* _ip=argv[1]; 49     int _port=atoi(argv[2]); 50     int listen_sock=start(_port,_ip);//create socket 51     int newsock=-1; 52     int max_fd=0; 53     fd_set _reads;//many of read event 54     fd_set _writes;//many of write event 55     struct sockaddr_in client; 56     int fds_count=sizeof(fileds)/sizeof(fileds[0]); 57     socklen_t len=sizeof(client); 58     int i=0; 59     while(i0) 78             { 79                 FD_SET(fileds[i],&_reads); 80                 if(fileds[i]>max_fd) 81                 { 82                     max_fd=fileds[i]; 83                 } 84             } 85         } 86         switch(select(max_fd+1,&_reads,NULL,NULL,&_timeout)) 87         {//the return val is how many fd is ready  88             case 0: //timeout 89                 printf("select is timeout\n"); 90                 break; 91             case -1://error 92                 perror("select"); 93                 break; 94             default:  //less than 1 fd is ready 95             { 96                 for(i=0;i0 &&FD_ISSET(fileds[i],&_reads))122                     {123                         char buf[1024];124                         ssize_t _size=read(fileds[i],buf,sizeof(buf)-1);124                         ssize_t _size=read(fileds[i],buf,sizeof(buf)-1);125                         if(_size>0)126                         {127                             buf[_size]='\0';128                             printf("client#%s",buf);129                             write(fileds[i],buf,_size);130                         }else if(_size==0)//client close131                         {132                             printf("client is closed...\n");133                             close(fileds[i]);134                             fileds[i]=-1;//set -1,135                         }136                     }137                 }138             }139             break;140    141         }142     }143     return 0;144 }


客户端:


1 #include  2 #include  3 #include  4 #include  5 #include  6 #include  7 #include  8   9 void Usage(const char*proc) 10 { 11     printf("Usage:%s [remoteip] [remoteport]\n",proc); 12 } 13  14 int main(int argc,char* argv[]) 15 { 16     if(argc!=3) 17      { 18          Usage(argv[0]); 19          exit(1); 20      } 21  22      int _port=atoi(argv[2]); 23      char *_ip=argv[1]; 1 #include  2 #include  3 #include  4 #include  5 #include  6 #include  7 #include  8   9 void Usage(const char*proc) 10 { 11     printf("Usage:%s [remoteip] [remoteport]\n",proc); 12 } 13  14 int main(int argc,char* argv[]) 15 { 16     if(argc!=3) 17      { 18          Usage(argv[0]); 19          exit(1); 20      } 21  22      int _port=atoi(argv[2]); 23      char *_ip=argv[1]; 24      int sock=socket(AF_INET,SOCK_STREAM,0); 25      if(sock<0) 26      { 27          perror("socket"); 28          exit(2); 29      } 30      struct sockaddr_in remote; 31      remote.sin_family=AF_INET; 32      remote.sin_port=htons(_port); 33      remote.sin_addr.s_addr=inet_addr(_ip); 34      int ret=connect(sock,(struct sockaddr*)&remote,sizeof(remote)); 35      if(ret<0) 36      { 37          perror("connect"); 38          exit(3); 39      } 40      char buf[1024]; 41      while(1) 42      { 43          memset(buf,'\0',sizeof(buf)); 44          printf("please input:"); 45          fflush(stdout); 46          if(read(0,buf,sizeof(buf)-1)>0) 47          { 48              write(sock,buf,strlen(buf)); 49          } 50     51          ssize_t _size=read(sock,buf,sizeof(buf)-1); 52          if(_size>0) 53          { 54              printf("server----client:%s",buf); 55          } 56     57      } 58      return 0; 59 }


结果回显:

使用select实现多路复用同时也存在弊端:

每次调用select,都需要把fd集合从用户态拷贝到内核态,需要在内核中遍历传递进的所有文件描述符,当fd很多时导致开销大

 


0