系統(tǒng)提供select函數(shù)來實(shí)現(xiàn)多路復(fù)用輸入/輸出模型。select系統(tǒng)調(diào)用是用來讓我們的程序監(jiān)視多個文件句柄的狀態(tài)變化的。
天心網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、響應(yīng)式網(wǎng)站設(shè)計等網(wǎng)站項目制作,到程序開發(fā),運(yùn)營維護(hù)。創(chuàng)新互聯(lián)從2013年開始到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。
select:該函數(shù)允許進(jìn)程指示內(nèi)核等待多個事件中的任何一個發(fā)生,并只在有一個或多個事件發(fā)生或經(jīng)歷一段指定的時間后才喚醒它。
參數(shù)含義:
timeout:它告知內(nèi)核等待所制定描述符中的任何一個就緒可在多長時間,其timeval結(jié)構(gòu)用于指定這段時間的秒數(shù)和微妙數(shù)。
struct timeval
{
long tv_sec;//seconds
long tv_usec;//microseconds
}
有三種情況:
永遠(yuǎn)等待下去:僅在有一個描述符準(zhǔn)備好I/O時才返回,為此,我們把該參數(shù)設(shè)為NULL.
等待固定時間:在有一個描述符準(zhǔn)備好I/O時返回,但是不超過由該參數(shù)所指向的timeval結(jié)構(gòu)中指定的秒數(shù)和微妙數(shù)。
根本不等待:檢查描述符后立即返回,這稱為輪詢,為此該參數(shù)必須指向一個timeval結(jié)構(gòu),而且其中的定時器值必須為0;
前兩種情形的等待通常會被進(jìn)程在等待期間捕獲的信號中斷,并從信號處理函數(shù)返回。
注:有的Linux會在select函數(shù)返回時修改timeval結(jié)構(gòu),從移植性考慮,我們應(yīng)假設(shè)timeval結(jié)構(gòu)在函數(shù)返回時未定義,因而每次調(diào)用select前都得對它進(jìn)行初始化。
中間的三個參數(shù)readset,writeset和exceptset制定我們要內(nèi)核測試讀,寫,異常條件的描述符。
異常原因:
1.某個套接字的帶外數(shù)據(jù)的到達(dá)
2.某個已置為分組模式的偽終端存在可從其主端讀取的控制狀態(tài)信息
select使用描述符集,通常是一個×××數(shù)組,其中每個整數(shù)中每一位對應(yīng)一個描述符
四個宏:
maxfd:待測試描述符個數(shù):待測試描述符加1,描述符1,2...到maxfd-1均被測試。
select函數(shù)修改三個描述符集,參數(shù)是值-結(jié)果參數(shù),調(diào)用時,是關(guān)心描述符值。返回時,指示那些描述符已就緒。
描述符中任何與未就緒描述符對應(yīng)的位返回時均被清為0,因此每次重調(diào)時,再次把所有描述符集中所關(guān)心的位置1.
返回值:大于0:所有描述符中已就緒的總位數(shù)。
等于0:超時
-1:出錯
就緒條件:
//監(jiān)視輸入輸出
#include#include #include #include #include #include int main() { int read_fd=0; int write_fd=1; fd_set reads; fd_set writes; FD_ZERO(&reads); FD_ZERO(&writes); struct timeval _timeout={5,0}; int ret=-1; int max_fd=write_fd; char buf[1024]; while(1) { FD_SET(read_fd,&reads); FD_SET(write_fd,&writes); _timeout.tv_sec=5; _timeout.tv_usec=0; ret=select(max_fd,&reads,&writes,NULL,&_timeout); switch(ret) { case -1://error perror("select"); break; case 0://time out printf("time is out...\n"); break; default://normal { ssize_t _s; if(FD_ISSET(read_fd,&reads)) { _s=read(0,buf,sizeof(buf)-1); buf[_s]='\0'; if(strncmp(buf,"quit",4)==0) { printf("quit\n"); return 1; } printf("echo: %s",buf); } if(FD_ISSET(write_fd,&writes)) { strcpy(buf,"hello world"); printf("show: %s\n",buf); } } break; } } return 0;
運(yùn)行截圖:
實(shí)現(xiàn)TCP通信,處理任意個客戶的單進(jìn)程,而不是為每一個客戶派生一個子進(jìn)程。
創(chuàng)建監(jiān)聽套接字并初始化:調(diào)用socket,bind,listen,唯一描述符是監(jiān)聽描述符初始化數(shù)據(jù)結(jié)構(gòu)。
阻塞于select:select等待某個事件發(fā)生或新客戶連接的建立或是數(shù)據(jù),F(xiàn)IN或RST的到達(dá)。
accept新連接
如果監(jiān)聽套接字變?yōu)榭勺x,那么已建立一個新的連接,我們調(diào)用accept并更新相應(yīng)數(shù)據(jù)結(jié)構(gòu)。使用fds數(shù)組中第一個未用項記錄這個已連接描述符。
檢查現(xiàn)有連接
//server #include#include #include #include #include #include #include #include #include #include #define _BACKLOG_ 5 #define _SIZE_ 64 void Usage(const char* proc) { printf("%s [ip][port]\n",proc); } int Start(const char* _ip,int _port) { if(_ip==NULL) return -1; int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"); return 1; } struct sockaddr_in local; local.sin_family=AF_INET; local.sin_addr.s_addr=inet_addr(_ip); local.sin_port=htons(_port); if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind"); return 2; } if(listen(sock,_BACKLOG_)<0) { perror("listen"); return 3; } return sock; } int main(int argc,char* argv[]) { if(argc!=3) { Usage(argv[0]); return 1; } char* ip=argv[1]; int port=atoi(argv[2]); int listen_sock=Start(ip,port); //allsets存儲舊的描述符集 fd_set reads,allsets; int fds[_SIZE_]={0}; int max_fd=listen_sock; FD_ZERO(&reads); FD_ZERO(&allsets); FD_SET(listen_sock,&allsets); fds[0]=listen_sock; int i=0; for(i=1;i<_SIZE_;++i)//init fds { fds[i]=-1; } struct timeval _timeout={5,0}; struct sockaddr_in remote; socklen_t size=sizeof(remote); char buf[1024]; ssize_t _s; while(1) { reads=allsets; _timeout.tv_sec=5; _timeout.tv_usec=0; for(i=0;i<_SIZE_;++i) { if(fds[i]>max_fd) max_fd=fds[i]; } switch(select(max_fd+1,&reads,NULL,NULL,&_timeout)) { case -1://error perror("select"); return 2; case 0://timeout printf("time is out...\n"); break; default://normal { //printf("have a one is comming");//test for(i=0;i<_SIZE_;++i) { if(fds[i]==listen_sock&&FD_ISSET(fds[i],&reads)) { int newsock=accept(listen_sock,(struct sockaddr*)&remote,&size); if(newsock<0) { perror("accept"); continue; } FD_SET(newsock,&allsets); int j; for(j=0;j<_SIZE_;++j) { if(fds[j]==-1) { fds[j]=newsock; break; } } if(j==_SIZE_)//full close(newsock); break; } else if(fds[i]>0&&FD_ISSET(fds[i],&reads)) { _s=read(fds[i],buf,sizeof(buf)-1); if(_s==0) { fds[i]=-1; FD_CLR(fds[i],&allsets); close(fds[i]); break; } buf[_s]='\0'; printf("client:%s",buf); write(fds[i],buf,_s); } else {} } } break; } } for(i=0;i<_SIZE_;++i) { if(fds[i]>0) { close(fds[i]); } } return 0; } //client #include #include #include #include #include #include #include #include void Usage(const char* proc) { printf("%s [ip][port]",proc); } int main(int argc,char* argv[]) { if(argc!=3) { Usage(argv[0]); return 1; } int client_sock=socket(AF_INET,SOCK_STREAM,0); if(client_sock<0) { perror("socket"); return 1; } struct sockaddr_in client; client.sin_family=AF_INET; client.sin_port=htons(atoi(argv[2])); client.sin_addr.s_addr=inet_addr(argv[1]); char buf[1024]; ssize_t _s; if(connect(client_sock,(struct sockaddr*)&client,sizeof(client))<0) { perror("connection"); return 2; } while(1) { printf("please enter:\n"); _s=read(0,buf,sizeof(buf)-1); if(_s>0) buf[_s]='\0'; if(strncmp(buf,"quit",4)==0) { printf("client is quit\n"); break; } write(client_sock,buf,_s); _s=read(client_sock,buf,sizeof(buf)-1); if(_s>0) { buf[_s]='\0'; printf("server->client: %s",buf); } } close(client_sock); return 0; }
//回顯的