在游双的《高性能服务器编程》这本书里面写到三种定时器的存储结构:链表、时间轮、时间堆。这个TinyWebServer使用的是最好实现的链表定时器。
我们有一个定时器结点类util_timer,每个结点表示一个客户连接,它保存了双向链表的前后指针,客户数据client_data和回调函数。如果我们判断到这个结点长时间无反应,所以我们调用这个回调函数传入client_data,然后回调函数就会把这个客户断开,并且做一些善后工作。
我们还有链表类sort_timer_lst,这个链表是一个时间递增的结点链表,即从链表头到尾这个客户的最后一次反应时间是递增的。这个链表类当然有插入和删除结点函数。并且还有adjust_timer调整链表位置函数,作用是当一个客户有了反应,那么我们需要更新他的最后一次反应时间,那么为了维护链表的递增特性,我们需要这么一个调整位置的函数。此外,这个类还有一个检查函数(定时清扫),作用是我们上文提到统一了事件源,把信号回调函数逻辑搬到主函数执行,所以这个定时清扫检查逻辑就是在这个检查函数。主函数判断到信号来了,就执行这个函数进行检查链表中长时间无反应的结点进行清扫。
#ifndef LST_TIMER
#define LST_TIMER
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/uio.h>
#include <time.h>
#include "../log/log.h"
class util_timer;
struct client_data
sockaddr_in address;
int sockfd;
util_timer *timer;
class util_timer
public:
util_timer() : prev(NULL), next(NULL) {}
public:
time_t expire;
void (* cb_func)(client_data *);
client_data *user_data;
util_timer *prev;
util_timer *next;
class sort_timer_lst
public:
sort_timer_lst();
~sort_timer_lst();
void add_timer(util_timer *timer);
void adjust_timer(util_timer *timer);
void del_timer(util_timer *timer);
void tick();
private:
void add_timer(util_timer *timer, util_timer *lst_head);
util_timer *head;
util_timer *tail;
class Utils
public:
Utils() {}
~Utils() {}
void init(int timeslot);
int setnonblocking(int fd);
void addfd(int epollfd, int fd, bool one_shot, int TRIGMode);
static void sig_handler(int sig);
void addsig(int sig, void(handler)(int), bool restart = true);
void timer_handler();
void show_error(int connfd, const char *info);
public:
static int *u_pipefd;
sort_timer_lst m_timer_lst;
static int u_epollfd;
int m_TIMESLOT;
void cb_func(client_data *user_data);
#endif
#include "lst_timer.h"
#include "../http/http_conn.h"
sort_timer_lst::sort_timer_lst()
head = NULL;
tail = NULL;
sort_timer_lst::~sort_timer_lst()
util_timer *tmp = head;
while (tmp)
head = tmp->next;
delete tmp;
tmp = head;
void sort_timer_lst::add_timer(util_timer *timer)
if (!timer)
return;
if (!head)
head = tail = timer;
return;
if (timer->expire < head->expire)
timer->next = head;
head->prev = timer;
head = timer;
return;
add_timer(timer, head);
void sort_timer_lst::adjust_timer(util_timer *timer)
if (!timer)
return;
util_timer *tmp = timer->next;
if (!tmp || (timer->expire < tmp->expire))
return;
if (timer == head)
head = head->next;
head->prev = NULL;
timer->next = NULL;
add_timer(timer, head);
timer->prev->next = timer->next;
timer->next->prev = timer->prev;
add_timer(timer, timer->next);
void sort_timer_lst::del_timer(util_timer *timer)
if (!timer)
return;
if ((timer == head) && (timer == tail))
delete timer;
head = NULL;
tail = NULL;
return;
if (timer == head)
head = head->next;
head->prev = NULL;
delete timer;
return;
if (timer == tail)
tail = tail->prev;
tail->next = NULL;
delete timer;
return;
timer->prev->next = timer->next;
timer->next->prev = timer->prev;
delete timer;
void sort_timer_lst::tick()
if (!head)
return;
time_t cur = time(NULL);
util_timer *tmp = head;
while (tmp)
if (cur < tmp->expire)
break;
tmp->cb_func(tmp->user_data);
head = tmp->next;
if (head)
head->prev = NULL;
delete tmp;
tmp = head;
void sort_timer_lst::add_timer(util_timer *timer, util_timer *lst_head)
util_timer *prev = lst_head;
util_timer *tmp = prev->next;
while (tmp)
if (timer->expire < tmp->expire)
prev->next = timer;
timer->next = tmp;
tmp->prev = timer;
timer->prev = prev;
break;
prev = tmp;
tmp = tmp->next;
if (!tmp)
prev->next = timer;
timer->prev = prev;
timer->next = NULL;
tail = timer;
void Utils::init(int timeslot)
m_TIMESLOT = timeslot;
int Utils::setnonblocking(int fd)
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
return old_option;
void Utils::addfd(int epollfd, int fd, bool one_shot, int TRIGMode)
epoll_event event;
event.data.fd = fd;
if (1 == TRIGMode)
event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
event.events = EPOLLIN | EPOLLRDHUP;
if (one_shot)
event.events |= EPOLLONESHOT;
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
setnonblocking(fd);
void Utils::sig_handler(int sig)
int save_errno = errno;
int msg = sig;
send(u_pipefd[1], (char *)&msg, 1, 0);
errno = save_errno;
void Utils::addsig(int sig, void(handler)(int), bool restart)
struct sigaction sa;
memset(&sa, '\0', sizeof(sa));
sa.sa_handler = handler;
if (restart)
sa.sa_flags |= SA_RESTART;
sigfillset(&sa.sa_mask);
assert(sigaction(sig, &sa, NULL) != -1);
void Utils::timer_handler()
m_timer_lst.tick();
alarm(m_TIMESLOT);
void Utils::show_error(int connfd, const char *info)
send(connfd, info, strlen(info), 0);
close(connfd);
int *Utils::u_pipefd = 0;
int Utils::u_epollfd = 0;
class Utils;
void cb_func(client_data *user_data)
epoll_ctl(Utils::u_epollfd, EPOLL_CTL_DEL, user_data->sockfd, 0);
assert(user_data);
close(user_data->sockfd);
http_conn::m_user_count--;
TinyWebServer代码详细讲解(timer模块)基础知识设计思路定时器触发方式定时器的数据结构代码详解基础知识非活跃,是指客户端(这里是浏览器)与服务器端建立连接后,长时间不交换数据,一直占用服务器端的文件描述符,导致连接资源的浪费。定时事件,是指固定一段时间之后触发某段代码,由该段代码处理一个事件,如从内核事件表删除事件,并关闭文件描述符,释放连接资源。定时器,是指利用结构体或其他形式,将多种定时事件进行封装起来。具体的,这里只涉及一种定时事件,即定期检测非活跃连接,这里将该定时事件与连接
TinyWebServer代码详细讲解(http模块)http模块设计思路http_conn.cppread业务函数集process_read函数parse_request_line函数总结
这里的参照的代码是https://github.com/qinguoyi/TinyWebServer
对于原代码的不足之处,我会在之后的文章中给出改进代码 在笔者fork的这版中,原代码作者对于代码作出了更细化的分类
细节问题可以参考《APUE》《Linux高性能服务器编程》或者我之前的博客
毋庸置疑,http模块
Web服务器---TinyWebServer代码详细讲解(log模块)基础知识单例模式经典的线程安全懒汉模式局部静态变量之线程安全懒汉模式饿汉模式异步日志block_queue.h 代码解析log.cpp代码解析
log是日志模块,一个合格的服务器当然少不了日志来记录错误异常等等信息。我们想设计一个日志模块,他能顺利写日志但是又不要占用主线程时间去写,所以我们设计异步写日志的模块。
日志,由服务器自动创建,并记录运行状态,错误信息,访问数据的文件。
同步日志,日志写入函数与工作线程串行执行,由于涉
此博客记录对于TinyWebServer项目的学习,并根据自己的理解做出些许更改。
原项目地址:https://github.com/qinguoyi/TinyWebServer
网络程序通常需要处理定时事件,例如定期检测客户连接的活动状态,因为非活跃连接占用了连接资源,需要定期检测释放非活跃连接。通常将定时事件封装为定时器类,然后使用排序链表、时间轮等数据结构管理定时器。
linux提供了三种定时方法,
1.socket选项SO_RCVTIMEO和SO_SNDTIMEO
2.SIGALRM信
文章目录项目介绍一、服务器编程基本框架一、WebServer类详解1.初始化2.启动WebServer二、I/O处理的具体流程三、线程池四、HTTP请求报文解析与响应报文生成1.请求报文2.响应报文3.process()函数五、缓冲区六、定时器1.定时器的组成2.定时器的管理七、数据库连接池八、压力测试总结
Linux下C++轻量级Web服务器,使用线程池+非阻塞socket+epoll(ET模式)+事件处理(Reactor)的并发模型
使用状态机解析HTTP报文请求,支持解析GET、P
首先来了解一下GET与POST
GET是想获取server数据,将请求的数据添加到URL中,以?分割URL和传输数据,参数值之间以&相连,因此GET不安全。GET产生一个TCP数据包,浏览器将HTTP header和data一起发送给server,server响应状态码200(请求正常处理完毕)(返回数据)
POST是想修改server数据,
public class SecurityDemo {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
for (int i = 0...
self-timer.js是用于javascript的轻量级回调运行器库。
self-timer.js的名称源自“ self-timer”,它是相机上的设备。 您可以在要执行时的时间运行回调,就像self-timer一样。
例如,如果今天是工作日(星期一至星期五),请运行一些功能, self-timer.js可以编写simple 。
var st = new SelfTimer ( new Date ( ) ) ;
st . on ( )
. Weekdays ( function ( ) {
// callback
console . log ( "this method run on Monday to Friday!" ) ;
} ) ;
如果您使用服务器端模板(exp:nunjucks,EJS ..等等)。 您可以使用
eureka.server.eviction-interval-timer-in-ms 是一个配置属性,它用于指定 Eureka 服务器清理过期实例的时间间隔。具体来说,它表示清理任务执行的时间间隔,单位是毫秒(ms)。
当 Eureka 服务器运行时,它会维护一个注册表,记录着各个服务实例的信息。如果一个服务实例在一段时间内没有发送心跳给 Eureka 服务器,Eureka 会将其标记为过期实例,并在清理任务中将其从注册表中删除。
eureka.server.eviction-interval-timer-in-ms 属性可以用来配置清理任务的执行间隔。较短的间隔意味着 Eureka 服务器会更加及时地清理过期实例,但同时也会增加服务器的负载。较长的间隔则会减少服务器负载,但可能导致过期实例在注册表中停留更久。
根据具体的需求和系统负载情况,你可以根据需要调整这个属性的值。默认情况下,该属性的值为 60,000 毫秒(即 1 分钟)。