UNIX网络编程学习(四)——I/O复用:select和poll

 I/O复用就是I/O进程不必阻塞于等待I/O,I/O就绪时内核将会通知该进程。I/O复用是通过select和poll函数支持的。

在谈I/O复用之前,先来看下unix下5种I/O模型。以recvfrom(recvfrom)为例。

阻塞式I/O:|-----------recvfrom等待数据报----------|--------接收-------|

                                                                      数据报到来

非阻塞式I/O:|-----recvfrom不断询问内核数据报是否到来-----|--------接收-------|

                                                                                        数据报到来

I/O复用:|----------进程阻塞于select调用--------|-------接收--------|

                                                                 某sockfd可读

信号驱动I/O:|--------------进程建立信号SIGIO处理程序,继续执行|-----接收-------|

                                                                                            信号中断

异步I/O:|---------------进程继续执行,等待内核通知完成----------|

                                                                                             内核通知完成

===============================================华丽的分割线=====================================

select函数

函数原型:select(int maxfdp, fd_set *rset, fd_set *wset, fd_set *eset, struct timeval *timeout);

select第一个参数意为描述符的个数,它的值为rset,wset,eset三个描述符集合中最大的fd+1。

rset,wset,eset分别为读fd集合,写fd集合,异常fd集合。

fd_set设置描述符很有技巧。int型变量有32位,它会将已就绪的描述符的那一位置为1。比如读集合rset中的描述符33,它就将该集合中的第二个元素值的第二位置为1(31+2)。(rset的第一个元素代表着描述符0~31)

select中用到的函数:
void FD_ZERO(fd_set *fdset)

void FD_SET(int fd, fd_set *fdset)

void FD_CLR(int fd, fd_set *fdset)

int FD_ISSET(int fd, fd_set *fdset)

皆可望文生义。

select最终是用在socket编程里,以下就直接奉上我理解到的伪代码和书中的源码:

(伪代码一词用的并不准确,但我只是想表达这种意思)

服务端伪代码:

int main(int argc, char **argv)
{
    int client[FD_SETSIZE];
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    bind(listenfd, (SA *)sockaddr, sizeof(sockaddr));
    listen(listenfd, LISTENQ);

    fd_set rset;
    maxfd = listenfd + 1;
    for(; ;) {
        select(maxfd, &rset, NULL, NULL, NULL);
        if(FD_ISSET(listenfd, &rset)) {
            connfd = accept(listenfd, cliaddr, clilen);
            for(i = 1; I < FD_SETSIZE; i++)
                if(client[I] < 0) {
                    client[I] = connfd;
                    break;
            }
            maxi = I;
            FD_SET(connfd, &rset);
        }
  
        for(I = 1;i < maxi; I++) {
            if(client[i] < 0)
                 continue;
            if(FD_ISISET(client[I], &rset))
                 do_read;
        }
    }
}

  服务端源码:

#include "unp.h"
  
int main(int argc, char **argv)
  {
      int       i, maxi, maxfd, listenfd, connfd, sockfd;
      int       nready, client[FD_SETSIZE];
      ssize_t   n;
      fd_set    rset,allset;
      char      buf[MAXLINE];
      socklen_t clilen;
      struct sockaddr_in cliaddr, servaddr;
  
      listenfd = Socket(AF_INET, SOCK_STREAM, 0);
  
      bzero(&servaddr, sizeof(servaddr));
      servaddr.sin_family = AF_INET;
      servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
      servaddr.sin_port = htons(SERV_PORT);
  
      Bind(listenfd, (SA *)&servaddr, sizeof(servaddr));
  
      Listen(listenfd, LISTENQ);
   
      maxfd = listenfd;
      maxi = -1;
      for(i = 0; i < FD_SETSIZE; i++)
          client[i] = -1;
      FD_ZERO(&allset);
      FD_SET(listenfd, &allset);
  
      for(; ;) {
          rset = allset;
          nready = Select(maxfd+1, &rset, NULL, NULL, NULL);
  
          if(FD_ISSET(listenfd, &rset)) {
              clilen = sizeof(cliaddr);
              connfd = Accept(listenfd, (SA *)&cliaddr, &clilen);
  
              for(i = 0; i < FD_SETSIZE; i++)
                  if(client[i] < 0) {
                      client[i] = connfd;
                      break;
                  }
              if(i == FD_SETSIZE)
                  err_quit("too many clients");
  
              FD_SET(connfd, &allset);
              if(connfd > maxfd)
                  maxfd = connfd;
              if(i > maxi)
                  maxi = i;
  
              if(--nready <= 0)
                  continue;
          }
  
          for(i = 0; i <= maxi; i++) {
              if((sockfd = client[i]) < 0)
                  continue;
              if(FD_ISSET(sockfd, &rset)) {
                  if((n = Read(sockfd, buf, MAXLINE)) == 0) {
                      Close(sockfd);
                      FD_CLR(sockfd, &allset);
                      client[i] = -1;
                  } else
                       Writen(sockfd, buf, n);
  
                  if(--nready <= 0)
                      break;
              }
          }
      }
  }



