您好,登錄后才能下訂單哦!
poll提供的功能與select類似,與select在本質(zhì)上沒有多大差別,管理多個(gè)描述符也是進(jìn)行輪詢,但poll比select的優(yōu)點(diǎn)是,不限制所能監(jiān)視的描述符的數(shù)目,但隨著所監(jiān)視描述符的數(shù)目的增加,性能也會下降
函數(shù)原型:
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
返回值:成功時(shí),poll()返回結(jié)構(gòu)體中revents域不為0的文件描述符個(gè)數(shù);如果在超時(shí)前沒有任何事件發(fā)生,返回0;失敗返回-1
參數(shù):
fds:結(jié)構(gòu)體指針,該結(jié)構(gòu)體結(jié)構(gòu)如下:
struct pollfd{
int fd; //所感興趣的文件描述符
short events; //用于指定等待的事件
short revents; //用于指定poll返回時(shí),在該文件描述符上實(shí)際發(fā)生了的事件
};
每一個(gè)pollfd結(jié)構(gòu)體制定了一個(gè)被監(jiān)視文件描述符,可傳遞多個(gè)該結(jié)構(gòu)體,指示poll監(jiān)視多個(gè)文件描述符
nfds:要監(jiān)視的描述符的個(gè)數(shù)
timeout:單位(微秒),timeout指定等待的毫秒數(shù),無論I/O是否準(zhǔn)備好,poll都會返回,指定為負(fù)數(shù)值表示無限超時(shí),使poll()一直掛起直到一個(gè)指定事件發(fā)生;timeout為0指示poll調(diào)用立即返回并列出準(zhǔn)備好I/O的文件描述符,但并不等待其它的事件,立即返回
events域中請求的任何事件都可能在revents域中返回。合法的事件如下:
POLLIN 有數(shù)據(jù)可讀
POLLPRI 有緊迫數(shù)據(jù)可讀
POLLOUT 寫數(shù)據(jù)不會導(dǎo)致阻塞
POLLRDNORM 有普通數(shù)據(jù)可讀。
POLLRDBAND 有優(yōu)先數(shù)據(jù)可讀。
POLLWRNORM 寫普通數(shù)據(jù)不會導(dǎo)致阻塞。
POLLWRBAND 寫優(yōu)先數(shù)據(jù)不會導(dǎo)致阻塞。
POLLMSGSIGPOLL 消息可用。
此外,revents域中還可能返回下列事件:
POLLER 指定的文件描述符發(fā)生錯(cuò)誤。
POLLHUP 指定的文件描述符掛起事件。
POLLNVAL 指定的文件描述符非法。
這些事件在events域中無意義,因?yàn)樗鼈冊诤线m的時(shí)候總是會從revents中返回。
POLLIN | POLLPRI等價(jià)于select()的讀事件,POLLOUT |POLLWRBAND等價(jià)于select()的寫事件。POLLIN等價(jià)于POLLRDNORM |POLLRDBAND,而POLLOUT則等價(jià)于POLLWRNORM。例如,要同時(shí)監(jiān)視一個(gè)文件描述符是否可讀和可寫,我們可以設(shè)置 events為POLLIN |POLLOUT。在poll返回時(shí),我們可以檢查revents中的標(biāo)志,對應(yīng)于文件描述符請求的events結(jié)構(gòu)體。如果POLLIN事件被設(shè)置,則文件描述符可以被讀取而不阻塞。如果POLLOUT被設(shè)置,則文件描述符可以寫入而不導(dǎo)致阻塞。這些標(biāo)志并不是互斥的:它們可能被同時(shí)設(shè)置,表示這個(gè)文件描述符的讀取和寫入操作都會正常返回而不阻塞。
示例代碼如下:
編寫一個(gè)echo server程序,功能是客戶端向服務(wù)器發(fā)送信息,服務(wù)器接收輸出并原樣發(fā)送回給客戶端,客戶端接收到輸出到終端
server_poll.c
#include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <poll.h> #define _BLOCKLOG_ 6 void usage(char *_proc) { printf("%s [ip] [port]\n",_proc); } int create(char *_ip,int _port) { int listen_fd=socket(AF_INET,SOCK_STREAM,0); if(listen_fd<0){ perror("socket"); exit(1); } struct sockaddr_in local; local.sin_family=AF_INET; local.sin_port=htons(_port); local.sin_addr.s_addr=inet_addr(_ip); struct linger lig; int iLen; lig.l_onoff=1; lig.l_linger=0; iLen=sizeof(struct linger); setsockopt(listen_fd,SOL_SOCKET,SO_LINGER,(char *)&lig,iLen); if(bind(listen_fd,(struct sockaddr*)&local,sizeof(local))<0){ perror("bind"); exit(2); } if(listen(listen_fd,_BLOCKLOG_)<0){ perror("listen"); exit(3); } return listen_fd; } int main(int args,char *argv[]) { if(args!=3){ usage(argv[0]); return 1; } char *ip=argv[1]; int port=atoi(argv[2]); //創(chuàng)建監(jiān)聽描述符并綁定 int listen_fd=create(ip,port); nfds_t nfds=64; struct pollfd fds[nfds]; //初始化描述符 int i=0; for(;i<nfds;++i){ fds[i].fd=-1; } //添加監(jiān)聽描述符 fds[0].fd=listen_fd; fds[0].events=POLLIN; struct sockaddr_in client; socklen_t len; char buf[1024]; int maxi=0; int done=0; while(!done){ int timeout=5000; switch(poll(fds,maxi+1,timeout)){ case -1: perror("poll"); break; case 0: printf("poll timeout...\n"); break; default: { int i=0; for(;i<nfds;++i){ if(fds[i].fd==listen_fd && (fds[i].revents&POLLIN)){//listen event happend int new_socket=accept(listen_fd,(struct sockaddr*)&client,&len); if(new_socket<0){ perror("accept"); continue; }else{ printf("get a connection...%s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port)); int i=0; //將新的連接和讀事件添加到數(shù)組中 for(;i<nfds;++i){ if(fds[i].fd<0){ fds[i].fd=new_socket; fds[i].events=POLLIN; ++maxi;//更新客戶連接的文件描述符的個(gè)數(shù) break; } } } }else if(fds[i].fd>0 && (fds[i].revents&POLLIN)){//normal events happend //接收客戶端發(fā)來的消息 ssize_t _size=read(fds[i].fd,buf,sizeof(buf)-1); if(_size<0){ perror("read"); }else if(_size==0){//client closed printf("client shutdown\n"); close(fds[i].fd); fds[i].fd=-1; continue; }else{ buf[_size]='\0'; printf("client# %s",buf); //向客戶端發(fā)送消息 if(write(fds[i].fd,buf,sizeof(buf)-1)<0){ perror("write"); } } } } break; } break; } } return 0; }
client_poll.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",buf); } } return 0; }
運(yùn)行結(jié)果:
服務(wù)器端運(yùn)行結(jié)果:
客戶端1運(yùn)行結(jié)果:
客戶端2運(yùn)行結(jié)果:
最后還有一點(diǎn),poll()會自動(dòng)把revents域設(shè)置為0,不需要我們手動(dòng)的去設(shè)置
測試程序如下:
以下程序片段只是在上面代碼的基礎(chǔ)上添加了幾行代碼,為看的更清楚,我用紅色注釋了這幾行代碼
運(yùn)行出來的結(jié)果如下:
現(xiàn)在對上面運(yùn)行結(jié)果進(jìn)行分析,為簡單起見,我們只關(guān)注四個(gè)結(jié)構(gòu)體,所以以四行為單位
可看到第一次(前四行)的revents都為隨機(jī)值
第二次(次四行)poll()函數(shù)把fd=3的描述符中revents設(shè)置為0
第四次poll()函數(shù)把fd=3的描述符中revents設(shè)置為1(因?yàn)榇藭r(shí)監(jiān)聽到了客戶端請求)所以接下來有:
get a connection...1.0.0.0:0 這條消息
第五次fd=3的revents還是為1,因?yàn)榇藭r(shí)還沒有執(zhí)行poll(),fd=4的revents是隨機(jī)值
第六次fd=3和fd=4的events都為1,它們都添加了讀事件,而fd=3的revents被poll設(shè)置為了0,fd=4的revents為1(因?yàn)榭蛻舳税l(fā)送了條消息,使讀事件發(fā)生,因此有下面一條消息:)
client# hello
第七次和第六次的一致,因?yàn)檫€未被poll()設(shè)置
第八次和第七次的一致,但此時(shí)fd=4的revents=1是因?yàn)榭蛻舳税聪铝薈trl+c,所以接下來會有:
client shutdown
第九次把fd=4從數(shù)組中去掉
《完》
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。