linux网络编程多路IO复用----poll

poll函数其实和select函数没有太大区别,与select不同的是函数接口,poll不为每种状态设置对应的文件描述符集,而是设置一个struct pollfd类型的结构体数组,对其数组元素设置文件描述符和对其所关心的状态。

优点:自带数组结构,并且将监听事件集合和返回事件集合分离,可以扩展监听上限,突破1024上限。
缺点:不能跨平台

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

fds为结构体指针,指向一个结构体数组,其结构体内容为:

           struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };

events指定监听文件描述符的事件,其值一般可设置为POLLIN,POLLOUT,POLLERR,分别表述读,写,发生错误。
revents也有POLLIN,POLLOUT,POLLERR三种值,不过其代表着文件描述符操作结果事件.

nfds这个参数我看网上都说它是第一个参数所指向的数组实际元素个数,但是在实际编程中却不能传实际个数,而应该传 最大下标+1,否则会出错。这块我还没弄清楚原因。

timeout参数:
值为0时,设置非阻塞
值为-1时,设置阻塞
值大于0时,设置等待时间,单位为毫秒级

返回值:成功返回设置监听文件描述符中发生对应事件的总个数,失败返回-1,并设置errno

对其用法和select函数一样,直接给出代码:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<arpa/inet.h>
  5 #include<sys/types.h>
  6 #include<sys/socket.h>
  7 #include<ctype.h>
  8 #include<poll.h>
  9 #define SERVER_PORT 7777
 10 void perr_exit(const char *p)
 11 {   
 12     perror(p);
 13     exit(1);
 14 }
 15 int main(void)
 16 {
 17     int lfd,cfd,ret,maxi;
 18     struct sockaddr_in serv_addr,client_addr;
 19     socklen_t client_addr_len = sizeof(client_addr);
 20     char buf[BUFSIZ];
 21     struct pollfd pfd[1024];
 22 
 23     lfd = socket(AF_INET,SOCK_STREAM,0);
 24     if(lfd == -1)
 25         perr_exit("socket error");
 26 
 27     //绑定ip和port
 28     serv_addr.sin_family = AF_INET;
 29     serv_addr.sin_port = htons(SERVER_PORT);
 30     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 31     ret = bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
 32     if(ret == -1)
 33         perr_exit("bind error");
 34 
 35     listen(lfd,128);
 36 
 37     for(int i = 0;i < 1024;i++)//初始化pfd数组里的文件描述符
 38         pfd[i].fd = -1;
 39     pfd[0].fd = lfd;//将监听的第一个文件描述符,存入pfd[0]中
 40     pfd[0].events = POLLIN;//lfd监听普通读事件
 41     pfd[0].revents = 0;
 42     maxi = 0;//存放文件描述符的数组最大下标
 43     while(1)
 44     {
 45         ret = poll(pfd,maxi+1,-1);//阻塞监听客户端请求
 46         if(ret == -1)
 47             perr_exit("poll error");
 48         if(pfd[0].revents & POLLIN)//判断其事件是否为读事件
 49         {
 50             cfd = accept(lfd,(struct sockaddr
 51                         *)&client_addr,&client_addr_len);//接受客户端连接请求accept不会阻塞
 52             if(cfd == -1)
 53                 perr_exit("accept error");
 54             for(int i = 1;i < 1024;i++)//将文件描述符存放在pfd数组中的空闲位置
 55             {
 56                 if(pfd[i].fd == -1)
 57                 {
 58                     pfd[i].fd = cfd;
 59                     pfd[i].events = POLLIN;//设置其为普通读事件
 60                     pfd[i].revents = 0;
 61                     if(maxi < i)//保存最大下标
 62                         maxi = i;
 63                     break;
 64                 }
 65             }
 66             if(--ret == 0)//若处理完事件返回poll继续阻塞监听,不用执行后面代码
 67                 continue;
 68         }
 69         for(int i = 1;i <= maxi ;i++)
 70         {
 71             if(pfd[i].fd == -1)//跳过文件描述符为空的数组单元
 72                 continue;
 73             if(pfd[i].revents & POLLIN)//若文件描述符有普通读事件发生则处理
 74             {
 75                 int n = read(pfd[i].fd,buf,sizeof(buf));
 76                 if(n < 0)
 77                     perr_exit("read error");
 78                 else if(n == 0)//n == 0说明对端套接字关闭则关闭文件描述符,并将其从fds数组中去除
 79                 {
 80                     close(pfd[i].fd);
 81                     pfd[i].fd = -1;
 82                     continue;
 83                 }
 84                 for(int i = 0 ;i < n;i++)
 85                     buf[i] = toupper(buf[i]);
 86                 write(pfd[i].fd,buf,n);
 87                 if(--ret == 0)//若处理完发生事件的文件描述符则不需要往后遍历,返回到poll处阻塞监听
 88                     break;
 89             }
 90         }
 91     }
 92     return 0;
 93 }
发布了31 篇原创文章 · 获赞 4 · 访问量 945

猜你喜欢

转载自blog.csdn.net/qq_39781096/article/details/104582831