I/O多路轉(zhuǎn)接之select(只負(fù)責(zé)等)
成都創(chuàng)新互聯(lián)公司專(zhuān)業(yè)為企業(yè)提供赤坎網(wǎng)站建設(shè)、赤坎做網(wǎng)站、赤坎網(wǎng)站設(shè)計(jì)、赤坎網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、赤坎企業(yè)網(wǎng)站模板建站服務(wù),十余年赤坎做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
系統(tǒng)提供select函數(shù)來(lái)實(shí)現(xiàn)多路復(fù)用輸入/輸出模型。
傳向select的參數(shù)告訴內(nèi)核:
1)我們所關(guān)心的文件描述符。
參數(shù)nfds是需要監(jiān)視的最大的文件描述符值+1;
2)對(duì)每個(gè)描述符,我們所關(guān)心的狀態(tài)。
rdset,wrset,exset分別對(duì)應(yīng)于需要檢測(cè)的可讀文件描述符的集合,可寫(xiě)文件描述符的集合及異常文件描述符的集合。
監(jiān)視的文件描述符分為三類(lèi)set,每一種對(duì)應(yīng)等待不同的事件。readfds中列出的文件描述符被監(jiān)視是否有數(shù)據(jù)可供讀?。ㄈ绻x取操作完成則不會(huì) 阻 塞)。writefds中列出的文件描述符則被監(jiān)視是否寫(xiě)入操作完成而不阻塞后,exceptfds中列出的文件描述符則被監(jiān)視是否發(fā)生異常,或者無(wú) 法控制的數(shù)據(jù)是否可用(這些狀態(tài)僅僅應(yīng)用于套接字)。這三類(lèi)set可以是NULL,這種情況下select()不監(jiān)視這一類(lèi)事件。
下面的宏提供了處理這三種描述詞組的方式:
FD_ZERO移除指定set中的所有文件描述符。每一次調(diào)用select()之前都應(yīng)該先調(diào)用它;
FD_CLR則從指定的set中移除一個(gè)文件描述符;
FD_SET添加一個(gè)文件描述符到指定的set中;
FD_ISSET測(cè)試一個(gè)文件描述符是否指定set的一部分。如果文件描述符在set中則返回一個(gè)非0整數(shù),不在則返0,F(xiàn)D_ISSET在調(diào)用select() 返回之后使用,測(cè)試指定的文件描述符是否準(zhǔn)備好相關(guān)動(dòng)作。
3)我們要等待多長(zhǎng)時(shí)間。(我們可以等待無(wú)限長(zhǎng)的時(shí)間,等待固定的一段時(shí)間,或者根本就不等待)
timeout參數(shù)是一個(gè)指向timeval結(jié)構(gòu)體的指針,timeval定義如下:
struct timeval結(jié)構(gòu)用于描述一段時(shí)間長(zhǎng)度,如果在這個(gè)時(shí)間內(nèi),需要監(jiān)視的描述符沒(méi)有事件發(fā)生則函數(shù)返回,返回值為0。
從 select函數(shù)返回后,內(nèi)核告訴我們以下信息:
1)已經(jīng)做好準(zhǔn)備的描述符的個(gè)數(shù)。
2)對(duì)于三種條件哪些描述符已經(jīng)做好準(zhǔn)備。(讀,寫(xiě),異常)
執(zhí)成功則返回件描述詞狀態(tài)已改變的個(gè)數(shù);如果返回0代表在描述詞狀態(tài)改變前已超過(guò)timeout時(shí)間,沒(méi)有返回;
當(dāng)有錯(cuò)誤發(fā)時(shí)則返回-1,錯(cuò)誤原因存于errno,此時(shí)參數(shù)readfds,writefds,exceptfds和timeout的值變成不可預(yù)測(cè)。錯(cuò)誤值可能為:EBADF 件描述詞為效的或該件已關(guān)閉 ,EINTR 此調(diào)被信號(hào)所中斷 EINVAL 參數(shù)n 為負(fù)值,ENOMEM 核內(nèi)存不。
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/socket.h> #include<sys/types.h> #include<netinet/in.h> #include<arpa/inet.h> int fds[64]; const fds_nums=sizeof(fds)/sizeof(fds[0]); static int startup(const char* _ip,int _port) { int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"); exit(2); } int opt=1; setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); struct sockaddr_in local; local.sin_family=AF_INET; local.sin_port=htons(_port); local.sin_addr.s_addr=inet_addr(_ip); if(bind(sock,(struct sockaddr*)&local, sizeof(local))<0) { perror("bind"); exit(3); } if(listen(sock,5)<0) { perror("listen"); exit(4); } return sock; } static void usage(const char* _proc) { printf("Usage:%s[IP] [PORT]\n",_proc); } int main(int argc,char* argv[]) { if(argc!=3) { usage(argv[0]); exit(1); } int i=0; for(i=0;i<fds_nums;++i) { fds[i]=-1; } int listen_sock=startup(argv[1],atoi(argv[2])); fd_set rset; FD_ZERO(&rset); FD_SET(listen_sock,&rset); fds[0]=listen_sock; int done=0; while(!done) { int max_fd=-1; for(i=0;i<fds_nums;++i) { if(fds[i]>0) { FD_SET(fds[i],&rset); max_fd=max_fd<fds[i]?fds[i]:max_fd; } } struct timeval timeout={0,0}; switch(select(max_fd+1,&rset,NULL,NULL,NULL/*&timeout*/)) { case 0: printf("timeout..\n"); break; case 1: perror("select"); break; default: for(i=0;i<fds_nums;++i) { if(i=0&& FD_ISSET(listen_sock,&rset)) { struct sockaddr_in peer; socklen_t len=sizeof(peer); int new_fd=accept(listen_sock,(struct sockaddr*)&peer,&len); if(new_fd>0) { printf("get a new client:socket->%s:%d\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port)); int j=0; for(j=0;j<fds_nums;j++) { if(fds[i]==-1) { fds[j]=new_fd; break; } } if(j==fds_nums) { close(new_fd); } } else { if(FD_ISSET(fds[i],&rset)) { char buf[1024]; memset(buf,'\0',sizeof(buf)); ssize_t _s=read(fds[i],buf,sizeof(buf)-1); if(_s>0) { printf("client# %s\n",buf); } else if(_s==0) { printf("client close...\n"); close(fds[i]); } else { perror("read"); } } } } } break; } } return 0; }
select優(yōu)點(diǎn):
(1)相較于之前多線(xiàn)程的方法,使用select不用創(chuàng)建線(xiàn)程,更方便
(2)select目前幾乎在所有的平臺(tái)上都支持,其良好跨平臺(tái)支持也是它的一個(gè)優(yōu)點(diǎn)
select缺點(diǎn):
(1)每次調(diào)用select,都需要把fd集合從用戶(hù)態(tài)拷貝到內(nèi)核態(tài),這個(gè)開(kāi)銷(xiāo)在fd很多時(shí)會(huì)很大
(2)同時(shí)每次調(diào)用select都需要在內(nèi)核遍歷傳遞進(jìn)來(lái)的所有fd,這個(gè)開(kāi)銷(xiāo)在fd很多時(shí)也很大
(3)能夠監(jiān)視的文件描述符的數(shù)量存在最大限制,在Linux上一般為1024,因?yàn)樗蕾?lài)于文件系統(tǒng)
(4)select()所維護(hù)的文件描述符的數(shù)據(jù)結(jié)構(gòu),隨著文件描述符數(shù)量的增大,其復(fù)制的開(kāi)銷(xiāo)也線(xiàn)性增長(zhǎng)。
(5)由于網(wǎng)絡(luò)響應(yīng)時(shí)間的延遲使得大量TCP連接處于非活躍狀態(tài),但調(diào)用select()會(huì)對(duì)所有socket進(jìn)行一次線(xiàn)性?huà)呙?,這也會(huì)有一些開(kāi)銷(xiāo)
網(wǎng)站標(biāo)題:I/O多路轉(zhuǎn)接之select
文章網(wǎng)址:http://muchs.cn/article36/gphgsg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊(cè)、軟件開(kāi)發(fā)、小程序開(kāi)發(fā)、電子商務(wù)、微信公眾號(hào)、ChatGPT
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)