1、使用定时器的原因:
HTTP/1.1 默认是长连接的
过多的长连接会占用服务器资源,所以服务器会用一些策略有选择地关闭长连接,比如通过定时器设置最大长连接时间管理非活动连接,超时释放资源
2、定时器的工作机制
- 通过alarm (unsigned int __seconds)系统调用,固定时间触发SIGALRM信号,比如alarm(5)调用5s之后触发SIGALRM信号;
- 通过
int sigaction (int __sig, const struct sigaction * __act,struct sigaction * __oact)
系统调用传入自定义的信号处理函数,第一个参数是具体的信号,第二个参数是一个结构体,结构体有一个成员 函数指针sa_handler指向信号处理函数,第三个参数表示原来的信号处理函数,调用时可以为NULL; - 自定义的信号处理函数只是将信号写到一个管道的写端,我们将管道读端的可读事件注册到epoll内核事件表中,在程序的主循环中执行真正的信号处理函数;
int socketpair(int domain,int type,int protocol,int sv[2])可以创建管道,比如socketpair(PF_UNIX,SOCK_STREAM,0,pipefd)创建了一个双向管道,可以实现双向的数据传输。 - 主循环中将定时器事件进行统一事件源处理,当接收到SIGALRM信号时,会遍历升序的定时器链表,清理超时的连接,判断的依据是链表节点的值是否在当前时间之前。
3、升序的双向链表定时器容器
将每一个客户端连接的作为一个节点,节点包括连接的最大超时时间,连接的通信套接字,回调函数,以及前后节点。
回调函数主要用于节点将自身从链表中删除,关闭套接字以及从epoll事件表中删除该套接字对应的事件。
添加节点的时间复杂度为O(n),删除定时器的时间复杂度为O(1),调整定时器节点的时间复杂度为O(n),当套接字中有读写事件时需要将该套接字对应的节点的超时时间重置并后移节点。