poll使用一个结构体指针代替select中读事件、写事件及异常事件。

结构体组成:

多路复用之用poll编写服务器_return

events(请求事件)关心该文件描述符的什么事件

所关心的事件有:

多路复用之用poll编写服务器_return_02


poll函数原型:int poll(struct pollfd* fds,nfds_t nfds,int timeout)

fds:为一个struct pollfd结构体类型的数组,存放要检测其状态的socket描述符

nfds:表示结构体数组总大小

timeout:poll函数阻塞等待的时间

return val:

大于0:结构体数组中准备好读、写或出错状态的socket描述符的总数量;

等于0:结构体数组中没有任何socket描述符就绪,poll超时,超时时间是timeout毫秒

小于: poll函数调用失败

服务器端代码:(针对单客户端)

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<sys/socket.h>
  4 #include<sys/types.h>
  5 #include<netinet/in.h>
  6 #include<arpa/inet.h>
  7 #include<errno.h>
  8 #include<poll.h>
  9 #include<stdlib.h>
 10 #include<assert.h>
 11 
 12 #define MAX_SIZE 64
 13 #define BACK_LOG 5
 14 void Usage(const char* proc)
 15 {
 16     printf("Usage:%s [ip] [port]\n",proc);
 17 }
 18 
 19 static int startup(char* ip,int port)
 20 {
 21     assert(ip);
 22     int listen_sock=socket(AF_INET,SOCK_STREAM,0);
 23     if(listen_sock<0)
 24     {
 25         perror("socket");
 26         exit(1);
 27     }
 28     struct sockaddr_in local;
 29     local.sin_family=AF_INET;
 30     local.sin_port=htons(port);
 31     local.sin_addr.s_addr=inet_addr(ip);
 32     socklen_t size=sizeof(local);
 33     if(bind(listen_sock,(struct sockaddr*)&local,size)<0)
 34     {
 35         perror("bind");
 36         exit(2);
 37     }
 38     if(listen(listen_sock,BACK_LOG)<0)
 39     {
 40         perror("listen");
 41         exit(3);
 42     }
 43     return listen_sock;
 44 }
 45 int main(int argc,char* argv[])
 46 {
 47     if(argc!=3)
 48     {
 49         Usage(argv[0]);
 50         exit(1);
 51     }
 52     char *_ip=argv[1];
 53     int _port=atoi(argv[2]);
 54     int listen_sock=startup(_ip,_port);
 55     struct sockaddr_in client;
 56     socklen_t size=sizeof(client);
 57     struct pollfd fd_set[MAX_SIZE];
 58     fd_set[0].fd=listen_sock;
 59     fd_set[0].events=POLLIN;
 60     fd_set[0].revents=0;
 61     char buf[1024];
 62     int max_fd=0;
 63     int timeout=5000;
 64     int done=0;
 65     int i=1;
 66     int j=0;
 67     for(;i<MAX_SIZE;++i)
 68     {
 69         fd_set[i].fd=-1;
 70     }
 71     while(!done)
 72     {
 73         timeout=5000;
 74         switch(poll(fd_set,max_fd+1,timeout))
 75         {
 76             case -1:
 77                 perror("poll");
 78                 break;
 79             case 0:
 80                 printf("poll timeout...\n");
 81                 break;
 82             default:
 83             {
 84                 for(i=0;i<MAX_SIZE;++i)
 85                 {
 86                     if(fd_set[i].fd==listen_sock && \
 87                         (fd_set[i].revents&POLLIN))
 88                     {//**判断是否为监听状态且判断它返回的哪一个就绪事件
 89                         int new_sock=accept(listen_sock,(struct sockaddr*)&client,&size);
 90                         if(new_sock<0)
 91                         {
 92                             perror("accept");
 93                             return -1;
 94                         }
 95                         printf("get a new conn... [fd:%d] [ip:%s]\n",\
 96                         new_sock,inet_ntoa(client.sin_addr));
 97                         for(j=0;i<MAX_SIZE;++j)
 98                         {
 99                             if(fd_set[j].fd==-1)
100                             {
101                                 fd_set[j].fd=new_sock;
102                                 fd_set[j].events=POLLIN;
103                                 break;
104                             }
105                         }
106                         if(j==MAX_SIZE)
107                         {
108                             printf("fd_set[] is full...\n ");
109                             close(new_sock);
110                             exit(1);
111                         }
112                         if(j>max_fd)
113                         {
114                             max_fd=j;
115                         }
116                
117                     }else if(fd_set[i].fd>0 &&\
118                      (fd_set[i].revents&POLLIN))
119                     { //表示读事件是否已经就绪
120                         ssize_t _size=read(fd_set[i].fd,buf,sizeof(buf)-1);
121                         if(_size>0)
122                         {
123                             buf[_size]='\0';
124                             printf("client:%s",buf);
125                             fd_set[i]._events=POLLOUT;
126                             fd_set[i].revents=0;
127                         }
128                         else if(_size==0)
129                         {
130                             printf("client is closed...\n");
131                             close(fd_set[i].fd);
132                             fd_set[i].fd=-1;
133                         }
134                     }else if(fd_set[i].fd>0 &&\
135                          fd_set[i].revents&POLLOUT)
136                           {  //写事件就绪
137                               int fd=fd_set[i].fd;
138                               write(fd,buf,sizeof(buf)-1);
139                               fd_set[i].events=POLLIN;
140                           }
141                 }
142             }
143         break;
144    
144         }
145     }
146 
147 
148     return 0;
149 }

