您好,登錄后才能下訂單哦!
select系統(tǒng)調(diào)用用于一次監(jiān)控多個(gè)句柄(文件描述符)的狀態(tài)變化的。程序會(huì)停在select處等待,直到被監(jiān)視的句柄有一個(gè)或多個(gè)發(fā)生了狀態(tài)改變。
select函數(shù)原型:
int select(int nfds, fd_set *readfds, fd_set *writefd,fd_set *exceptfds, struct timeval *timeout);
nfds:表示文件描述符集的個(gè)數(shù)
readfds:當(dāng)前有多少個(gè)讀事件(有數(shù)據(jù)到了這個(gè)狀態(tài)稱之為讀事件)
writefd:當(dāng)前有多少個(gè)寫(xiě)事件(關(guān)心輸出緩沖區(qū)是否已滿)
最后一個(gè)結(jié)構(gòu)體表示每個(gè)幾秒鐘醒來(lái)做其他事件,用來(lái)設(shè)置select等待時(shí)間
函數(shù)返回值:
執(zhí)行成功返回描述符集當(dāng)中狀態(tài)已改變的個(gè)數(shù)
返回0表示在描述符狀態(tài)改變前已經(jīng)timeout
返回-1表示有錯(cuò)誤發(fā)生,可能為某個(gè)套接字的帶有外數(shù)據(jù)到達(dá)
關(guān)于select函數(shù)的相關(guān)函數(shù)操作
FD_CLR(inr fd,fd_set* set);來(lái)清除描述詞組set中相關(guān)fd 的位
FD_ISSET(int fd,fd_set *set);判斷該文件描述符是否在當(dāng)前文件描述符集中
FD_SET(int fd,fd_set*set);將該描述符設(shè)置到當(dāng)前文件描述符集中
FD_ZERO(fd_set *set);可以用來(lái)初始化文件描述符集
select模型中的fd_set,例:若fd_set為1字節(jié),共有8位,則它的每一個(gè)位都可以對(duì)應(yīng)一個(gè)文件描述符,若fd=1則應(yīng)為0000 0001
將fd加入select監(jiān)控集時(shí),要用一個(gè)數(shù)組保存放到select監(jiān)控集中的文件描述符,為了在select 返回后,該數(shù)組中保存的數(shù)據(jù)可以和fd_set進(jìn)行FD_ISSET判斷,select在返回后會(huì)把以前加入的但并無(wú)任何事件發(fā)生的fd清空,每次開(kāi)始select前都要重新從數(shù)組取得fd
逐一加入。
使用select實(shí)現(xiàn)TCP通信
服務(wù)器端:
1 #include<stdio.h> 2 #include<string.h> 3 #include<errno.h> 4 #include<sys/select.h> 5 #include<stdlib.h> 6 #include<netinet/in.h> 7 #include<unistd.h> 8 #include<arpa/inet.h> 9 #include<sys/socket.h> 10 int fileds[64]; //用一個(gè)數(shù)組保存select監(jiān)控集中的文件描述符 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<fds_count) 60 { 61 fileds[i]=-1; //init fileds[] 62 ++i; 63 } 64 fileds[0]=listen_sock; 65 max_fd=fileds[0];//init maxfd 66 int done=0; 67 while(!done) 68 { 69 FD_ZERO(&_reads); //每次循環(huán)進(jìn)來(lái)都要進(jìn)行初始化 70 FD_ZERO(&_writes); 71 FD_SET(listen_sock,&_reads);//set listen_sock to reads fd 72 struct timeval _timeout; 73 _timeout.tv_sec=20; 74 _timeout.tv_usec=0; 75 for(i=1;i<fds_count;++i) 76 { 77 if(fileds[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<fds_count;++i) 97 { 98 if(fileds[i]==listen_sock \ 99 && FD_ISSET(fileds[i],&_reads)) 100 {//listen_sock is ready 101 newsock=accept(listen_sock,(struct sockaddr*)&client ,&(len)); 102 if(newsock<0) 103 { 104 perror("accept"); 105 return 1; 106 } 107 printf("get a new connect... [fd:%d],[ip:%s]\n",news ock,inet_ntoa(client.sin_addr)); 108 for(i=0;i<fds_count;++i) 109 { 110 if(fileds[i]==-1) 111 { 112 fileds[i]=newsock;//add newsock fd 113 break; 114 } 115 } 116 if(i==fds_count)//sum fd effective is full 117 { 118 close(newsock); 119 } 120 //break; 121 }else if(fileds[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 #include<stdio.h> 2 #include<string.h> 3 #include<errno.h> 4 #include<stdlib.h> 5 #include<netinet/in.h> 6 #include<unistd.h> 7 #include<arpa/inet.h> 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<stdio.h> 2 #include<string.h> 3 #include<errno.h> 4 #include<stdlib.h> 5 #include<netinet/in.h> 6 #include<unistd.h> 7 #include<arpa/inet.h> 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 }
結(jié)果回顯:
使用select實(shí)現(xiàn)多路復(fù)用同時(shí)也存在弊端:
每次調(diào)用select,都需要把fd集合從用戶態(tài)拷貝到內(nèi)核態(tài),需要在內(nèi)核中遍歷傳遞進(jìn)的所有文件描述符,當(dāng)fd很多時(shí)導(dǎo)致開(kāi)銷大
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。