select实现I/O复用

来源:互联网 发布:mac电脑激活时间查询 编辑:程序博客网 时间:2024/05/17 00:59

select:


系统提供select函数来实现多路复用输入/输出模型。select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。关于文件句柄,其实就是一个整数,我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,2是标准错误输出。0、1、2是整数表示的对应的FILE *结构的表示就是stdin、stdout、stderr。


select函数:

       #include <sys/time.h>       #include <sys/types.h>       #include <unistd.h>       int select(int nfds, fd_set *readfds, fd_set *writefds,                  fd_set *exceptfds, struct timeval *timeout);


 参数nfds是需要监视的最大的文件描述符值+1;

 

 rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符的集合。

 

struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。


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

FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真

FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位

FD_ZERO(fd_set *set);用来清除描述词组set的全部位


参数timeout为结构timeval,用来设置select()的等待时间,

(1)如果参数timeout设为:

NULL:则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了

事件。

(2)0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。

(3)特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。


函数返回值:


执行成功则返回文件描述词状态已改变的个数


如果返回0代表在描述词状态改变前已超过timeout时间,没有返回;


当有错误发生时则返回-1,



select实现I/0复用:

tcp_server.c:

  1 #include<stdio.h>  2 #include<stdlib.h>  3 #include<sys/types.h>  4 #include<sys/socket.h>  5 #include<arpa/inet.h>  6 #include<netinet/in.h>  7 #include<assert.h>  8 #include<unistd.h>  9  10 int fds[64]; 11 const int back_log=5; 12 void usage(char* argv) 13 {  14   printf("%s:[ip][port]\n",argv); 15 } 16 int start_up(char* ip,int port) 17 { 18     //sock 19   int sock=socket(AF_INET,SOCK_STREAM,0); 20   if(sock<0) 21   { 22       perror("sock"); 23       exit(0); 24   } 25   struct sockaddr_in local; 26   local.sin_port=htons(port); 27   local.sin_family=AF_INET; 28   local.sin_addr.s_addr=inet_addr(ip); 29  30   //bind 31   if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) 32   { 33       perror("bind"); 34       exit(1); 35   } 36   //listen 37   if(listen(sock,back_log)<0) 38   { 39       perror("sock"); 40       exit(1); 41   } 42   return sock; 43 } 44 int main(int argc,char* argv[])                         45 { 46     if(argc!=3) 47     { 48         usage(argv[0]); 49         exit(1); 50     } 51     int port=atoi(argv[2]); 52     char* ip=argv[1]; 53  54  55     int done=0; 56     int new_sock=-1; 57     int listen_sock=start_up(ip,port); 58     struct sockaddr_in client; 59     socklen_t len=sizeof(client); 60  61     int max_fd; 62     fd_set _reads; 63     fd_set _writes; 64  65     int i=0; 66     int fds_num=sizeof(fds)/sizeof(fds[0]); 67     for(i=0;i<fds_num;i++) 68     { 69        fds[i]=-1; 70     } 71     fds[0]=listen_sock; 72     max_fd=fds[0]; 73  74     while(!done) 75     { 76         FD_ZERO(&_reads);   //每次循环把_reads,_writes初始化(输入、输出 参数) 77         FD_ZERO(&_writes); 78         FD_SET(listen_sock,&_reads);    //把listen_sock加到_reads文件描 述符集中 79         struct timeval _timeout={5,0};  //设置等待时间 80         for(i=0;i<fds_num;i++) 81         { 82             if(fds[i]>0) 83             { 84                 FD_SET(fds[i],&_reads);   85                 if(fds[i]>max_fd) 86                 { 87                     max_fd=fds[i]; 88                 } 89             } 90         } 91         switch(select(max_fd+1,&_reads,&_writes,NULL,&_timeout))  //_reads,_writes输入,输出参数 92         { 93             case 0: 94                 printf("timeout\n"); 95                 break; 96             case -1: 97                 perror("select"); 98                 break; 99             default:100               {101                 for(i=0;i<fds_num;i++)102                 {103                     if(fds[i]==listen_sock&&FD_ISSET(fds[i],&_reads)) //listen_sock104                   {105                         new_sock=accept(listen_sock,(struct sockaddr*)&clien    t,&len);106107                     if(new_sock<0)108                     {109                         perror("new_sock");110                         continue;111                     }112                     printf("get connection...%ld\n",new_sock);113                     for(i=0;i<fds_num;i++)   //把new_sock加到_reads文件描述集114                     {115                         if(fds[i]==-1)116                         {117                             fds[i]=new_sock;118                             break;119                         }120                     }121                     if(i==fds_num)   //文件描述符个数已达到最大值122                     {123                         close(new_sock);124                     }125                   }126 127                 else if(fds[i]>0&&FD_ISSET(fds[i],&_reads))  //普通的sock,通信128                 {129                     char buf[1024];130                     ssize_t _s=read(fds[i],buf,sizeof(buf)-1);131                     if(_s>0)132                     {133                         buf[_s]='\0';134                         printf("%s\n",buf);135                     }136                     else if(_s==0)137                     {138                         printf("client closed\n");139                     }140                     else141                     {142                         perror("read");143                     }144                 }145                 else146                 {147  148                 }149                 }150               }151         }152     }153 return 0;154 }

tcp_client.c:

  1   2 #include<sys/socket.h>  3 #include<sys/types.h>  4 #include<unistd.h>  5 #include<errno.h>  6 #include<string.h>  7 #include<arpa/inet.h>  8 #include<netinet/in.h>  9 #include<string.h> 10 #include<stdlib.h> 11 #include<stdio.h> 12  13  14 void usage(char* proc) 15 {  16   printf("Usage:%s[ip][port]\n",proc); 17 } 18 int main(int argc,char* argv[]) 19 { 20     if(argc!=3) 21     { 22         usage(argv[0]); 23         exit(1); 24     } 25 char* ip=argv[1]; 26 int port=atoi(argv[2]); 27  28     //socket 29     int sock=socket(AF_INET,SOCK_STREAM,0); 30     if(sock<0) 31     { 32         perror("sock"); 33         exit(2); 34     } 35     struct sockaddr_in remote; 36     remote.sin_family=AF_INET; 37     remote.sin_port=htons(port); 38     remote.sin_addr.s_addr=inet_addr(ip); 39  40      int ret=connect(sock,(struct sockaddr*)&remote,sizeof(remote)); 41      if(ret<0) 42      { 43        perror("coneect"); 44      } 45  46      char buf[1024]; 47      while(1) 48      { 49          memset(buf,'\0',sizeof(buf)); 50          read(0,buf,sizeof(buf)-1); 51          ssize_t _s= write(sock,buf,sizeof(buf)-1); 52          if(_s<0) 53          { 54              perror("write"); 55          } 56      } 57      return 0; 58 }

结果:

server端:
[admin@www Internet1]$ ./tcp_server 127.0.0.1 8080get connection...4timeoutwe are young^C

client端:

[admin@www Internet1]$ ./tcp_client 127.0.0.1 8080we are young^C[admin@www Internet1]$

select缺点:

(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

(3)select支持的文件描述符数量太小了,默认是1024


本文出自 “liveyoung” 博客,转载请与作者联系!

0 0