用epoll 写一个server

1. 首先熟悉下epoll的三个接口

  • int epoll_create(int size);
    创建epoll相关数据结构,其最重要的是
    1. 红黑树, 用于存储需要监控的文件句柄以及事件
    2. 就绪链表,用于存储被触发的文件句柄以及事件
  • int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    用于设定,修改,或者删除 监控的文件句柄以及事件
  • int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
    阻塞等待timeout时间,如果文件句柄上相关事件被触发,则epoll_wait退出,并将触发的事件 写入出参 events参数,触发的事件个数作为返回值返回

2. 如何使用这三个接口写一个server

  • 首先使用epoll_create 创建epoll相关数据结构
  • 其次创建TCP socket 文件句柄acceptfd,绑定(ip:port),然后开启监听,并使用epoll_ctl 注册到epoll中,监听acceptfd句柄的EPOLL_IN事件(即可读事件)
  • 调用epoll_wait 开始进行阻塞等待
  • 如果有客户端连接过来,则触发acceptfd上的EPOLL_IN事件,epoll_wait返回后,可以得到触发事件的信息, 这些信息其实就是一个struct epoll_event对象, 我们可以判断这个epoll event对象fd是否和acceptfd一致,如果一致在认为有新连接进来,则获得新连接对应的clientfd, 并使用epoll_ctl注册到epoll, 监控clientfd上的epoll_in事件,这个时候这个客户端和服务器的连接就建立了
           typedef union epoll_data {
               void    *ptr;
               int      fd; //可以用fd, 也可以用ptr来保存事件对应的文件句柄
               uint32_t u32;
               uint64_t u64;
           } epoll_data_t;

           struct epoll_event {
               uint32_t     events;    /* Epoll events */
               epoll_data_t data;      /* User data variable */
           };
  • 用户在客户端输入命令,将会触发服务器端 clientfd上的epoll_in事件,epoll_wait返回触发的event, 读取event对应的clientfd内核缓冲区中的数据,解析协议,执行命令,得到返回结果,这个返回结果要返回给客户端,则再使用epoll_ctl注册clientfd的epoll_out事件到epoll,这个时候,我们会注意到clientfd上既有epoll_in,也有epoll_out,这样其实没有必要,客户端在这个时候等待返回结果,不会再输入命令,所以需要使用epoll_ctl把epoll_out删除掉
  • 如果clientfd内核缓冲区可写,epoll_wait这个时候会返回,并返回epoll_out事件,此时把返回的结果数据写入clientfd, 返回给客户端

3. 为什么epoll可以支持百万级别的连接?

  1. 在server的处理过程中,大家可以看到其中重要的操作是,使用epoll_ctl修改clientfd在epoll中注册的epoll_event, 这个操作首先在红黑树中找到fd对应的epoll_event, 然后进行修改,红黑树是典型的二叉平衡树,其时间复杂度是log2(n), 1百万的文件句柄,只需要16次左右的查找,速度是非常快的,支持百万级别毫无压力
  2. 另外,epoll通过注册fd上的回调函数,回调函数监控到有事件发生,则准备好相关的数据放到到就绪链表里面去,这个动作非常快,成本也非常小

4. 未完,后续继续补充

你可能感兴趣的:(用epoll 写一个server)