POll函数

    struct pollfd {

        int fd;

        short events;  //调用时的值

        short revents; //调用返回的值

    } 

   插入伪码和源码:

   服务端伪码:

    

int main(int argc, char **argv)
{
    int client[FD_SETSIZE];
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    bind(listenfd, (SA *)sockaddr, sizeof(sockaddr));
    listen(listenfd, LISTENQ);

    struct pollfd client[OPENMAX];
    
    client[0].fd = listenfd;
    client[0].events = POLLRDNORM;
    
    for(I = 1; I < OPENMAX; I++)
        client[I].fd = -1;
    maxi = 0;
    
    for(; ;) {
        if(client[0].revents & POLLRDNORM) { //new connn com
            connfd = accept();
        for(I = 1;i < OPENMAX; I++)
        {
            if(client[i] < 0) {
                client[i].fd = connfd;
                break; 
            } 
        }       
        maxi = i; 

        client[i].events = POLLRDNORM;
     
       }


       for(i = 1;i < maxi; i++)
       {
           if(client[i] < 0)
               continue;
           if(client[i].revents & (POLLRDNORM | POLLERR))
               do_read;
       }

    }
}

  服务器端源码:

#include "unp.h"
  #include <limits.h>
  
int main(int argc, char **argv)
  {
      int       i, maxi, listenfd, connfd, sockfd;
      int       nready;
      ssize_t   n;
      char      buf[MAXLINE];
      socklen_t clilen;
      struct pollfd client[OPEN_MAX];
      struct sockaddr_in cliaddr, servaddr;
  
      listenfd = Socket(AF_INET, SOCK_STREAM, 0);
  
      bzero(&servaddr, sizeof(servaddr));
      servaddr.sin_family = AF_INET;
      servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
      servaddr.sin_port = htons(SERV_PORT);
  
      Bind(listenfd, (SA *)&servaddr, sizeof(servaddr));

      Listen(listenfd, LISTENQ);
  
      client[0].fd = listenfd;
      client[0].events = POLLDNORM;
      for(i = 1; i < OPEN_MAX; i++)
          client[i].fd = -1;
      maxi = 0;
  
      for(; ;) {
          nready = Poll(client, maxi + 1, INFTIM);
  
          if(client[0].revents & POLLRDNORM) {
              clilen = sizeof(cliaddr);
              connfd = Accept(listenfd, (SA *)&cliaddr, &clilen);
  
              for(i = 0; i < OPEN_MAX; i++)
                  if(client[i].fd < 0) {
                      client[i].fd = connfd;
                      break;
                  }
  
              if(i == OPEN_MAX)
                   err_quit("too many clients");
  
              client[i].events = POLLRDNORM;
  
              if(i > maxi)
                  maxi = i;
  
              if(--nready <= 0)
                  continue;
          }
  
          for(i = 0; i <= maxi; i++) {
              if((sockfd = client[i].fd) < 0)
                  continue;
              if(client[i].revents & (POLLRDNORM | POLLERR)) {
                  if((n = Read(sockfd, buf, MAXLINE)) == 0) {
                      if(errno == ECONNRESET) {
                          Close(sockfd);
                          client[i].fd = -1;
                      } else
                          err_sys("read error");
                  } else if(n == 0) {
                      Close(sockfd);
                      client[i].fd = -1;
                  } else
                      Writen(sockfd, buf, n);
                  if(--nready <= 0)
                      break;
              }
          }
      }
  }

猜你喜欢

转载自blog.csdn.net/zzxding888/article/details/81106443