上一篇介绍了关于I/O多路复用的select服务器,今天便介绍一下poll服务器。对两种模式再做一个比较。 I/O多路复用select服务器: http://blog.csdn.net/payshent/article/details/74909010
一、poll函数的介绍 我们先看一下poll函数的原型: 我们可以看出poll函数有三个参数,poll系统调用和select的系统调用类似,也是在指定的时间内轮询一定数量的文件描述符,来看其中是否有就绪。 1、fds:fds是pollfd结构类型的数组,指定我们所有感兴趣的文件描述符上的可读、可写、异常事件。 poll结构体的定义如下: 在这里,fd是指定的文件描述符,events成员告诉poll监听的fd上的哪些事件,是一系列事件的按位或。revents是由内核修改的,通知应用程序fd上实际发生了哪些事件,poll支持的类型事件如下所示: 上面虽然列出了所有的poll事件类型,但是我们最为常用的还是POLLIN(普通或优先数据可读)、POLLOUT(普通或优先数据可写)、POLLERR(错误)、POLLHUP(挂起) 2、nfds:指定被监听的事件集合fds的大小,其中nfds_t的定义如下:
typedef unsigned long int nfds_t; 3、timeout: 指定poll的超时值,单位是毫秒。timeout是-1,poll将永远阻塞,知道某个事件发生;timeout为0的时候,poll调用立即返回 4、返回值 poll的返回值情况和select的返回值情况是一致的,有问题请看select服务器。
二、select和poll的优缺点分析 select的缺点: 1、每次调用select,都需要把fd的集合从用户态拷贝到内核态,这个开销在fd很大的时会很大 2、同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大 3、select支持的文件描述符太小了,默认是1024。
三、poll服务器的代码展示 服务器端poll.c:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/socket.h> #include<sys/poll.h> #include<sys/types.h> #include<fcntl.h> #include<arpa/inet.h> #include<netinet/in.h> #define BUF_SIZE 1024 static usage(const char* proc) { printf("Usage: [local_ip],[local_port]%s\n",proc); } 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,10) < 0) { perror("listen"); exit(4); } return sock; } int main(int argc,char* argv[]) { if(argc != 3) { usage(argv[0]); return 1; } int listen_sock = startup(argv[1],atoi(argv[2])); struct pollfd evs[BUF_SIZE]; int timeout = 5000; int i = 0; for(;i < BUF_SIZE;i++) { evs[i].fd = -1; evs[i].events = 0; } evs[0].fd = listen_sock; evs[0].events = POLLIN; evs[0].revents = 0; int count = 0; while(1) { switch(poll(evs,count+1,timeout)) { case -1: { perror("poll"); break; } case 0: { printf("time out...\n"); break; } default: { for(i = 0;i < BUF_SIZE;i++) { if(evs[i].fd == -1) { continue; } //listen_sock的读事件就绪 else if(evs[i].fd == listen_sock && evs[i].revents & POLLIN) { struct sockaddr_in client; socklen_t len = sizeof(client); int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len); if(new_sock < 0) { perror("accept"); continue; } printf("get a new client! ip:%s port:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port)); int j = 1; for(; j < BUF_SIZE;j++) { if(evs[j].fd == -1) { evs[j].fd = new_sock; evs[j].events = POLLIN; break; } } if(j > count) { count = j; } if(j == BUF_SIZE) { close(new_sock); } } //普通事件的读事件就绪 else if(evs[i].revents & POLLIN) { char buf[1024]; ssize_t s = read(evs[i].fd,buf,sizeof(buf)-1); if(s > 0) { buf[s] = 0; printf("client echo#: %s\n",buf); write(evs[i].fd,buf,strlen(buf)); evs[i].revents = 0; } else if(s == 0) { printf("client is cose!!!\n"); close(evs[i].fd); evs[i].fd = -1; } else { perror("read"); close(evs[i].fd); evs[i].fd = -1; } } } break; } } } return 0; }客户端:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<fcntl.h> static void usage(const char* proc) { printf("%s [local_ip] [local_port]\n",proc); } int main(int argc,const char* argv[]) { if(argc != 3) { usage(argv[0]); return 1; } int sock = socket(AF_INET,SOCK_STREAM,0); if(sock < 0) { perror("socket"); return 2; } struct sockaddr_in server; server.sin_family = AF_INET; server.sin_port = htons(atoi(argv[2])); server.sin_addr.s_addr = inet_addr(argv[1]); if(connect(sock,(struct sockaddr*)&server,sizeof(server)) < 0) { perror("connect"); return 3; } //客户端不需要绑定 也不需要监听 //客户端是先write后read char buf[1024]; while(1) { printf("please Enter# "); fflush(stdout); ssize_t s = read(0,buf,sizeof(buf)-1); if(s > 0) { buf[s-1] = 0; write(sock,buf,strlen(buf)); ssize_t _s = read(sock,buf,sizeof(buf)-1); if(_s > 0 ) { buf[_s] = 0; printf("server echo# %s\n",buf); } } } close(sock); return 0; }四、代码运行结果