当前位置: 首页 > news >正文

WebServer类

namespace tulun
{const int MAX_FD = 65536;           // 最大文件描述符const int MAX_EVENT_NUMBER = 10000; // 最大事件数const int TIMESLOT = 5;             // 最小超时单位class WebServer{public:int m_port;char *m_root;int m_log_write;int m_close_log;int m_actormodel;int m_pipefd[2];int m_epollfd;http_conn *users;// 数据库相关tulun::connection_pool *m_connpool;std::string m_user;         // 登陆数据库用户名std::string m_password;     // 登陆数据库密码std::string m_databasename; // 使用数据库名int m_sql_num;// 线程池相关tulun::threadpool<http_conn> *m_pool;int m_thread_num;// epoll_event 相关epoll_event events[MAX_EVENT_NUMBER];int m_listenfd;int m_OPT_LINGER;int m_TRIGMode; // ET ; LTint m_LISTENTrigmode;int m_CONNTrigmode;// 定时器相关tulun::client_data *users_timer;tulun::Utils utils;public:WebServer();~WebServer();void init(int port, const std::string &user, std::string &password,const std::string &databasename, int log_write, int opt_linger,int trigmode, int sql_num, int thread_num, int close_log, int actor_model);void thread_pool();void sql_pool();void log_write();void trig_mode();void eventListen();void eventLoop();void timer(int connfd, const struct sockaddr_in &client_address);void adjust_timer(tulun::util_timer *timer);           // 调整void deal_timer(tulun::util_timer *timer, int sockfd); // 分配,bool dealclientdata();bool dealwithsignal(bool &timeout, bool &stop_sever); // 处理信号void dealwithread(int sockfd);void dealwithwrite(int sockfd);};
        int m_port;char *m_root;int m_log_write;int m_close_log;int m_actormodel;int m_pipefd[2];int m_epollfd;http_conn *users;
变量名类型含义与作用
m_portint服务器监听的端口号(如 80、8080 等),用于客户端建立 TCP 连接时指定目标端口。
m_rootchar*指向 Web 服务器静态资源根目录的路径(如/var/www/html),用于查找并返回 HTML、CSS 等静态文件。
m_log_writeint日志写入模式的标志(如 0 表示同步写入,1 表示异步写入),控制日志输出的方式。
m_close_logint日志开关标志(0 表示开启日志,1 表示关闭日志),用于控制是否记录服务器运行日志。
m_actormodelint并发模型标志(如 0 表示 Proactor 模型,1 表示 Reactor 模型),决定服务器处理 I/O 事件的模式。
m_pipefdint[2]用于信号处理的管道文件描述符数组(m_pipefd[0]为读端,m_pipefd[1]为写端),将信号转换为 I/O 事件供主循环处理。
m_epollfdintepoll实例的文件描述符,是 I/O 多路复用的核心,用于监控所有客户端连接的读写事件及信号管道事件。
usershttp_conn*指向http_conn对象数组的指针,每个元素对应一个客户端连接,封装了该连接的 HTTP 请求解析、响应构建等逻辑(数组大小为MAX_FD,即最大文件描述符数量)。

这些变量是WebServer类的核心成员,贯穿服务器的初始化(如init函数)、事件监听(eventListen)、事件循环(eventLoop)等全过程,负责存储配置信息、管理 I/O 资源和客户端连接,是服务器正常运行的基础。

        // 线程池相关tulun::threadpool<http_conn> *m_pool;int m_thread_num;
  1. tulun::threadpool<http_conn> *m_pool

    • 这是一个指向tulun命名空间下的threadpool模板类对象的指针,其中模板参数为http_conn
    • threadpool是线程池的核心类,用于管理一组工作线程,实现线程的复用(避免频繁创建 / 销毁线程的开销)。
    • 模板参数http_conn指定了线程池处理的任务类型 —— 即每个工作线程将处理http_conn类的对象(封装了客户端的 HTTP 连接和请求处理逻辑)。
    • 该指针在WebServer初始化阶段(如thread_pool成员函数)会被实例化,指向实际创建的线程池对象。
  2. int m_thread_num

    • 用于存储线程池中的工作线程数量(线程池规模)。
    • 该值通常通过WebServerinit函数从外部传入(对应参数thread_num),决定了线程池可并发处理的任务数量上限。
    • 线程池会根据此数值预先创建对应数量的工作线程,等待处理客户端请求任务。

总结

这两个成员变量共同构成了WebServer中线程池管理的基础:m_pool指向实际的线程池实例,负责任务的调度和线程管理;m_thread_num则记录线程池的大小,是初始化线程池的关键参数。它们的存在使得 Web 服务器能够通过多线程并发处理多个客户端的 HTTP 请求,提高服务器的并发处理能力和响应效率。

        // epoll_event 相关epoll_event events[MAX_EVENT_NUMBER];int m_listenfd;int m_OPT_LINGER;int m_TRIGMode; // ET ; LTint m_LISTENTrigmode;int m_CONNTrigmode;

这段代码是WebServer类中与事件监听、I/O 复用及连接模式相关的成员变量定义,主要用于配置和管理服务器的网络事件处理机制,具体解释如下:

变量名类型含义与作用
eventsepoll_event[]存储epoll监控的事件数组,大小为MAX_EVENT_NUMBER(最大事件数)。当epoll_wait返回时,就绪的事件会被填充到该数组中,供服务器处理(如客户端连接、读写请求等)。
m_listenfdint服务器监听套接字的文件描述符。通过socket创建,绑定端口后用于监听客户端的连接请求(listen调用的对象)。
m_OPT_LINGERint套接字关闭模式标志,对应socket选项SO_LINGER。用于控制关闭连接时是否等待未发送数据发送完成(0 表示立即关闭,1 表示优雅关闭,等待数据发送)。
m_TRIGModeint事件触发模式的总开关,通常是一个组合值(如 0-3),用于配置监听套接字和连接套接字的触发模式(LT/ET)。
m_LISTENTrigmodeint监听套接字(m_listenfd)的事件触发模式(LT 水平触发或 ET 边缘触发),由m_TRIGMode解析得到。
m_CONNTrigmodeint客户端连接套接字的事件触发模式(LT 或 ET),同样由m_TRIGMode解析得到,用于处理已建立连接的读写事件。

核心逻辑关联

这些变量是服务器实现I/O 多路复用(epoll) 和事件触发机制的基础:

  1. epoll_event数组eventsepoll机制的核心数据结构,用于接收就绪事件,是服务器处理并发连接的关键。
  2. m_listenfd是服务器与客户端建立连接的入口,所有新连接请求都通过该套接字接收。
  3. 触发模式相关变量(m_TRIGModem_LISTENTrigmodem_CONNTrigmode)决定了epoll如何通知事件(LT 模式下事件会被重复通知,直到处理完成;ET 模式下仅通知一次,效率更高但需一次性处理完数据)。
  4. m_OPT_LINGER用于控制连接关闭的行为,避免数据丢失或连接异常。

这些变量通常在trig_mode()(设置触发模式)、eventListen()(创建监听套接字)等函数中被初始化和使用,共同决定了服务器处理网络事件的效率和行为模式。

    void WebServer::trig_mode(){if (0 == m_TRIGMode) // LT + LT{m_LISTENTrigmode = 0;m_CONNTrigmode = 0;}else if (1 == m_TRIGMode) // LT + ET{m_LISTENTrigmode = 0;m_CONNTrigmode = 1;}else if (2 == m_TRIGMode) // ET + LT{m_LISTENTrigmode = 1;m_CONNTrigmode = 0;}else if (3 == m_TRIGMode) // ET + ET{m_LISTENTrigmode = 1;m_CONNTrigmode = 1;}LOG_INFO << "m_LISTENTrigmode: " << m_LISTENTrigmode;LOG_INFO << "m_CONNTrigmode: " << m_CONNTrigmode;}

trig_mode 函数的核心作用是根据服务器配置的触发模式总开关(m_TRIGMode),分别设置监听套接字(listenfd)和客户端连接套接字(connfd)的事件触发模式(LT 或 ET),是 Web 服务器 I/O 事件处理机制的关键配置函数。

具体逻辑说明

函数通过判断 m_TRIGMode 的值(0-3),将其解析为监听套接字和连接套接字的具体触发模式(LT 或 ET):

m_TRIGMode 值含义监听套接字(m_LISTENTrigmode连接套接字(m_CONNTrigmode
0LT + LT 组合模式0(LT 水平触发)0(LT 水平触发)
1LT + ET 组合模式0(LT 水平触发)1(ET 边缘触发)
2ET + LT 组合模式1(ET 边缘触发)0(LT 水平触发)
3ET + ET 组合模式1(ET 边缘触发)1(ET 边缘触发)

关键概念补充

  • LT(水平触发):只要套接字缓冲区中还有未处理的数据(可读 / 可写),epoll 就会持续触发事件通知,适合简单场景,无需一次性处理完所有数据。
  • ET(边缘触发):仅在套接字缓冲区状态发生变化时(如新数据到达、缓冲区从不可写到可写)触发一次事件,效率更高,但要求一次性处理完所有数据,否则可能丢失事件。

函数作用总结

  1. 模式解析:将外部配置的 m_TRIGMode(总触发模式)拆解为两个具体套接字的触发模式,实现监听和连接套接字的灵活组合配置。
  2. 参数初始化:为 m_LISTENTrigmode 和 m_CONNTrigmode 赋值,这两个参数会在后续的 eventListen(创建监听套接字)、addfd(注册事件)等函数中被使用,决定 epoll 如何监控事件。
  3. 日志记录:输出最终的触发模式配置,便于调试和确认服务器运行状态。

该函数是服务器事件驱动模型的基础配置环节,直接影响 epoll 机制的事件通知行为,进而影响服务器的并发处理效率。

 void WebServer::log_write(){if (0 == m_close_log){if (1 == m_log_write){tulun::Log::get_instance()->init("./ServerLog", m_close_log, 2000, 8000, 800);}else{tulun::Log::get_instance()->init("./ServerLog", m_close_log, 2000, 8000, 0);}}}

这段代码是WebServer类中的log_write函数,主要作用是根据服务器配置初始化日志系统,控制日志的写入模式和存储参数。具体解释如下:

函数逻辑说明

  1. 日志开关判断通过m_close_log(日志开关标志)判断是否开启日志:

    • m_close_log == 0:表示开启日志,进入初始化流程。
    • m_close_log != 0:表示关闭日志,函数不执行任何操作。
  2. 日志写入模式选择当开启日志时,通过m_log_write(日志写入模式标志)选择不同的初始化参数:

    • m_log_write == 1:调用日志单例的init方法,传入参数800(推测为异步日志的队列大小或线程相关参数),启用异步日志模式
    • m_log_write != 1:调用init方法时传入参数0,启用同步日志模式
  3. 日志初始化参数解析无论同步还是异步模式,init方法的前 4 个参数固定:

    • "./ServerLog":日志文件存储路径。
    • m_close_log:日志开关状态(此处已确定为开启,即0)。
    • 2000:单个日志文件的最大大小(单位通常为字节)。
    • 8000:日志文件的最大数量(超过后可能自动滚动或覆盖旧文件)。

核心作用

  • 封装了日志系统的初始化逻辑,根据服务器配置(m_close_logm_log_write)决定是否开启日志以及使用同步 / 异步模式。
  • 异步日志模式(m_log_write == 1)可避免日志写入阻塞主程序,适合高并发场景;同步模式(默认)实现简单,适合调试或低负载场景。

该函数通常在服务器启动初期调用,为后续的日志记录(如LOG_INFOLOG_ERROR等)提供基础配置。

    void WebServer::sql_pool(){LOG_TRACE << " sql_pool()";m_connpool = tulun::connection_pool::GetInstance();m_connpool->init("127.0.0.1", m_user, m_password, m_databasename, 3306, m_sql_num, m_close_log);LOG_TRACE << "call initmysql_result ";users->initmysql_result(m_connpool);LOG_TRACE << "sql_pool end";}

这段代码是WebServer类中的sql_pool函数,主要作用是初始化数据库连接池并为 HTTP 连接对象准备数据库数据库操作的基础资源,具体解释如下:

函数逻辑说明

  1. 获取数据库连接池单例

    m_connpool = tulun::connection_pool::GetInstance();
    
    • 通过connection_pool类的静态方法GetInstance()获取数据库连接池的单例实例,确保整个服务器中只有一个数据库连接池,避免资源浪费和连接冲突。
  2. 初始化数据库连接池

    m_connpool->init("127.0.0.1", m_user, m_password, m_databasename, 3306, m_sql_num, m_close_log);
    
    • 调用连接池的init方法配置数据库连接参数,参数含义:
      • "127.0.0.1":数据库服务器地址(本地主机)。
      • m_user/m_password:数据库登录的用户名和密码(从WebServerinit方法传入)。
      • m_databasename:要连接的数据库名称。
      • 3306:数据库服务端口(MySQL 默认端口)。
      • m_sql_num:连接池中的最大连接数(控制并发数据库操作的连接上限)。
      • m_close_log:日志开关(控制是否输出数据库相关日志)。
    • 该操作会预先创建指定数量的数据库连接,存入连接池供后续复用,避免频繁创建 / 销毁连接的开销。
  3. 初始化 HTTP 连接的数据库操作资源

    users->initmysql_result(m_connpool);
    
    • usershttp_conn对象的数组(每个元素对应一个客户端连接),通过调用其initmysql_result方法,将数据库连接池实例传递给 HTTP 连接对象。
    • 推测该方法的作用是:让http_conn对象初始化数据库查询相关的资源(如预处理语句、结果集等),以便后续处理客户端请求时(如用户登录、数据查询)能直接从连接池获取连接并执行数据库操作。
  4. 日志跟踪函数中通过LOG_TRACE输出关键步骤的日志,用于调试和跟踪数据库连接池的初始化过程。

核心作用

  • 封装了数据库连接池的初始化流程,通过单例模式管理数据库连接,实现连接的复用,提升服务器处理数据库请求的效率。
  • 为所有客户端连接(http_conn对象)提供数据库操作的基础资源,确保 HTTP 请求处理过程中能便捷地访问数据库。

该函数通常在服务器启动阶段调用,是服务器与数据库交互的基础配置环节。

void WebServer::thread_pool(){m_pool = new tulun::threadpool<tulun::http_conn>(m_actormodel, m_connpool, m_thread_num);LOG_TRACE << "new threadpool m_thread_num: " << m_thread_num;}

这段代码是WebServer类中的thread_pool函数,主要作用是初始化线程池,为服务器处理并发任务(如 HTTP 请求)提供线程资源支持,具体解释如下:

函数逻辑说明

  1. 创建线程池实例

    m_pool = new tulun::threadpool<tulun::http_conn>(m_actormodel, m_connpool, m_thread_num);
    
    • 通过new创建tulun命名空间下的threadpool模板类实例,模板参数为tulun::http_conn,表示该线程池用于处理http_conn类型的任务(即 HTTP 连接请求)。
    • 构造函数参数解析:
      • m_actormodel:指定线程池的工作模式(reactorproactor,影响任务处理流程)。
      • m_connpool:数据库连接池实例,线程池中的工作线程可通过它获取数据库连接,执行数据库操作。
      • m_thread_num:线程池中的线程数量,决定了服务器并发处理任务的能力。
  2. 日志记录

    LOG_TRACE << "new threadpool m_thread_num: " << m_thread_num;
    
    • 输出日志记录线程池的创建信息,包括线程数量,用于调试和确认线程池初始化状态。

核心作用

  • 线程池初始化:封装了线程池的创建过程,通过指定线程数量和工作模式,为服务器提供可复用的线程资源,避免频繁创建 / 销毁线程的开销,提升并发处理效率。
  • 任务处理关联:将线程池与 HTTP 连接任务(http_conn)、数据库连接池绑定,确保工作线程能直接处理 HTTP 请求并访问数据库,形成 “接收请求 - 线程处理 - 数据库交互” 的完整链路。

该函数通常在服务器启动阶段调用,是服务器并发架构的核心组件之一,为高并发场景下的请求处理提供基础支撑。

void WebServer::eventListen(){m_listenfd = socket(PF_INET, SOCK_STREAM, 0);assert(m_listenfd >= 0);LOG_INFO << "WebServer m_OPT_LINGER: " << m_OPT_LINGER;// 优雅关闭连接if (0 == m_OPT_LINGER){struct linger tmp = {1, 1}; // 控制套接字关闭时的延迟和数据处理方式//// tmp.l_onoff = 0;  // 0 表示关闭该选项// tmp.l_linger = 1; // 设置延迟时间为 1 秒setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));// SOL_SOCKET 表示要设置的是通用的套接字选项,这些选项不依赖于具体的协议(如 TCP 或 UDP),而是与套接字本身的行为相关}else if (1 == m_OPT_LINGER){struct linger tmp = {1, 1};setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));}int ret = 0;struct sockaddr_in address = {};bzero(&address, sizeof(address));address.sin_family = AF_INET;address.sin_addr.s_addr = htonl(INADDR_ANY);address.sin_port = htons(m_port);int flag = 1;// SO_REUSEADDR 是一个通用套接字选项,其作用是允许在同一地址和端口上重复绑定套接字。// 开启这个选项后,即使之前的套接字连接还处于 TIME_WAIT 状态,新的套接字也能绑定到相同的地址和端口。setsockopt(m_listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));ret = bind(m_listenfd, (struct sockaddr *)&address, sizeof(address));assert(ret >= 0);ret = listen(m_listenfd, 5);assert(ret >= 0);utils.init(TIMESLOT);// epoll创建内核事件表epoll_event events[MAX_EVENT_NUMBER];m_epollfd = epoll_create(5);assert(m_epollfd != -1);LOG_INFO<<"call utils.addfd "<<"  m_epollfd: "<<m_epollfd<<" m_listendfd: "<<m_listenfd;utils.addfd(m_epollfd, m_listenfd, false, m_LISTENTrigmode);tulun::http_conn::m_epollfd = m_epollfd;// socketpair 函数会将创建的两个相互连接的套接字的文件描述符分别存储在 sockfd[0] 和 sockfd[1] 中。ret = socketpair(PF_UNIX, SOCK_STREAM, 0, m_pipefd);LOG_INFO<<"call socketpair: m_pipefd[0]: "<<m_pipefd[0]<<" m_pipefd[1]: "<<m_pipefd[1];assert(ret != -1);utils.setnonblocking(m_pipefd[1]);utils.addfd(m_epollfd, m_pipefd[0], false, 0);utils.addsig(SIGPIPE, SIG_IGN);utils.addsig(SIGALRM, utils.sig_handler, false);utils.addsig(SIGTERM, utils.sig_handler, false);alarm(TIMESLOT);// 工具类,信号和描述符基础操作tulun::Utils::u_pipefd = m_pipefd;tulun::Utils::u_epollfd = m_epollfd;LOG_INFO << "m_epollfd: " << tulun::http_conn::m_epollfd;LOG_INFO << "m_listenfd: " << m_listenfd;}
        if (0 == m_OPT_LINGER){struct linger tmp = {1, 1}; // 控制套接字关闭时的延迟和数据处理方式//// tmp.l_onoff = 0;  // 0 表示关闭该选项// tmp.l_linger = 1; // 设置延迟时间为 1 秒setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));// SOL_SOCKET 表示要设置的是通用的套接字选项,这些选项不依赖于具体的协议(如 TCP 或 UDP),而是与套接字本身的行为相关}else if (1 == m_OPT_LINGER){struct linger tmp = {1, 1};setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));}

这段代码的核心作用是配置监听套接字(m_listenfd)的关闭行为,通过设置 SO_LINGER 套接字选项实现连接的 “优雅关闭”。以下是详细解析:

1. 代码逻辑说明

  • 条件判断if (0 == m_OPT_LINGER) 表示当服务器配置的 m_OPT_LINGER 参数为 0 时,执行该代码块。m_OPT_LINGER 是一个控制套接字关闭行为的开关参数。
  • 初始化 linger 结构体
    struct linger tmp = {1, 1};
    
    struct linger 结构体用于定义套接字关闭时的延迟策略,包含两个成员:
    • l_onoff:是否启用延迟关闭(1 表示启用,0 表示禁用)。
    • l_linger:延迟关闭的时间(单位:秒),仅当 l_onoff=1 时有效。此处 tmp = {1, 1} 表示:启用延迟关闭,关闭时最多等待 1 秒以发送剩余数据。
  • 设置套接字选项
    setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));
    
    • m_listenfd:要配置的监听套接字文件描述符。
    • SOL_SOCKET:表示设置的是通用套接字选项(不依赖具体协议)。
    • SO_LINGER:选项名,用于控制套接字关闭时的行为。
    • &tmp 和 sizeof(tmp):传入 linger 结构体的地址和大小,指定具体的延迟策略。

2. SO_LINGER 选项的作用

当调用 close() 关闭套接字时,SO_LINGER 决定了系统如何处理未发送完成的数据:

  • 若 l_onoff=1 且 l_linger>0:系统会延迟 l_linger 秒关闭连接,尝试发送剩余数据。若超时仍未发送完成,剩余数据会被丢弃并强制关闭。
  • 若 l_onoff=0:关闭时立即返回,系统在后台继续发送剩余数据(默认行为)。

此处配置 {1, 1} 的目的是避免关闭连接时丢失未发送的数据,确保数据尽可能被完整传输,尤其适用于需要可靠关闭的场景(如 HTTP 响应未完全发送时)。

3. 注意点

代码中注释了 tmp.l_onoff = 0 和 tmp.l_linger = 1,但实际初始化时使用了 {1, 1},说明最终生效的是 “启用延迟关闭,等待 1 秒” 的策略。这种配置在服务器需要优雅终止连接时很有用,但可能会略微增加连接关闭的延迟。

4. 初始化 epoll 事件机制

// 初始化工具类(可能与定时器相关)
utils.init(TIMESLOT);// 创建 epoll 实例(内核事件表)
m_epollfd = epoll_create(5); // 参数 5 为历史遗留,无实际意义
assert(m_epollfd != -1);// 将监听套接字添加到 epoll 事件表,使用配置的触发模式(LT/ET)
utils.addfd(m_epollfd, m_listenfd, false, m_LISTENTrigmode);// 为所有 HTTP 连接对象设置全局 epoll 文件描述符
tulun::http_conn::m_epollfd = m_epollfd;
  • epoll 是 Linux 下高效的 I/O 多路复用机制,用于监控多个文件描述符的事件(如可读、可写)。
  • utils.addfd 封装了 epoll_ctl 操作,将监听套接字注册到 epoll 中,后续客户端连接请求会通过 epoll 通知。

5. 创建信号通信管道(socketpair

// 创建一对相互连接的 Unix 域套接字,用于信号处理
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, m_pipefd);
assert(ret != -1);// 将管道写端设为非阻塞(避免信号处理时阻塞)
utils.setnonblocking(m_pipefd[1]);// 将管道读端添加到 epoll 事件表,监控读事件
utils.addfd(m_epollfd, m_pipefd[0], false, 0);
  • socketpair 创建的管道用于信号处理机制:当信号(如定时器超时、进程终止)发生时,信号处理函数会向管道写端发送数据,epoll 监控到读端事件后在主循环中处理信号(避免信号处理函数中调用复杂逻辑)。

6. 配置信号处理

// 忽略 SIGPIPE 信号(避免写入已关闭的连接导致进程终止)
utils.addsig(SIGPIPE, SIG_IGN);// 注册 SIGALRM(定时器超时)和 SIGTERM(进程终止请求)的处理函数
utils.addsig(SIGALRM, utils.sig_handler, false);
utils.addsig(SIGTERM, utils.sig_handler, false);// 启动定时器(每隔 TIMESLOT 秒触发一次 SIGALRM)
alarm(TIMESLOT);
  • SIGPIPE 通常在向已关闭的套接字写数据时产生,忽略该信号可防止服务器意外退出。
  • SIGALRM 用于定时器机制(如检测超时连接),SIGTERM 用于优雅终止服务器。

7. 全局参数设置

// 工具类中保存管道和 epoll 的文件描述符,供信号处理函数使用
tulun::Utils::u_pipefd = m_pipefd;
tulun::Utils::u_epollfd = m_epollfd;
  • 将关键文件描述符存入工具类的静态成员,确保信号处理函数等全局场景可访问。

这段代码的作用是将web服务器的核心文件描述符赋值给Utils工具类的静态成员变量,以便工具类在全局范围内访问这些核心的资源

    • tulun::Utils::u_pipefd = m_pipefd;将服务器创建的管道文件描述符数组(m_pipefd)赋值给 Utils 类的静态成员 u_pipefd。该管道用于处理信号(如定时器超时信号 SIGALRM、进程终止信号 SIGTERM),工具类需要通过管道传递信号信息。

    • tulun::Utils::u_epollfd = m_epollfd;将服务器的 epoll 实例文件描述符(m_epollfd)赋值给 Utils 类的静态成员 u_epollfd。epoll 是 I/O 多路复用的核心,工具类在操作事件(如添加 / 移除文件描述符到 epoll 监听列表)时需要使用该句柄。

  • 核心目的:通过静态成员变量实现 Utils 工具类对服务器核心资源的全局访问,避免在工具类的各个方法中反复传递这些文件描述符,简化代码逻辑(例如信号处理函数中需要操作 epoll 或管道时,可直接通过 Utils 的静态成员获取)。

总结

eventListen 函数是服务器的 “初始化中枢”,完成了从套接字创建、网络配置、epoll 初始化到信号处理的全流程准备工作,为后续进入 eventLoop 循环处理客户端连接和事件奠定了基础。其核心目标是构建一个高效、稳定的网络事件监控框架,支撑服务器的并发处理能力。

  void WebServer::timer(int connfd, const struct sockaddr_in &client_address){LOG_INFO << " WebServer::timer connfd:  " << connfd;users[connfd].init(connfd, client_address, m_root, m_CONNTrigmode, m_close_log, m_user, m_password, m_databasename);// 初始化client_data数据// 创建定时器,设置回调函数和超时时间,绑定用户数据,将定时器添加到链表中users_timer[connfd].address = client_address;users_timer[connfd].sockfd = connfd;tulun::util_timer *timer = new tulun::util_timer;timer->user_data = &users_timer[connfd];timer->cb_func = cb_func;time_t cur = time(nullptr);timer->expire = cur + 3 * TIMESLOT;users_timer[connfd].timer = timer;LOG_INFO << "utils.m_timer_list.add_timer(timer): " << timer;utils.m_timer_list.add_timer(timer);LOG_INFO << "function end";}

这段 WebServer::timer 函数是 Web 服务器中用于初始化新客户端连接的定时器及关联资源的核心方法,主要作用是为每个新接入的客户端创建超时管理机制,确保空闲连接能被及时释放。以下是详细解析:

1. 函数功能概述

当服务器通过 accept 接收到新的客户端连接(得到 connfd)后,调用该函数完成两件核心工作:

  • 初始化该连接对应的 HTTP 处理对象(http_conn)。
  • 创建并配置定时器,用于监测该连接的活跃度,超时后自动关闭连接。

2. 代码逐行解析

LOG_INFO << " WebServer::timer connfd:  " << connfd;
  • 日志输出,记录当前处理的客户端连接文件描述符(connfd),用于调试和追踪。
users[connfd].init(connfd, client_address, m_root, m_CONNTrigmode, m_close_log, m_user, m_password, m_databasename);
  • 初始化 http_conn 对象:users 是一个 http_conn 数组(大小为 MAX_FD),下标为 connfd 的元素对应当前客户端连接。
  • init 方法的作用:绑定 connfd、客户端地址(client_address)、设置网站根目录(m_root)、连接触发模式(m_CONNTrigmode)、日志开关(m_close_log),以及数据库连接信息(用户名、密码、数据库名),为后续 HTTP 请求处理做准备。
// 初始化client_data数据
users_timer[connfd].address = client_address;
users_timer[connfd].sockfd = connfd;
  • users_timer 是一个 client_data 数组(与 users 一一对应,下标为 connfd),用于关联客户端连接与定时器的元数据。
  • 这里存储客户端的地址(client_address)和连接文件描述符(connfd),便于定时器超时回调时获取连接信息。
tulun::util_timer *timer = new tulun::util_timer;
  • 创建一个新的定时器对象(util_timer),用于管理当前连接的超时逻辑。
timer->user_data = &users_timer[connfd];
  • 将定时器与 client_data 关联:user_data 是定时器的成员变量,用于存储与该定时器绑定的客户端元数据(即 users_timer[connfd] 的地址),便于超时回调时操作连接。
timer->cb_func = cb_func;
  • 设置超时回调函数:cb_func 是一个全局或类内静态函数,定义了超时发生时的处理逻辑(通常是关闭连接、释放资源等)。当定时器到期时,会自动调用该函数。
time_t cur = time(nullptr);
timer->expire = cur + 3 * TIMESLOT;
  • 设置定时器的过期时间:TIMESLOT 是定时器的基础时间间隔(如 5 秒),这里将过期时间设为当前时间 + 3 倍 TIMESLOT(即 15 秒)。若 15 秒内客户端无任何活动(如未发送请求或响应),则触发超时。
users_timer[connfd].timer = timer;
  • 在 client_data 中记录定时器指针,形成 “连接元数据 - 定时器” 的双向关联,便于后续调整定时器(如客户端活跃时延长超时时间)。
utils.m_timer_list.add_timer(timer);
  • 将定时器添加到定时器链表(m_timer_list)中:该链表由工具类 Utils 管理,用于统一维护所有客户端的定时器,定期检查并处理超时的定时器。
LOG_INFO << "function end";
  • 日志输出,标记函数执行结束。

3. 核心作用总结

  • 连接与定时器绑定:为每个新客户端连接创建专属定时器,通过 client_data 实现连接与定时器的双向关联。
  • 超时管理:设置初始超时时间(3 倍 TIMESLOT),当客户端长时间不活动时,定时器会触发回调函数关闭连接,避免资源浪费。
  • 模块化设计:将连接初始化与定时器管理分离,通过数组下标(connfd)快速索引对应资源,提高效率。

该函数是服务器并发连接管理的关键部分,确保了服务器能自动清理空闲连接,维持稳定的资源占用。

  void WebServer::deal_timer(tulun::util_timer *timer, int sockfd){LOG_TRACE<<"deal_timer: timer: "<<timer;LOG_TRACE<<"deal_timer: sockfd: "<<sockfd;LOG_TRACE<<"call timer->cb_func(&users_timer[sockfd])";timer->cb_func(&users_timer[sockfd]);if (nullptr != timer){LOG_TRACE<<" utils.m_timer_list.del_timer(timer)";utils.m_timer_list.del_timer(timer);}}

这段 WebServer::deal_timer 函数是 Web 服务器中处理超时或需要关闭的客户端连接的核心方法,主要作用是执行超时连接的清理逻辑并移除对应的定时器,避免资源泄漏。以下是详细解析:

1. 函数功能概述

当客户端连接超时(未在规定时间内活动)或因其他原因需要关闭时,该函数会被调用,完成两件核心工作:

  • 执行预设的回调函数,关闭客户端连接并释放相关资源。
  • 将该连接对应的定时器从定时器链表中删除,避免重复处理。

2. 代码逐行解析

LOG_TRACE<<"deal_timer: timer: "<<timer;
LOG_TRACE<<"deal_timer: sockfd: "<<sockfd;
  • 输出调试日志,记录当前处理的定时器指针(timer)和客户端连接的文件描述符(sockfd),用于追踪连接关闭过程。
LOG_TRACE<<"call timer->cb_func(&users_timer[sockfd])";
timer->cb_func(&users_timer[sockfd]);
  • 调用定时器绑定的回调函数(cb_func),并传入与该连接关联的元数据(users_timer[sockfd])。
    • users_timer 是存储客户端连接元数据的数组(下标为 sockfd),包含连接的地址、文件描述符等信息。
    • 回调函数 cb_func 的核心逻辑通常是:关闭 sockfd 对应的客户端连接、从 epoll 事件表中移除该文件描述符、减少连接计数等,实现连接的彻底释放。
if (nullptr != timer)
{LOG_TRACE<<" utils.m_timer_list.del_timer(timer)";utils.m_timer_list.del_timer(timer);
}
  • 若定时器指针有效(非 nullptr),则将其从定时器链表(utils.m_timer_list)中删除。
    • 定时器链表用于统一管理所有客户端的超时定时器,删除操作确保该定时器不再参与后续的超时检查,避免无效的重复处理。

3. 调用场景

该函数通常在以下情况被触发:

  • 客户端连接超时(定时器到期时,由定时器链表的检查逻辑调用)。
  • 客户端主动断开连接或发生错误(如 EPOLLRDHUPEPOLLERR 事件),需要清理资源。
  • 读写操作失败时(如 dealwithread 或 dealwithwrite 中检测到异常)。

4. 核心作用总结

  • 资源释放:通过回调函数关闭客户端连接,释放文件描述符、epoll 事件等资源,防止资源泄漏。
  • 定时器管理:从链表中移除已处理的定时器,保证定时器链表的有效性,避免对已关闭的连接进行无效的超时检查。

该函数是服务器连接生命周期管理的重要环节,确保系统能及时清理无效连接,维持稳定的资源占用。

http://www.dtcms.com/a/498682.html

相关文章:

  • 吃透 C++ 栈和队列:stack/queue/priority_queue 用法 + 模拟 + STL 标准实现对比
  • 重庆网站模版建设青岛做网站的公司排名
  • 圆桌讨论:Coding Agent or AI IDE 的现状和未来发展
  • 相机成像中的平行平面成像
  • 并发集合踩坑现场:ConcurrentHashMap size() 阻塞、HashSet 并发 add 丢数据、Queue 伪共享
  • BT之家1LOU站永久地址发布页 - 回归初心,最新官网入口
  • 《Linux系统编程之入门基础》【Linux基础 理论+命令】(上)
  • 如何套用别人网站模板wordpress 4.7.6
  • Git个人配置偏好记录以及注意事项
  • 做律师网站推广优化哪家好哪些设计网站可以赚钱
  • Windows / Linux 中如何使用 Docker 部署项目
  • JPA 学习笔记 4:JPQL
  • Linux网络编程:Socket编程UDP
  • 详解Redis锁误删、原子性难题及Redisson加锁底层原理、WatchDog续约机制
  • 【Java 集合】核心知识点梳理
  • 郑州建设厅官方网站地方网站推广
  • 宁波网站建设建站厂家wordpress 站点描述
  • 兴扬汽车网站谁做的公司设计图
  • 上海石化有做网站设计的吗广西网络广播电视台直播
  • 网站和推广在一家做的好处卓智网络科技有限公司
  • 推广网站有哪些做网站销售水果
  • 产品网站推广淄博做网站建设的公司
  • 整站下载器 安卓版企业网站多大空间够用
  • 博罗做网站战队头像在线制作免费
  • 东莞专业网站建设推广欧洲c2c平台
  • 手机网站做指向沃尔玛网上商城可以用购物卡吗
  • 中国建设银行北京市互联网网站成都小程序开发公司找哪家
  • 电商网站要素如何提升网站营销力
  • 成都模板网站建设服务深圳创纪录暴雨19小时
  • 网站地图后缀响应式博客网站模板