客户端代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<sys/socket.h>
 4 #include<sys/types.h>
 5 #include<netinet/in.h>
 6 #include<arpa/inet.h> 
 7 #include<errno.h>
 8 #include<stdlib.h>
 9 #include<assert.h>
 10 
 11 void Usage(const char* proc)
 12 {
 13     printf("Usage:%s [remoteip] [remotrport]\n",proc);
 14 }
 15 
 16 int main(int argc,char* argv[])
 17 {
 18     if(argc!=3)
 19     {
 20         Usage(argv[0]);
 21         exit(1);
 22     }
 23     int sock=socket(AF_INET,SOCK_STREAM,0);
 24     if(sock<0)
 25     {
 26         perror("sock");
 27         exit(2);
 28     }
 29     char *_ip=argv[1];
 30     int _port=atoi(argv[2]);
 31     struct sockaddr_in remote;
 32     remote.sin_family=AF_INET;
 33     remote.sin_port=htons(_port);
 34     remote.sin_addr.s_addr=inet_addr(_ip);
 35     socklen_t len=sizeof(remote);
 36     int ret=connect(sock,(struct sockaddr*)&remote,len);
 37     if(ret<0)
 38     {
 39         perror("connect");
 40         return -1;
 41     }
 42     char buf[1024];
 43     while(1)
 44     {
 45         memset(buf,'\0',sizeof(buf));
 46         printf("please input:");
 47         fflush(stdout);
 48         if(read(0,buf,sizeof(buf)-1)>0)
 49         {
 50             write(sock,buf,strlen(buf));
 51         }
 52 
 53         ssize_t _size=read(sock,buf,sizeof(buf)-1);
 54         if(_size>0)
 55         {
 56             printf("server---client:%s",buf);
 57         }
 58     }
 59     close(sock);
 60     return 0;
 61 }

运行结果:

多路复用之用poll编写服务器_include_03

总结:(select与poll之间的区别)

select:使用三个文件描述符集(分别为读事件、写事件、异常事件)表示它们各自关心什么事件,将对应的文件描述符设置进去

它存在一定的弊端:

1.每次轮询前都要对所关心的文件描述符集进行设置及初始化(因为它们是输入输出型参数),每次都要遍历所有的文件描述符

2.它依赖文件描述符,但是文件描述符集的大小是有一定的限度的,当有大量的客户端同时请求服务器时不适宜使用

3.它的三个文件描述符集当中若有多个被修改,每次修改都要从用户态切换到内核态,内核态来负责修改文件描述符的状态变化

poll:使用一个结构体数组来代替select中的三个文件描述符集,它的大小不受限制

优化:

1.若有需要关心的文件描述符时,将它保存到结构体中,并设置该描述符所关心的事件,每次轮询完后若有无效的文件描述符时,将其描述符的状态置-1,当再次调poll函数时会忽略它

2.当有新的文件描述符时,重新遍历结构体,将出现第一个为-1的状态时,将它设置为要关心的描述符事件状态,每次有新的文件描述符加入时,更改要关心的文件描述符数量

3.调用poll函数后,结构体中的revents(返回已经就绪的事件)会存储就绪事件状态,重新调用poll之前,系统设置该状态默认其为0,重新监听关心事件

但poll也有相应的弊端:当同一时刻有大量客户端发来请求连接,但有只有很少的事件处于就绪状态,因此可能随着监视描述符数量的增长,效率也会降低