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_port | int | 服务器监听的端口号(如 80、8080 等),用于客户端建立 TCP 连接时指定目标端口。 |
m_root | char* | 指向 Web 服务器静态资源根目录的路径(如/var/www/html ),用于查找并返回 HTML、CSS 等静态文件。 |
m_log_write | int | 日志写入模式的标志(如 0 表示同步写入,1 表示异步写入),控制日志输出的方式。 |
m_close_log | int | 日志开关标志(0 表示开启日志,1 表示关闭日志),用于控制是否记录服务器运行日志。 |
m_actormodel | int | 并发模型标志(如 0 表示 Proactor 模型,1 表示 Reactor 模型),决定服务器处理 I/O 事件的模式。 |
m_pipefd | int[2] | 用于信号处理的管道文件描述符数组(m_pipefd[0] 为读端,m_pipefd[1] 为写端),将信号转换为 I/O 事件供主循环处理。 |
m_epollfd | int | epoll 实例的文件描述符,是 I/O 多路复用的核心,用于监控所有客户端连接的读写事件及信号管道事件。 |
users | http_conn* | 指向http_conn 对象数组的指针,每个元素对应一个客户端连接,封装了该连接的 HTTP 请求解析、响应构建等逻辑(数组大小为MAX_FD ,即最大文件描述符数量)。 |
这些变量是WebServer
类的核心成员,贯穿服务器的初始化(如init
函数)、事件监听(eventListen
)、事件循环(eventLoop
)等全过程,负责存储配置信息、管理 I/O 资源和客户端连接,是服务器正常运行的基础。
// 线程池相关tulun::threadpool<http_conn> *m_pool;int m_thread_num;
tulun::threadpool<http_conn> *m_pool
- 这是一个指向
tulun
命名空间下的threadpool
模板类对象的指针,其中模板参数为http_conn
。 threadpool
是线程池的核心类,用于管理一组工作线程,实现线程的复用(避免频繁创建 / 销毁线程的开销)。- 模板参数
http_conn
指定了线程池处理的任务类型 —— 即每个工作线程将处理http_conn
类的对象(封装了客户端的 HTTP 连接和请求处理逻辑)。 - 该指针在
WebServer
初始化阶段(如thread_pool
成员函数)会被实例化,指向实际创建的线程池对象。
- 这是一个指向
int m_thread_num
- 用于存储线程池中的工作线程数量(线程池规模)。
- 该值通常通过
WebServer
的init
函数从外部传入(对应参数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 复用及连接模式相关的成员变量定义,主要用于配置和管理服务器的网络事件处理机制,具体解释如下:
变量名 | 类型 | 含义与作用 |
---|---|---|
events | epoll_event[] | 存储epoll 监控的事件数组,大小为MAX_EVENT_NUMBER (最大事件数)。当epoll_wait 返回时,就绪的事件会被填充到该数组中,供服务器处理(如客户端连接、读写请求等)。 |
m_listenfd | int | 服务器监听套接字的文件描述符。通过socket 创建,绑定端口后用于监听客户端的连接请求(listen 调用的对象)。 |
m_OPT_LINGER | int | 套接字关闭模式标志,对应socket 选项SO_LINGER 。用于控制关闭连接时是否等待未发送数据发送完成(0 表示立即关闭,1 表示优雅关闭,等待数据发送)。 |
m_TRIGMode | int | 事件触发模式的总开关,通常是一个组合值(如 0-3),用于配置监听套接字和连接套接字的触发模式(LT/ET)。 |
m_LISTENTrigmode | int | 监听套接字(m_listenfd )的事件触发模式(LT 水平触发或 ET 边缘触发),由m_TRIGMode 解析得到。 |
m_CONNTrigmode | int | 客户端连接套接字的事件触发模式(LT 或 ET),同样由m_TRIGMode 解析得到,用于处理已建立连接的读写事件。 |
核心逻辑关联
这些变量是服务器实现I/O 多路复用(epoll) 和事件触发机制的基础:
epoll_event
数组events
是epoll
机制的核心数据结构,用于接收就绪事件,是服务器处理并发连接的关键。m_listenfd
是服务器与客户端建立连接的入口,所有新连接请求都通过该套接字接收。- 触发模式相关变量(
m_TRIGMode
、m_LISTENTrigmode
、m_CONNTrigmode
)决定了epoll
如何通知事件(LT 模式下事件会被重复通知,直到处理完成;ET 模式下仅通知一次,效率更高但需一次性处理完数据)。 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 ) |
---|---|---|---|
0 | LT + LT 组合模式 | 0(LT 水平触发) | 0(LT 水平触发) |
1 | LT + ET 组合模式 | 0(LT 水平触发) | 1(ET 边缘触发) |
2 | ET + LT 组合模式 | 1(ET 边缘触发) | 0(LT 水平触发) |
3 | ET + ET 组合模式 | 1(ET 边缘触发) | 1(ET 边缘触发) |
关键概念补充
- LT(水平触发):只要套接字缓冲区中还有未处理的数据(可读 / 可写),
epoll
就会持续触发事件通知,适合简单场景,无需一次性处理完所有数据。 - ET(边缘触发):仅在套接字缓冲区状态发生变化时(如新数据到达、缓冲区从不可写到可写)触发一次事件,效率更高,但要求一次性处理完所有数据,否则可能丢失事件。
函数作用总结
- 模式解析:将外部配置的
m_TRIGMode
(总触发模式)拆解为两个具体套接字的触发模式,实现监听和连接套接字的灵活组合配置。 - 参数初始化:为
m_LISTENTrigmode
和m_CONNTrigmode
赋值,这两个参数会在后续的eventListen
(创建监听套接字)、addfd
(注册事件)等函数中被使用,决定epoll
如何监控事件。 - 日志记录:输出最终的触发模式配置,便于调试和确认服务器运行状态。
该函数是服务器事件驱动模型的基础配置环节,直接影响 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
函数,主要作用是根据服务器配置初始化日志系统,控制日志的写入模式和存储参数。具体解释如下:
函数逻辑说明
日志开关判断通过
m_close_log
(日志开关标志)判断是否开启日志:- 若
m_close_log == 0
:表示开启日志,进入初始化流程。 - 若
m_close_log != 0
:表示关闭日志,函数不执行任何操作。
- 若
日志写入模式选择当开启日志时,通过
m_log_write
(日志写入模式标志)选择不同的初始化参数:- 若
m_log_write == 1
:调用日志单例的init
方法,传入参数800
(推测为异步日志的队列大小或线程相关参数),启用异步日志模式。 - 若
m_log_write != 1
:调用init
方法时传入参数0
,启用同步日志模式。
- 若
日志初始化参数解析无论同步还是异步模式,
init
方法的前 4 个参数固定:"./ServerLog"
:日志文件存储路径。m_close_log
:日志开关状态(此处已确定为开启,即0
)。2000
:单个日志文件的最大大小(单位通常为字节)。8000
:日志文件的最大数量(超过后可能自动滚动或覆盖旧文件)。
核心作用
- 封装了日志系统的初始化逻辑,根据服务器配置(
m_close_log
和m_log_write
)决定是否开启日志以及使用同步 / 异步模式。 - 异步日志模式(
m_log_write == 1
)可避免日志写入阻塞主程序,适合高并发场景;同步模式(默认)实现简单,适合调试或低负载场景。
该函数通常在服务器启动初期调用,为后续的日志记录(如LOG_INFO
、LOG_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 连接对象准备数据库数据库操作的基础资源,具体解释如下:
函数逻辑说明
获取数据库连接池单例
m_connpool = tulun::connection_pool::GetInstance();
- 通过
connection_pool
类的静态方法GetInstance()
获取数据库连接池的单例实例,确保整个服务器中只有一个数据库连接池,避免资源浪费和连接冲突。
- 通过
初始化数据库连接池
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
:数据库登录的用户名和密码(从WebServer
的init
方法传入)。m_databasename
:要连接的数据库名称。3306
:数据库服务端口(MySQL 默认端口)。m_sql_num
:连接池中的最大连接数(控制并发数据库操作的连接上限)。m_close_log
:日志开关(控制是否输出数据库相关日志)。
- 该操作会预先创建指定数量的数据库连接,存入连接池供后续复用,避免频繁创建 / 销毁连接的开销。
- 调用连接池的
初始化 HTTP 连接的数据库操作资源
users->initmysql_result(m_connpool);
users
是http_conn
对象的数组(每个元素对应一个客户端连接),通过调用其initmysql_result
方法,将数据库连接池实例传递给 HTTP 连接对象。- 推测该方法的作用是:让
http_conn
对象初始化数据库查询相关的资源(如预处理语句、结果集等),以便后续处理客户端请求时(如用户登录、数据查询)能直接从连接池获取连接并执行数据库操作。
日志跟踪函数中通过
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 请求)提供线程资源支持,具体解释如下:
函数逻辑说明
创建线程池实例
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
:指定线程池的工作模式(reactor
或proactor
,影响任务处理流程)。m_connpool
:数据库连接池实例,线程池中的工作线程可通过它获取数据库连接,执行数据库操作。m_thread_num
:线程池中的线程数量,决定了服务器并发处理任务的能力。
- 通过
日志记录
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. 调用场景
该函数通常在以下情况被触发:
- 客户端连接超时(定时器到期时,由定时器链表的检查逻辑调用)。
- 客户端主动断开连接或发生错误(如
EPOLLRDHUP
、EPOLLERR
事件),需要清理资源。 - 读写操作失败时(如
dealwithread
或dealwithwrite
中检测到异常)。
4. 核心作用总结
- 资源释放:通过回调函数关闭客户端连接,释放文件描述符、
epoll
事件等资源,防止资源泄漏。 - 定时器管理:从链表中移除已处理的定时器,保证定时器链表的有效性,避免对已关闭的连接进行无效的超时检查。
该函数是服务器连接生命周期管理的重要环节,确保系统能及时清理无效连接,维持稳定的资源占用。