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

    wKiom1dLxtDCYBNDAAAU9pUEahM313.png

        1.参数

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

            wKiom1dLx3vBuVOnAAAI4LLXvt8814.png

                该结构体封装了 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 <stdio.h>
#include <poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#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;
}