select系统调用用于一次监控多个句柄(文件描述符)的状态变化的。程序会停在select处等待,直到被监视的句柄有一个或多个发生了状态改变。
创新互联公司长期为1000+客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为黑龙江企业提供专业的网站设计制作、成都网站建设,黑龙江网站改版等技术服务。拥有十多年丰富建站经验和众多成功案例,为您定制开发。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 #include2 #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(i 0) 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;i 0 &&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 close 131 { 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 #include2 #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很多时导致开销大
另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。