poll函数类似于select,但是程序接口有所不同。poll函数任何类型的文件描述符。
在用poll函数编写程序之前,我们先来看看poll函数的原型:
int poll(struct pollfd* fds,nfds_t nfds,int timeout);
我们来看看它的参数:
第一参数是指向一个结构数组第一个元素的指针。每个数组元素都是一个pollfd结构,用于指定测试某个给定描述符fd的条件。与select不同,poll不是为每个条件(可读性,可写性和异常条件)构造一个描述符集,而是构造一个pollfd结构的数组,每个数组元素指定一个描述符编号以及我们对该描述符感兴趣的条件。这个结构体如下:
struct pollfd{
int fd;
short events;
short revents;
}
数组中的元素数由nfds指定。要测试的条件由events成员指定,这样我们就告诉了内核我们关心的是每个描述符的哪些事件。函数在相应的revents成员中返回该描述符的状态,revents成员由内核设置,用于说明每个描述符发生了哪些事件。
第三个参数和select一样,有3种不同的情形,指的是我们愿意等待多长时间。
当timeout为-1时,说明要永远等待,一直等。当所指定的描述符中的一个已准备好,或捕捉到一个信号时返回。
当timeout为0时,我们不等待。测试所有描述符并立即返回。可以说这是一种轮询方法,可以找到多个描述符的状态而不阻塞poll函数。
当timeout大于0时,我们等待timeout毫秒。当指定的描述符之一以准备好,或timeout超时时立即返回。如果timeout到期时还没有一个描述符准备好,则返回值是0;
如果我们不再关心某个特定描述符,那么我们可以把与它对应的pollfd结构的fd成员设置成一个负值。poll函数将忽略这样的pollfd结构的events成员,返回时将它的revents成员的值置为0。
接下来我们来看看具体的代码,用poll函数实现一个简单的客户/服务端模型:
server端:
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <poll.h> #include <unistd.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #define _BACKLOG_ 5 #define _CLIENT_ 64 static void usage(const char* arg) { printf("usage:%s [ip][port]",arg); } static int start(char *ip,int port) { assert(ip); int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<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); if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind"); exit(2); } if(listen(sock,_BACKLOG_)<0) { perror("listen"); exit(3); } return sock; } int main(int argc,char *argv[]) { if(argc!=3) { usage(argv[0]); exit(1); } int i,maxi;//client数组的最大下标 int port=atoi(argv[2]); char *ip=argv[1]; int listenfd=start(ip,port); int done=0; int new_sock=-1; struct sockaddr_in client; socklen_t len=sizeof(client); struct pollfd clientfds[_CLIENT_]; clientfds[0].fd=listenfd; clientfds[0].events=POLLIN; int _timeout=5000; for(i=1;i<_BACKLOG_;++i) { clientfds[i].fd=-1; } maxi=0; int ret; while(1) { ret=poll(clientfds,maxi+1,_timeout); if(ret<0) { printf("poll error\n"); exit(1); } else if(ret==0) { perror("time out"); //exit(2); } if(clientfds[0].revents&POLLIN) { new_sock=accept(listenfd,(struct sockaddr*)&client,&len); printf("get a connect...%d\n",new_sock); for(i=1;i<_CLIENT_;++i) { if(clientfds[i].fd<0) { clientfds[i].fd=new_sock; //save descriptor break; } } if(i==_CLIENT_) { close(new_sock); } clientfds[i].events=POLLIN; if(i>maxi) { maxi=i; } } for(i=1;i<=maxi;++i) { if(clientfds[i].fd<0) { continue; } if(clientfds[i].revents&POLLIN) { char buf[1024]; ssize_t _s=read(clientfds[i].fd,buf,sizeof(buf)-1); if(_s>0) { buf[_s]=‘\0‘; printf("client: %s",buf); } else if(_s==0) { printf("client quit...\n"); close(clientfds[i].fd); clientfds[i].fd=-1; } } } } return 0; }
client端:
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <poll.h> #include <unistd.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> static void usage(const char* arg) { printf("usage:%s [ip][port]",arg); } int main(int argc,char *argv[]) { if(argc!=3) { usage(argv[0]); exit(1); } int port=atoi(argv[2]); char *ip=argv[1]; int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"); exit(2); } struct sockaddr_in remote; remote.sin_family=AF_INET; remote.sin_port=htons(port); remote.sin_addr.s_addr=inet_addr(ip); int ret=connect(sock,(struct sockaddr*)&remote,sizeof(remote)); char buf[1024]; while(1) { printf("please enter: "); fflush(stdout); ssize_t _s=read(0,buf,sizeof(buf)-1); buf[_s]=‘\0‘; write(sock,buf,sizeof(buf)-1); } return 0; }
上面代码的运行结果如下:
从上面的额结果我们看到,客户端发送的数据被服务短板收到了,服务端在我们设定timeout超时后会给我打印出一条“time out :success”消息,至此整个程序编写完成。