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_sock
104                   {
105                         new_sock=accept(listen_sock,(struct sockaddr*)&clien    t,&len);
106
107                     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                     else
141                     {
142                         perror("read");
143                     }
144                 }
145                 else
146                 {
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 8080
get connection...4
timeout
we are young

^C

client端:

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

select缺点:

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

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

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