-
创建监听描述符
- 绑定地址和端口,创建非阻塞监听描述符
-
创建Epoll实例
-
创建线程池
- 初始化线程池参数
- 创建线程
- 子线程运行以下代码:
- 对mutex加锁
- 通过condition_variable判断是否任务队列是否有待处理任务,如果没有任务则阻塞并对mutex解锁
- 如有待处理任务,取出任务队列的第一个节点
- 对线程池解锁
- 执行任务
-
创建定时器管理器
- 更新当前时间
-
注册监听描述符的可读事件
-
注册新连接、可读事件、可写事件和关闭连接的事件处理函数
-
进入事件循环
- 通过定时器管理器获取下一个超时时间点与现在的时间差timeMs
- 设置超时时间timeMs,调用epoll_ -> wait(),阻塞等待监听事件发生
- 调用epoll_ -> wait(),分配事件处理函数,新连接处理函数和断开连接处理函数在I/O线程调用,连接描述符的读写事件处理函数在线程池调用
- 处理超时事件
-
监听描述符设置为ET模式,所以需要循环读监听描述符直到返回EAGAIN错误
-
调用accept4接受新连接,accept4函数可以直接设置新连接描述符为非阻塞模式
-
为新连接分配一个HttpRequest对象,设置定时器和注册可读事件到epoll
-
判断该连接是否活跃,若活跃则退出,不断开连接
-
若不活跃,删除定时器,从epoll的监听描述符中删除该文件描述符,回收HttpRequest对象,断开连接
-
删除文件描述符的超时定时器
-
从文件描述符中读数据,根据read的返回值处理
- 返回0,断开连接
- EAGAIN错误,对文件描述符进行epoll的MOD操作,注册可读事件(因为使用了EPOLLONESHOT),设置超时定时器并返回
- 其它错误,断开连接
- 返回值大于0,解析报文,若解析报文出错,则返回400响应报文并断开连接
- 若解析报文完成,则通过HttpResponse类构造响应报文,并注册文件描述符可写事件(使用了LT模式)
- 解析报文未完成,对文件描述符进行epoll的MOD操作,注册可读事件(因为使用了EPOLLONESHOT)
-
删除文件描述符的超时定时器
-
若文件描述符的输出缓冲区为空,设置超时定时器,直接返回
-
往文件描述符中写数据,根据write的返回值处理
- EAGAIN错误,对文件描述符进行epoll的MOD操作,注册可写事件(因为使用了EPOLLONESHOT),返回
- 其它错误,断开连接
- 缓冲区的数据写完,如果是HTTP长连接,重置HTTP解析状态,对文件描述符进行epoll的MOD操作,注册可读事件(因为使用了EPOLLONESHOT),设置超时定时器,返回;不是HTTP长连接则断开连接
- 缓冲区的数据没有写完,对文件描述符进行epoll的MOD操作,注册可读事件(因为使用了EPOLLONESHOT),设置超时定时器,返回
-
ThreadPool类的数据成员如下
using JobFunction = std::function<void()>; std::vector<std::thread> threads_; std::mutex lock_; std::condition_variable cond_; std::queue<JobFunction> jobs_; bool stop_;
-
外部对线程池的操作只有添加任务,添加任务流程如下
- 对互斥量加锁
- 把任务push进任务队列
- 解锁
- 通过condition_variable唤醒一个阻塞线程
-
定时器包含两个类Timer和TimerManager
-
Timer类是内部类,不对外部开放,外部直接使用TimerManger类
-
TimerManager类用小根堆管理Timer,根据超时时间排序
-
TimerManager的关键函数是addTimer、delTimer、handleExpireTimers和getNextExpireTime
-
addTimer用于添加定时器,流程如下
- 对定时器管理器加锁
- 更新当前时间
- 创建一个Timer对象并添加进小根堆
- 使HttpRequest对象的timer_指向该Timer对象
-
delTimer用于删除定时器(懒删除)
- 通过HttpRequest对象的timer_指针对该定时器设置删除标志,并设置timer_为空
-
handleExpireTimers用于调用超时定时器的超时回调函数
- 遍历小根堆获取定时器,直到小根堆的根节点不超时
- 若定时器被设置删除标志,则回收定时器资源,继续下一次遍历
- 定时器超时,调用超时回调函数
-
getNextExpireTime用于获取最近的超时时间,用于设置epoll_wait超时时间
- 遍历小根堆获取定时器
- 若小根堆为空,返回-1
- 若定时器被设置删除标志,则回收定时器资源,继续下一次遍历
- 若定时器已超时,返回0
- 若定时器未超时,返回定时器的超时时间 - 当前时间
- 对muduo缓冲区做了修改,基本原理相似,具体参见muduo缓冲区