一.概念
select系统调用是用来让程序监视多个文件句柄的状态变化。程序会停在select这里等待,直到被监视的文件句柄有一个或多个的状态发生了改变。关于文件句柄,其实就是一个整数,我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,3是标准错误。0、1、2是整数表示的,对应的FILE*结构的表示是stdin、stdout、stderr。
参数说明:
(1)nfds:需要监视的最大文件描述符+1;
(2)rdset、wrset、exset:分别对应需要检测的可读文件描述符的集合,可写文件描述符的集合,异常文件描述符的集合。
(3)struct timeval:这个结构用于描述一段时间长度。如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。
(4)处理三组描述词组的方式
void FD_CLR(int fd,fd_set* set); 用来清除描述词组set中相关fd的位
int FD_ISSET(int fd,fd_set* set); 用来测试描述词组set中相关fd的位是否为真
void FD_SET(int fd,fd_set* set); 用来设置描述词组set中相关fd的位
void FD_ZERO(fd_set* set); 用来清除描述词组set中所有的位
(5)timeout:为结构timeval,用来设置select的等待时间。其结构如下:
a、当timeout参数设置为NULL:表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件。
b、当timeout参数设置为0:只检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。
c、当timeout参数设置为特定的时间值:如果在指定的时间段内没有事件发生,则select将超时返回。
三、select函数的返回值分析
(1)执行成功:返回文件描述词状态已改变的个数。
(2)返回0:在描述词状态改变前已超过timeout时间,没有返回。
(3) 返回-1:表示错误。错误原因存于errno中,此时的参数readfds,writefds,exceptfds,timeout的值都讲变成不可预测的。错误值可能为:
EBADF:文件描述词为无效的或者文件已关闭。
EINTR:此调用该信号被中断。
EINVAL:参数n为负值。
ENOMEM:核心内存不足。
四、对select模型的理解
取fd_set的长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
(1)执行fd_set set;FD_ZERO(&set):则set用位表示是0000 0000。
(2)若fd=5,执行FD_SET(fd,&set):set变为0001 0000。
(3)若再加入fd=2,fd=1,则set变为0001 0011。
(4)执行select(6,&set,0,0,0)阻塞等待。
(5)若fd=2,fd=1都发生可读事件,则select返回,此时select变为0000 0011。注意:没有发生事件的fd=5被清空。
基于上面的说法,可以得出selec模型的特点。
(1)可监控的文件描述符个数取决于sizeof(fd_set)的值。
(2)强fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd。一是用于在select返回后,array作为源数据和fd_set进行FD_ISSET判断。而是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前 都要重新从array取得fd逐一加入。扫描array的同时取得最大值maxfd,用于select的第一个参数。
(3)select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有事件发生)。
五、代码描述
server.c
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 11 #define _BACKLOG_ 5 12 13 void Usage(const char *proc) 14 { 15 printf("%s[ip][port]\n",proc); 16 } 17 18 int start(char *_ip,int _port) 19 { 20 int sock=socket(AF_INET,SOCK_STREAM,0); 21 if(sock<0) 22 { 23 perror("sock"); 24 return 1; 25 } 26 struct sockaddr_in local; 27 local.sin_family=AF_INET; 28 local.sin_port=htons(_port); 29 local.sin_addr.s_addr=inet_addr(_ip); 30 if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) 31 { 32 perror("bind"); 33 return 2; 34 } 35 if(listen(sock,_BACKLOG_)<0) 36 { 37 perror("listen"); 38 return 3; 39 } 40 return sock; 41 } 42 43 int main(int argc,char *argv[]) 44 { 45 if(argc!=3) 46 { 47 Usage(argv[0]); 48 //return 1; 49 } 50 char *ip=argv[1]; 51 int port=atoi(argv[2]); 52 int listen_sock=start(ip,port); 53 54 struct sockaddr_in cli; 55 socklen_t len=sizeof(cli); 56 fd_set _reads; 57 fd_set _writes; 58 int fds[64]={0}; 59 int fd_max=0; 60 int fds_num=sizeof(fds)/sizeof(fds[0]); 61 int i=0; 62 for(i=0;i 0&&i<32) 83 { 84 FD_SET(fds[i],&_reads); 85 if(fd_max 0&&FD_ISSET(fds[i],&_reads)) 130 {//ready is success 131 char buf[1024]; 132 memset(buf,'\0',sizeof(buf)); 133 ssize_t _size=read(fds[i],buf,sizeof(buf)-1); 134 if(_size<0) 135 { 136 perror("read"); 137 close(fds[i]); 138 fds[i]=-1; 139 } 140 else if(_size==0) 141 { 142 printf("client close..."); 143 close(fds[i]); 144 } 145 else 146 { 147 FD_CLR(fds[i],&_reads); 148 buf[_size]='\0'; 149 printf("client say:%s",buf); 150 write(fds[i],buf,sizeof(buf)-1); 151 fflush(stdout); 152 } 153 } 154 else 155 {} 156 } 157 } 158 break; 159 160 } 161 } 162 return 0; 163 }
client.c
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 10 void Usage(const char* proc) 11 { 12 printf("%s [ip] [port]\n",proc); 13 } 14 15 int main(int argc,char* argv[]) 16 { 17 if(argc!=3) 18 { 19 Usage(argv[0]); 20 exit(1); 21 } 22 int port=atoi(argv[2]); 23 char* ip=argv[1]; 24 int client_sock=socket(AF_INET,SOCK_STREAM,0); 25 if(client_sock<0) 26 { 27 perror("socket"); 28 exit(2); 29 } 30 31 struct sockaddr_in remote; 32 remote.sin_family=AF_INET; 33 remote.sin_port=htons(port); 34 remote.sin_addr.s_addr=inet_addr(ip); 35 if(connect(client_sock,(struct sockaddr*)&remote,sizeof(remote))<0) 36 { 37 perror("connect"); 38 exit(3); 39 } 40 char buf[1024]; 41 ssize_t _s; 42 while(1) 43 { 44 memset(buf,'\0',sizeof(buf)-1); 45 printf("please input:"); 46 fflush(stdout); 47 fgets(buf,sizeof(buf)-1,stdin); 48 _s=write(client_sock,buf,sizeof(buf)-1); 49 if(_s<0) 50 { 51 perror("write"); 52 exit(4); 53 } 54 _s=read(client_sock,buf,sizeof(buf)-1); 55 if(_s>0) 56 { 57 buf[_s]='\0'; 58 printf("server->client:%s",buf); 59 fflush(stdout); 60 } 61 else if(_s==0) 62 { 63 close(client_sock); 64 } 65 else 66 { 67 perror("read"); 68 exit(5); 69 } 70 } 71 return 0; 72 }
执行结果: