TinyWebSever源码逐行注释(二)_lst_timer.cpp

发布于:2024-09-18 ⋅ 阅读:(132) ⋅ 点赞:(0)

前言

项目源码地址
项目详细介绍

项目简介:
Linux下C++轻量级Web服务器,助力初学者快速实践网络编程,搭建属于自己的服务器.

  1. 使用 线程池 + 非阻塞socket + epoll(ET和LT均实现) + 事件处理(Reactor和模拟Proactor均实现) 的并发模型
  2. 使用状态机解析HTTP请求报文,支持解析GET和POST请求
  3. 访问服务器数据库实现web端用户注册、登录功能,可以请求服务器图片和视频文件
  4. 实现同步/异步日志系统,记录服务器运行状态
  5. 经Webbench压力测试可以实现上万的并发连接数据交换

lst_timer.cpp用于配置web服务器的定时器,包括但不限于定时器链表的处理、信号和管道、epoll的处理,原项目地址的注释较少不适合初学者,于是我将每行都加上了注释帮助大家更好的理解:

#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);
    }
    else
    {
        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;
}

//将内核事件表注册读事件,监听模式,选择开启EPOLLONESHOT
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;
    else
        event.events = EPOLLIN | EPOLLRDHUP;
    //开启epoll的oneshot
    if (one_shot)
        event.events |= EPOLLONESHOT;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    setnonblocking(fd);
}

// 信号处理函数,用于处理系统信号(如SIGTERM、SIGINT等)
void Utils::sig_handler(int sig)
{
    // 为保证函数的可重入性,保存当前的errno值(errno表示最近一次系统调用的错误代码)
    // 信号处理函数可能会改变errno的值,所以需要保存原来的errno,防止影响主程序的逻辑
    int save_errno = errno;

    // 将接收到的信号值保存到msg变量中
    int msg = sig;

    // 使用send函数将信号值通过管道发送给主程序
    // u_pipefd[1]是管道的写端,(char *)&msg是要发送的数据的地址,1是发送的数据长度(一个字节),0表示没有特殊的发送选项
    // 这种方式可以避免信号处理函数直接在其中执行复杂逻辑,而是通知主程序来处理信号,避免信号安全性问题
    send(u_pipefd[1], (char *)&msg, 1, 0);

    // 恢复原来的errno值,保证信号处理函数结束后,程序的errno值不变
    errno = save_errno;
}


// 设置信号处理函数
// sig: 要处理的信号编号,例如 SIGINT、SIGTERM 等
// handler: 信号处理函数的指针,指定信号触发时要执行的函数
// restart: 是否自动重启被信号中断的系统调用,若为 true 则设置 SA_RESTART 标志
void Utils::addsig(int sig, void(handler)(int), bool restart)
{
    // 定义一个 sigaction 结构体,用于描述信号处理的行为
    struct sigaction sa;

    // 使用 memset 将 sa 清零,以确保结构体中的所有字段被初始化为 0
    memset(&sa, '\0', sizeof(sa));

    // 设置信号处理函数,将 sa_handler 指向传入的处理函数 handler
    sa.sa_handler = handler;

    // 如果 restart 为 true,则设置 SA_RESTART 标志
    // SA_RESTART 标志表示当信号中断某些系统调用时,系统会自动重新启动被中断的系统调用
    if (restart)
        sa.sa_flags |= SA_RESTART;

    // 使用 sigfillset 函数将 sa_mask 设置为阻塞所有信号
    // 这意味着在处理该信号时,其他所有的信号都将被阻塞,防止信号处理函数被其他信号中断
    sigfillset(&sa.sa_mask);

    // 使用 sigaction 系统调用为指定的信号(sig)设置处理函数
    // 第一个参数是信号编号,第二个参数是指向 sigaction 结构体的指针,表示要设置的新信号处理方式
    // 第三个参数为 NULL,表示不关心旧的信号处理方式
    // assert 用于检查 sigaction 是否成功执行,若返回值为 -1 表示设置失败
    assert(sigaction(sig, &sa, NULL) != -1);
}


// 定时处理任务,重新设置定时器以不断触发 SIGALRM 信号
void Utils::timer_handler()
{
    // 调用定时器链表的 tick() 方法,处理到期的定时器任务
    // m_timer_lst 是一个定时器链表或管理器,tick() 方法通常用于遍历所有定时器,
    // 找到并执行已经到期的任务,如关闭超时的连接、释放资源等
    m_timer_lst.tick();

    // 重新设置定时器,以使 SIGALRM 信号在 m_TIMESLOT 秒后再次触发
    // alarm() 函数用于设定一个闹钟时间,m_TIMESLOT 是预设的时间间隔(以秒为单位)
    // 在 m_TIMESLOT 秒后,将触发 SIGALRM 信号,这个信号通常会被定时处理函数捕获并执行相应操作
    alarm(m_TIMESLOT);
}

// 定时处理任务,重新设置定时器以不断触发 SIGALRM 信号
void Utils::timer_handler()
{
    // 调用定时器链表的 tick() 方法,处理到期的定时器任务
    // 找到并执行已经到期的任务,如关闭超时的连接、释放资源等
    m_timer_lst.tick();

    // 重新设置定时器,以使 SIGALRM 信号在 m_TIMESLOT 秒后再次触发
    // alarm() 函数用于设定一个闹钟时间,m_TIMESLOT 是预设的时间间隔(以秒为单位)
    // 在 m_TIMESLOT 秒后,将触发 SIGALRM 信号,这个信号通常会被定时处理函数捕获并执行相应操作
    alarm(m_TIMESLOT);
}

int *Utils::u_pipefd = 0;
int Utils::u_epollfd = 0;

class Utils;

// 回调函数,用于处理客户端连接的超时或关闭
// user_data: 指向与客户端相关的数据信息结构体
void cb_func(client_data *user_data)
{
    // 从 epoll 实例中删除用户数据对应的文件描述符 sockfd
    // Utils::u_epollfd 是一个全局的 epoll 文件描述符,代表 epoll 实例
    // EPOLL_CTL_DEL 是操作类型,表示删除一个 epoll 监听的文件描述符
    // user_data->sockfd 是要删除的客户端 socket 文件描述符
    epoll_ctl(Utils::u_epollfd, EPOLL_CTL_DEL, user_data->sockfd, 0);

    // 断言检查 user_data 是否为非空指针,以确保其有效性
    // 如果 user_data 是 NULL,程序将在此处终止运行
    assert(user_data);

    // 关闭客户端的 socket 连接,释放资源
    close(user_data->sockfd);

    // 减少当前活跃连接的用户计数
    // m_user_count 是静态变量,记录当前活跃的客户端连接数
    http_conn::m_user_count--;
}

网站公告

今日签到

点亮在社区的每一天
去签到