系統(tǒng)提供select函數(shù)來實現(xiàn)多路復用輸入/輸出模型。
成都創(chuàng)新互聯(lián)長期為上千多家客戶提供的網(wǎng)站建設服務,團隊從業(yè)經(jīng)驗10年,關注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務;打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為保亭黎族企業(yè)提供專業(yè)的做網(wǎng)站、網(wǎng)站設計,保亭黎族網(wǎng)站改版等技術服務。擁有十載豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。select函數(shù)讓我們的程序監(jiān)視多個文件描述符的狀態(tài)變化。程序會停在select這里等待,直到被監(jiān)視的文件描述符中有一個或多個發(fā)生了狀態(tài)變化
函數(shù)原型如下:
返回值:成功返回就緒描述符的個數(shù),超過timeout時間且沒有任何事件發(fā)生返回0,失敗返回-1
參數(shù)解釋:
nfds: 被監(jiān)視的文件描述符中值大描述符值加1(描述符是從0開始的,描述符0、1、2...nfds-1均將被測試)
下面三個參數(shù)readset、writeset和exceptset指定我們要讓內(nèi)核測試讀、寫和異常條件的描述符。如果對某一個的條件不感興趣,就可以把它設為空指針。
readfds: 讀描述符集(所有關心讀事件的描述符被用戶添加進來),這個參數(shù)是個輸入輸出型參數(shù)
writefds: 寫描述符集
exceptfds: 異常描述符集
struct fd_set可以理解為一個集合,這個集合中存放的是文件描述符,可通過以下四個宏進行設置:
void FD_CLR(int fd, fd_set *fdset); //把給定的文件描述符從指定集合刪除
int FD_ISSET(int fd, fd_set *fdset); //判斷指定描述符時候被加入了指定集合中(是否可讀/可寫)
void FD_SET(int fd, fd_set *fdset); //把一個給定的文件描述符加入集合之中
void FD_ZERO(fd_set *fdset); //清空集合
timeout: 設置一段時間
(1)timeout=NULL時,當前進程一直等待,直到有一個描述符準備好I/O(永遠等下去)
(2)timeout指向timeval結構體且時間設置不為0時,當前進程掛起,如果到了設定的時間(timeout)還沒有任何事件發(fā)生就返回0,進程繼續(xù)執(zhí)行后面的代碼(等待一段固定時間)
(3)timeout指向timeval結構體且時間設置為0時,不會等待,立即返回(根本不等待)
struct timeval{
long tv_sec; //seconds
long tv_usec; //microseconds
};
server_select.c
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #define BLOCK 6 int fds[64]; void usage(char * proc) { printf("%s [ip] [port]\n",proc); } int create_sock(char *port,const char * inaddr) { //1. int listenfd=socket(AF_INET,SOCK_STREAM,0); if(listenfd<-1){ perror("listenfd"); exit(1); } //2. struct sockaddr_in local; local.sin_family=AF_INET; int _port=atoi(port); local.sin_port=htons(_port); local.sin_addr.s_addr=inet_addr(inaddr); struct linger lig; int iLen; lig.l_onoff=1; lig.l_linger=0; iLen=sizeof(struct linger); setsockopt(listenfd,SOL_SOCKET,SO_LINGER,(char *)&lig,iLen); //3. if(bind(listenfd,(struct sockaddr*)&local,sizeof(local))<0){ perror("bind"); exit(2); } //4. if(listen(listenfd,BLOCK)<0){ perror("listen"); exit(3); } return listenfd; } int main(int argc,char* argv[]) { if(argc!=3){ usage(argv[0]); exit(1); } //create listenfd int listen_fd=create_sock(argv[2],argv[1]); struct sockaddr_in client; socklen_t len=sizeof(client); int max_fd=listen_fd; char buf[1024]; //initialize fds[] int fds_num=sizeof(fds)/sizeof(fds[0]); int i=0; for(i=0;i<fds_num;++i){ fds[i]=-1; } fds[0]=listen_fd; int done=0; while(!done){ //define and initialize parameter of select() fd_set readset; FD_ZERO(&readset); FD_SET(listen_fd,&readset); fd_set writeset; FD_ZERO(&writeset); struct timeval timeout; timeout.tv_sec=5; timeout.tv_usec=0; // for(i=1;i<fds_num;++i){ if(fds[i]>0){ FD_SET(fds[i],&readset); FD_SET(fds[i],&writeset); if(fds[i]>max_fd) max_fd=fds[i]; } else break; } switch(select(max_fd+1,&readset,&writeset,NULL,&timeout)){ //switch(select(max_fd+1,&readset,NULL,NULL,&timeout)){ case -1: perror("select"); break; case 0: printf("timeout...\n"); break; default: for(i=0;i<fds_num;++i){ //listen_fd happen if(fds[i]==listen_fd && FD_ISSET(fds[i],&readset)){ int connfd=accept(listen_fd,(struct sockaddr*)&client,&len); if(connfd<0){ perror("accept"); break; }else{ printf("get a connect...\n"); for(i=0;i<fds_num;++i){ if(fds[i]==-1){ fds[i]=connfd; break; } } } } //normal event happen else if(fds[i]>0 && FD_ISSET(fds[i],&readset)){ ssize_t _size=read(fds[i],buf,sizeof(buf)-1); if(_size<0){ perror("read"); }else if(_size==0){//client closed printf("client shutdown...\n"); close(fds[i]); fds[i]=-1; continue; }else{ buf[_size]='\0'; printf("client# %s",buf); if(FD_ISSET(fds[i],&writeset)){ if(write(fds[i],buf,sizeof(buf)-1)<0){ perror("write"); } } } } else{ } } break; } } return 0; }
client_select.c
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <errno.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <arpa/inet.h> #include <netinet/in.h> void usage(char *proc) { printf("%s [ip] [port]\n",proc); } int creat_socket() { int fd=socket(AF_INET,SOCK_STREAM,0); if(fd<0){ perror("socket"); exit(1); } return fd; } int main(int argc,char* argv[]) { if(argc!=3){ usage(argv[0]); exit(1); } int fd=creat_socket(); int _port=atoi(argv[2]); struct sockaddr_in addr; addr.sin_family=AF_INET; addr.sin_port=htons(_port); inet_aton(argv[1],&addr.sin_addr); socklen_t addrlen=sizeof(addr); if(connect(fd,(struct sockaddr*)&addr,addrlen)<0){ perror("connect"); exit(2); } char buf[1024]; while(1){ memset(buf,'\0',sizeof(buf)); printf("Please Enter:"); fgets(buf,sizeof(buf)-1,stdin); if(send(fd,buf,sizeof(buf)-1,0)<0){ perror("send"); continue; } //回顯 ssize_t _size=recv(fd,buf,sizeof(buf)-1,0); if(_size>0){ buf[_size]='\0'; printf("echo->%s\n",buf); } } return 0; }
運行結果:(可把服務器端和客戶端結果對比起來看)
服務器端
客戶端
select模型的缺點:
1.我的機器測試
printf("fd_set=%d\n",sizeof(fd_set)); 值為128,說明能監(jiān)視的描述符的大值為128*8=1024個
2.每次都要把所關心的描述符重新添加一次,即從用戶態(tài)拷貝到內(nèi)核態(tài)(因為select返回時沒有發(fā)生事件的描述符將被清空),如果描述符較多的時候,開銷是很大的
3.當select返回的時候,即使只有一個事件發(fā)生了,也要把整個集遍歷一次,同樣,如果描述符較多的時候,開銷也很大
另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。
網(wǎng)站名稱:IO多路復用之select-創(chuàng)新互聯(lián)
URL分享:http://m.rwnh.cn/article4/cecjoe.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供關鍵詞優(yōu)化、Google、商城網(wǎng)站、靜態(tài)網(wǎng)站、微信小程序、自適應網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容