Bootstrap

poll模型

poll模型

  • poll系统调用的原理与原型和select基本类似,也是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。

    • poll改进了select模型,其一,不需要在修改传入的参数数组,其二,对于可以监控的个数不再局限于1024,可以通过cat /proc/sys/fs/file-max来查看。

    • 缺点:

      • 不能指定确定的那个sokect

      • 线程不安全

  • poll模型的使用

    • struct pollfd 结构体

        struct pollfd
      {
          int fd;             //指定要监听的文件描述符
          short events;       //指定监听fd上的什么事件
          short revents;      //fd上事件就绪后,用于保存实际发生的事件
      };

      pollfd中的fd是用于记录我们关心的文件描述符;events表示我们关心的事件,如POLLIN、POLLOUT等。revents是实际发生的事件。


    • 通常我们定义这样的结构体数组来存放我们关心的文件描述符,如果有事件发生会改变数组元素的revents值。

    • 函数接口

      #include <poll.h>
      int poll(struct pollfd *fds, nfds_t nfds, int timeout);

      参数:

      第一个参数是指向一个结构数组的第一个元素的指针,每个元素都是一个pollfd结构,用于指定测试某个给定描述符的条件。

      第二个参数是要监听的文件描述符的个数,也就是数组fds的元素个数。

      第三个参数意义与select相同。

    • 函数的返回值

      成功返回的是有事件的描述符号的个数,出错返回-1,如果返回0,代表等待时间结束,但是没有事件发生。

  • 编码流程

      1. 定义pollfd结构体数组

      2. 初始化pollfd结构体数组

      3. 设置监听poll事件

      4. 等待poll事件

      5. 判断触发事件的描述符,并做对应处理。

    • 伪代码

      // 定义pollfd结构体数组 
      struct pollfd pollfds[OPEN_MAX];
      // 初始化pollfd结构体数组 
      int i;
      for(i=0;i<OPEN_MAX;i++){
      pollfds[i].fd = -1;
      } 
      int pollfds_cnt = 0; 
      // 设置监听事件 
      pollfds[0].fd = fd1; 
      pollfds[0].event = POLLRDNOM;
      pollfds_cnt++;
      pollfds[1].fd = fd1;
      pollfds[1].event = POLLRDNORM; 
      pollfds_cnt++; 
      // 等待poll事件 
      if(poll(pollfds,pollfds_cnt,INFTIM)>0)
      { int i;
      for(i=0;i<pollfds_cnt;i++)
      {
      // 判断触发事件的描述符 
      if(pollfds[i].fd == fd1 && pollfds[i].revent &POLLRDNORM){ // do something } 
      if(pollfds[i].fd == fd2 && pollfds[i].revent &POLLRDNORM)
      { // do something } 
      }
      }
      
    • tcp_poll_server.cpp

      #include <stdio.h>
      #include <unistd.h>
      #include <fcntl.h>
      #include <string.h>
      #include <arpa/inet.h>
      #include <sys/socket.h>
      #include <sys/poll.h>
      #include <sys/stat.h>
      #include <sys/sendfile.h>
      #include <linux/fs.h>
      #include <stdlib.h>
      
      #define max(a,b) ((a)>(b)?(a):(b))
      void show_info(int connfd)
      {
          struct sockaddr_in local_addr;
          bzero(&local_addr,sizeof(local_addr));
          socklen_t local_addr_len = sizeof(local_addr);
          getsockname(connfd,(struct sockaddr*)&local_addr,&local_addr_len);
          printf("server local %s:%d\n",inet_ntoa(local_addr.sin_addr),ntohs(local_addr.sin_port));
      
          struct sockaddr_in peer_addr;
          bzero(&peer_addr,sizeof(peer_addr));
          socklen_t peer_addr_len = sizeof(peer_addr);
          getpeername(connfd,(struct sockaddr*)&peer_addr,&peer_addr_len);
          printf("server peer %s:%d\n",inet_ntoa(peer_addr.sin_addr),ntohs(peer_addr.sin_port));
      }
      int main(int argc,char* argv[])
      {
          if(3 != argc) {
              printf("usage:%s <ip> <#port>\n",argv[0]);
              return 1;
          }
      
          int listenfd = socket(AF_INET,SOCK_STREAM,0);
          if(-1 == listenfd) {
              perror("listenfd open err");
              return 1;
          }
          printf("socket create OK\n");
      
          int flag = 1;
          setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));
      
          struct sockaddr_in local_addr;
          bzero(&local_addr,sizeof(local_addr));
          local_addr.sin_family = AF_INET;
          local_addr.sin_addr.s_addr = inet_addr(argv[1]);
          local_addr.sin_port = htons(atoi(argv[2]));
      
          if(-1 == bind(listenfd,(struct sockaddr*)&local_addr,sizeof(local_addr))) {
              perror("bind err");
              return 1;
          }
          printf("bind OK\n");
      
          if(-1 == listen(listenfd,10)) {
              perror("listen err");
              return 1;
          }
          printf("listen OK\n");
      
          struct pollfd poll_fd[INR_OPEN_MAX];
          poll_fd[0].fd = listenfd;
          poll_fd[0].events = POLLRDNORM;
          size_t poll_fd_cnt = 1;
      
          for(;;) {
              if(-1 != poll(poll_fd,poll_fd_cnt,-1)) {
                  if(poll_fd[0].revents == POLLRDNORM) {
                      printf("accept listenfd\n");
                      int connfd = accept(listenfd,NULL,NULL);
                      if(-1 == connfd) {
                          perror("accept err");
                      } else {
                          if(poll_fd_cnt+1 == INR_OPEN_MAX) {
                              fprintf(stderr,"connfd size over %d",INR_OPEN_MAX);
                              close(connfd);
                          } else {
                              poll_fd[poll_fd_cnt].fd = connfd;
                              poll_fd[poll_fd_cnt].events = POLLRDNORM;
                              poll_fd_cnt++;
                          }
                      }
                  }
                  int i;
                //下标从1开始,0存放的是监听套结字
                  for(i=1; i<poll_fd_cnt; i++) {
                      if(poll_fd[i].revents & POLLRDNORM) {
                          char buf[BUFSIZ];
                          bzero(buf,BUFSIZ);
                          ssize_t len;
                          printf("read connfd %d\n",poll_fd[i].fd);
                          if((len = read(poll_fd[i].fd,buf,BUFSIZ-1)) == -1) {
                              perror("read err");
                              //return 1;
                          }
                          if(0 == len) {
                              printf("close %d\n",poll_fd[i].fd);
                              printf("%d vs %d\n",poll_fd[i].revents,poll_fd[i].revents);
                              close(poll_fd[i].fd);
                              printf("%d vs %d\n",poll_fd[i].revents,poll_fd[i].revents);
                              memcpy(poll_fd+i,poll_fd+i+1,poll_fd_cnt-i-1);
                              poll_fd_cnt--;
                              i--;//数组发生变化,重新判断i的fd
                              continue;
                              //break;
                          }
                          printf("server recv:%s\n",buf);
      
                          int fd = open(buf,O_RDONLY);
                          if(-1 == fd) {
                              perror("open file err");
                          }
                          struct stat file_stat;
                          fstat(fd,&file_stat);
                          if(-1 == sendfile(poll_fd[i].fd,fd,NULL,file_stat.st_size)) {
                              perror("sendfile err");
                          }
                          printf("server send file %s ok\n",buf);
                          close(fd);
                      }
                  }
              }
          }
      
          close(listenfd);
      }


;