多路I/O转接服务器又叫多任务I/O服务器。主要思想就是不再由应用程序自己来监视客户端的连接,而是由内核来代替应用程序监视。
主要有三种方法来实现多路I/O转接
一、select
优点:一个进程可以支持多个客户端和跨平台。
缺点:用户区和内核区需要来回拷贝、当多个客户端连接时,效率较低。
函数原型
int select(int nfds, fd_set *readfds, fd_set *writefds,
      fd_set *exceptfds, struct timeval *timeout);
nfds:监控的文件描述符集里最大文件描述符加1,因为此参数会告诉内核检测前多少个文件描述符的状态
readfds:监控有读数据到达文件描述符集合,传入传出参数
writefds:监控写数据到达文件描述符集合,传入传出参数
exceptfds:监控异常发生达文件描述符集合,如带外数据到达异常,传入传出参数
timeout:定时阻塞监控时间,3种情况
        1.NULL,永远等下去
        2.设置timeval,等待固定时间
        3.设置timeval里时间均为0,检查描述字后立即返回,轮询
  struct timeval {
    long tv_sec; /* seconds */
    long tv_usec; /* microseconds */
  };
void FD_CLR(int fd, fd_set *set);   //把文件描述符集合里fd清0
int FD_ISSET(int fd, fd_set *set);  //测试文件描述符集合里fd是否置1
void FD_SET(int fd, fd_set *set);   //把文件描述符集合里fd位置1
void FD_ZERO(fd_set *set);      //把文件描述符集合里所有位清0
二、poll
优点:把文件描述符和其他相关联的事件都定义在结构体中,使得编程接口简洁、没有最大连接数的限制
缺点:同select一样,文件描述符数组需要在用户区和内核态地址之间来回拷贝。
函数原型
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  struct pollfd {
    int fd; /* 文件描述符 */
    short events; /* 监控的事件 */
    short revents; /* 监控事件中满足条件返回的事件 */
  };
  POLLIN      普通或带外优先数据可读,即POLLRDNORM | POLLRDBAND
  POLLRDNORM    数据可读
  POLLRDBAND    优先级带数据可读
  POLLPRI     高优先级可读数据
  POLLOUT   普通或带外数据可写
  POLLWRNORM    数据可写
  POLLWRBAND    优先级带数据可写
  POLLERR     发生错误
  POLLHUP     发生挂起
  POLLNVAL    描述字不是一个打开的文件
  nfds      监控数组中有多少文件描述符需要被监控
  timeout     毫秒级等待
    -1:阻塞等,#define INFTIM -1        Linux中没有定义此宏
    0:立即返回,不阻塞进程
    >0:等待指定毫秒数,如当前系统时间精度不够毫秒,向上取值
三、epoll
优点:没有最大连接限制、效率提升、减少了复制的开销。
缺点:连接数较少的时候不适合使用。
函数原型
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
    epfd: 为epoll_creat的句柄
    op:   表示动作,用3个宏来表示:
      EPOLL_CTL_ADD (注册新的fd到epfd),
      EPOLL_CTL_MOD (修改已经注册的fd的监听事件),
      EPOLL_CTL_DEL (从epfd删除一个fd);
    event:  告诉内核需要监听的事件
    struct epoll_event {
      __uint32_t events; /* Epoll events */
      epoll_data_t data; /* User data variable */
    };
    typedef union epoll_data {
      void *ptr;
      int fd;
      uint32_t u32;
      uint64_t u64;
    } epoll_data_t;
EPOLLIN : 表示对应的文件描述符可以读(包括对端SOCKET正常关闭)
EPOLLOUT: 表示对应的文件描述符可以写
EPOLLPRI: 表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)
EPOLLERR: 表示对应的文件描述符发生错误
EPOLLHUP: 表示对应的文件描述符被挂断;
EPOLLET:  将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)而言的
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里











