一、poll()函数的接口解析

    

        1.参数

            struct pollfd* fds:(pollfd结构体指针)

            

                该结构体封装了 fd、events、revents、三个成员。

                fd:为监听的文件描述符(下例为socket类型的文件描述符)。

                events:为监听每个文件描述符关心的事件(可读、可写(在此站在系统角度,例如:用户可读,表示已写入数据,与我们看到的相反)),他是用一组宏来表示的,由用户(程序员)设置

                revents:函数返回时  文件描述符 满足的事件,与events对应,比如:

revents中 关心 读事件 ,该文件描述符可读,则 revents 被设置为 读。该结果

由系统设置,未发生的时间 revents域 置  0。

                events与revents的宏有:(注意:这些宏 都大于零)

                    POLLIN  有数据可读。

                       POLLRDNORM  有普通数据可读。
                       POLLRDBAND  有优先数据可读。
                    POLLPRI  有紧迫数据可读。
                    POLLOUT  写数据不会导致阻塞。
                    POLLWRNORM  写普通数据不会导致阻塞。
                    POLLWRBAND  写优先数据不会导致阻塞。
                    POLLMSGSIGPOLL  消息可用。

                  此外,revents域中还可能返回下列事件:

                    POLLER  指定的文件描述符发生错误。
                    POLLHUP  指定的文件描述符挂起事件。
                    POLLNVAL  指定的文件描述符非法。

           nfds nfds:表示 pollfd结构体数组中最大的有效的成员下标。(作用:提高效率,因为频繁申请空间会耗费资源,因此一般都会一次性开多个空间,通过nfds控制监听边界,还未使用的数组成员不监听,提高了效率)

            int timeout:为每次监听持续的最大时间(监听事件有相应会立即返回),单位为毫秒。因为监听是个持续的状态,不可能永远进行,因此,设计该参数,使用户可以根据实际情况 灵活控制监听时间,使效率最高。

        2.返回值

            失败:返回-1

            成功:返回 监听事件 有效的个数,对应的 revents 被设置为发生的大于0,未发生的等于0。)

            超时:返回0

   注意:

            返回后这些事件在events域中无意义,因为它们在合适的时候总是会从revents中返回。使用poll()和select()不一样,你不需要显式地请求异常情况报告。
            POLLIN | POLLPRI  等价于select()的读事件,POLLOUT |POLLWRBAND  等价于select()的写事件。POLLIN等价于 POLLRDNORM |POLLRDBAND,而POLLOUT则等价于 POLLWRNORM。
            例如,要同时监视一个文件描述符是否可读和可写,我们可以设置 events为POLLIN |POLLOUT。在poll返回时,我们可以检查revents中的标志,对应于文件描述符请求的events结构体。如果POLLIN事件被设置,则文件描述符可以被读取而不阻塞。如果POLLOUT被设置,则文件描述符可以写入而不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而不阻塞。
            timeout参数指定等待的毫秒数,无论I/O是否准备好,poll都会返回。timeout指定为负数值表示无限超时;timeout为0指示poll调用立即返回并列出准备好I/O的文件描述符,但并不等待其它的事件。这种情况下,poll()就像它的名字那样,一旦选举出来,立即返回。

        3.错误码

        poll()返回-1,并设置errno为下列值之一

            EBADF     一个或多个结构体中指定的文件描述符无效。
            EFAULT    fds指针指向的地址超出进程的地址空间。
            EINTR     请求的事件之前产生一个信号,调用可以重新发起。
            EINVAL    nfds参数超出PLIMIT_NOFILE值。
            ENOMEM    可用内存不足,无法完成请求。

二、使用poll函数的tcp 服务器实例:

        

    

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define _MAXFD_ 10 int startup(char* ip,int port)//注册绑定监听套接字{ int sock=socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in server; server.sin_family=AF_INET; server.sin_addr.s_addr=inet_addr(ip); server.sin_port=htons(port);// printf("port  %d  %d",port,(htons(port))); if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0){ perror("bind"); exit(-2); } if(listen(sock,5)<0){ perror("listen"); exit(-3); } return sock;}void usage(char* arg)//控制启动时绑定的 ip与接口{ printf("usage %s [ip] [port]\n",arg);}void pollup(int sock)//获取 对话套接字,使用poll监听事件,{ int maxfd=0,done=0,timeout=5000; nfds_t fd_num=0; struct pollfd event[_MAXFD_]; event[0].fd=sock; event[0].events=POLLIN; int new_sock=0; struct sockaddr_in client; in_addr_t len=sizeof(client); int i=0; for(i=1;i<_MAXFD_;++i){ event[i].fd=-1; } maxfd=0; char readbuf[1024]; char writebuf[_MAXFD_][1024]; while(!done){ int ret=0; ret=poll(event,_MAXFD_,timeout); switch( ret){ case -1:{//发生错误 perror("poll"); exit(-4); } break; case 0: {//超时 printf("timeout!...\n"); } break; default:{//正常 if(event[0].revents&POLLERR){//监听文件描述符错误 perror("POLLERR"); exit(-5); } if(event[0].revents&POLLIN){ new_sock=accept(sock,(struct sockaddr*)&client,&len); if((new_sock)<0){ if(errno=EINTR) continue; else{ printf("accept failed!\n"); exit(-6); } } for(i=1;i<_MAXFD_;++i){ if(event[i].fd==-1){ event[i].fd=new_sock; event[i].events=POLLIN; break; } } if(i==_MAXFD_){ printf("server busy...\n"); continue; } printf("connect a client [%d]\n",new_sock); } for(i=1;i<_MAXFD_;++i){//处理读事件 if(event[i].fd>0&&(event[i].revents&POLLIN)){       ssize_t size=read(event[i].fd,readbuf,sizeof(readbuf)-1);    if(size<0){    perror("read");    continue; } else if(size==0){ printf("client %d close!\n",event[i].fd); event[i].fd=-1; close(event[i].fd); continue; } else{ readbuf[size]=0; printf("client[%d]# %s\n",event[i].fd,readbuf); strcpy(writebuf[i],readbuf); // printf("wb  %s\n",readbuf); event[i].events=POLLOUT; } } else if(event[i].fd>0&&(event[i].revents&POLLOUT)){//将读到的数据回显给客户(测试监听写事件) ssize_t size=write(event[i].fd,writebuf[i],strlen(writebuf[i])); // printf("wb %d  %s\n",size,writebuf[i]); if(size<0){ printf("write error!\n"); break; } else if(size==0){ printf("client %d close!\n",event[i].fd); event[i].fd=-1; close(event[i].fd); continue; } else{ printf("already reply!\n"); event[i].events=POLLIN; // memset(writebuf[i],0,1024); } } } } break; } }}int main(int argc,char* argv[]){ if(argc!=3){ usage(argv[0]); exit(-1); } int port=atoi(argv[2]);// printf("port %s %d",argv[2],port); int listen_sock=startup( argv[1],port);        pollup(listen_sock); return 0;}