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

webserver类续

 bool WebServer::dealclientdata(){LOG_TRACE << "新客户的链接请求到达";struct sockaddr_in client_address;socklen_t client_addrlength = sizeof(client_address);if (0 == m_LISTENTrigmode){LOG_TRACE << "0 == m_LISTENTrigmode: " << (0 == m_LISTENTrigmode);int connfd = accept(m_listenfd, (struct sockaddr *)&client_address, &client_addrlength);LOG_INFO << "accept ret connfd: " << connfd;if (connfd < 0){LOG_ERROR << " accept error: " << strerror(errno);return false;}if (http_conn::m_user_count >= MAX_FD){utils.show_error(connfd, "Internal server busy");LOG_ERROR << "Internal server busy: m_user_count: " << http_conn::m_user_count;return false;}LOG_TRACE<<"call timer(connfd,client_address)";timer(connfd, client_address);}else{LOG_TRACE << "0 != m_LISTENTrigmode: " << (0 != m_LISTENTrigmode);while (1){int connfd = accept(m_listenfd, (struct sockaddr *)&client_address, &client_addrlength);LOG_INFO << "accept ret connfd: " << connfd;if (connfd < 0){LOG_ERROR << "accept error : " << strerror(errno);break;}if (http_conn::m_user_count >= MAX_FD){utils.show_error(connfd, "Internal server busy");LOG_ERROR << "Internal server busy m_user_count: " << http_conn::m_user_count;break;}timer(connfd, client_address);}return false;}LOG_TRACE << " function end ";return true;}

这段 WebServer::dealclientdata 函数是 Web 服务器处理新客户端连接请求的核心方法,根据监听套接字的触发模式(LT 或 ET),完成客户端连接的接收、资源检查及定时器初始化工作。以下是详细解析:

1. 函数功能概述

当服务器的监听套接字(m_listenfd)通过 epoll 检测到新连接事件(EPOLLIN)时,该函数被调用,主要职责包括:

  • 接收客户端的连接请求,获取新连接的文件描述符(connfd)。
  • 检查服务器资源是否充足(如连接数是否达到上限 MAX_FD)。
  • 为新连接初始化定时器(通过调用 timer 函数),用于后续的超时管理。
  • 根据监听套接字的触发模式(LT 或 ET),采用不同的连接处理逻辑。

2. 核心变量说明

  • m_LISTENTrigmode:监听套接字的触发模式(0 表示 LT 水平触发,1 表示 ET 边缘触发),由 trig_mode 函数初始化。
  • client_address:存储客户端的网络地址信息(IP 和端口)。
  • connfd:新建立的客户端连接的文件描述符,用于后续与客户端的通信。
  • http_conn::m_user_count:当前服务器已建立的连接总数,用于判断是否超过最大连接限制 MAX_FD

3. 代码逻辑解析

(1)LT 触发模式(m_LISTENTrigmode == 0
if (0 == m_LISTENTrigmode) {// 接收单个连接int connfd = accept(m_listenfd, (struct sockaddr *)&client_address, &client_addrlength);// 错误处理:accept 失败if (connfd < 0) {LOG_ERROR << " accept error: " << strerror(errno);return false;}// 连接数达上限:返回错误并关闭连接if (http_conn::m_user_count >= MAX_FD) {utils.show_error(connfd, "Internal server busy");LOG_ERROR << "Internal server busy: m_user_count: " << http_conn::m_user_count;return false;}// 为新连接初始化定时器timer(connfd, client_address);
}
  • 处理逻辑:LT 模式下,epoll 会持续通知未处理的连接事件,因此只需调用一次 accept 接收一个连接即可(后续未处理的连接会在下一次事件循环中被触发)。
  • 资源检查:若连接数已达上限(MAX_FD),通过 utils.show_error 向客户端返回 “服务器繁忙” 的错误,并关闭该连接。
  • 定时器初始化:调用 timer 函数为新连接创建定时器,管理其超时逻辑。
(2)ET 触发模式(m_LISTENTrigmode != 0
else {while (1) {// 循环接收所有待处理的连接int connfd = accept(m_listenfd, (struct sockaddr *)&client_address, &client_addrlength);// 错误处理:无更多连接时退出循环(ET 模式下 accept 返回 -1 且 errno 为 EAGAIN 或 EWOULDBLOCK)if (connfd < 0) {LOG_ERROR << "accept error : " << strerror(errno);break;}// 连接数达上限:返回错误并关闭连接,退出循环if (http_conn::m_user_count >= MAX_FD) {utils.show_error(connfd, "Internal server busy");LOG_ERROR << "Internal server busy m_user_count: " << http_conn::m_user_count;break;}// 为新连接初始化定时器timer(connfd, client_address);}return false;
}
  • 处理逻辑:ET 模式下,epoll 仅在连接事件首次发生时通知一次,因此需要通过 while(1) 循环调用 accept,直到返回错误(表示当前已无新连接待处理),避免遗漏连接。
  • 退出条件:当 accept 返回 -1 时(通常因 errno 为 EAGAIN 或 EWOULDBLOCK,表示暂时无新连接),退出循环。
  • 返回值:ET 模式下处理完所有连接后返回 false,不影响后续事件循环(因所有连接已一次性处理完毕)。

4. 关键作用总结

  • 连接接收:根据监听模式(LT/ET)差异,分别采用单次或循环 accept 方式,确保不遗漏客户端连接。
  • 资源控制:通过检查 m_user_count 限制最大连接数,防止服务器资源耗尽。
  • 超时管理前置:为每个新连接绑定定时器(timer 函数),是后续超时连接自动清理的基础。

该函数是服务器处理并发连接的入口,通过适配不同的触发模式,平衡了连接处理的效率与可靠性。

 bool WebServer::dealwithsignal(bool &timeout, bool &stop_server){LOG_INFO<<"call dealwithsignal timeout: "<<timeout<<" stop_server: "<<stop_server;int ret = 0;int sig;char signals[1024];ret = recv(m_pipefd[0], signals, sizeof(signals), 0);LOG_INFO<<"call recv:m_pipefd[0]:" <<m_pipefd[0]<<"  ret: "<<ret;if (ret == -1){return false;}else if (ret == 0){return false;}else{for (int i = 0; i < ret; ++i){LOG_INFO<<"signals[i]: "<<static_cast<int>(signals[i]);LOG_INFO<<" SIGALRM: "<<14;LOG_INFO<<" SIGTERM: "<<15;switch (signals[i]){case SIGALRM: // 当使用alarm函数设置一个定时器后,当定时器时间到达,就会产生SIGALRM信号timeout = true;break;case SIGTERM: // SIGTERM是一种信号(Signal),用于在类 Unix 操作系统中请求一个进程终止stop_server = true;break;}}}LOG_TRACE<<" end ";return true;}

这段 WebServer::dealwithsignal 函数是 Web 服务器中处理信号事件的核心方法,主要作用是解析通过管道传递的信号,并根据信号类型设置对应的标志位,以触发后续的超时处理或服务器停止逻辑。以下是详细解析:

1. 函数功能概述

在 Linux 系统中,信号是进程间通信的一种方式。服务器通过 socketpair 创建了一对管道(m_pipefd),当信号(如定时器超时信号 SIGALRM、终止信号 SIGTERM)产生时,信号处理函数会将信号写入管道的写端(m_pipefd[1]),而本函数则从管道的读端(m_pipefd[0])读取信号,并根据信号类型设置 timeout 或 stop_server 标志,供主事件循环(eventLoop)处理。

2. 代码逐行解析

LOG_INFO<<"call dealwithsignal timeout: "<<timeout<<" stop_server: "<<stop_server;
  • 日志输出,记录函数调用时 timeout(超时标志)和 stop_server(服务器停止标志)的初始状态,用于调试。
int ret = 0;
int sig;
char signals[1024];
ret = recv(m_pipefd[0], signals, sizeof(signals), 0);
  • 从管道读端(m_pipefd[0])读取信号数据:signals 数组用于存储读取到的信号(每个信号以字符形式存储),ret 接收实际读取的字节数。
  • 管道的作用:将信号事件转换为 I/O 事件,使服务器能在 epoll 主循环中统一处理信号(避免信号处理函数中直接操作复杂逻辑)。
LOG_INFO<<"call recv:m_pipefd[0]:" <<m_pipefd[0]<<"  ret: "<<ret;
if (ret == -1)
{return false;
}
else if (ret == 0)
{return false;
}
  • 错误处理:若 recv 失败(ret == -1)或读取到 0 字节(管道关闭),返回 false 表示信号处理失败。
else
{for (int i = 0; i < ret; ++i){LOG_INFO<<"signals[i]: "<<static_cast<int>(signals[i]);LOG_INFO<<" SIGALRM: "<<14;LOG_INFO<<" SIGTERM: "<<15;switch (signals[i]){case SIGALRM: // 定时器超时信号timeout = true;break;case SIGTERM: // 进程终止请求信号stop_server = true;break;}}
}
  • 解析信号:循环处理读取到的每个信号(ret 为实际读取的信号数量)。
    • SIGALRM(值为 14):由 alarm(TIMESLOT) 定时触发,设置 timeout = true,通知主循环执行定时器检查(清理超时连接)。
    • SIGTERM(值为 15):通常由 kill 命令发送,请求进程终止,设置 stop_server = true,通知主循环退出并关闭服务器。
  • 日志输出信号值,便于调试时确认信号类型。
LOG_TRACE<<" end ";
return true;
  • 日志标记函数结束,返回 true 表示信号处理成功。

3. 调用场景与核心作用

  • 调用时机:当 epoll 检测到管道读端(m_pipefd[0])有可读事件(EPOLLIN)时,在主循环(eventLoop)中调用本函数。
  • 核心作用
    1. 统一处理信号:将信号事件转换为管道 I/O 事件,使服务器能在 epoll 框架中统一处理所有事件(信号、连接、读写等),简化逻辑。
    2. 触发后续操作:通过设置 timeout 和 stop_server 标志,让主循环决定是否执行定时器检查或停止服务器,避免在信号处理函数中直接操作共享资源(减少线程安全问题)。

该函数是服务器信号处理机制的关键,确保了信号能被安全、有序地处理,同时与主事件循环无缝衔接。

 void WebServer::dealwithread(int sockfd){util_timer *timer = users_timer[sockfd].timer;LOG_TRACE<<"sockfd: "<<sockfd<<" timer: "<<timer;LOG_TRACE<<" m_actormode: "<<m_actormodel;// reactorif (1 == m_actormodel){LOG_INFO<<"1 == actormodel";if (timer){adjust_timer(timer);}// 若监测到读事件,将该事件放入请求队列LOG_TRACE<<"若监测到读事件,将该事件放入请求队列 call m_pool->append(users+sockfd)";m_pool->append(users + sockfd, 0);while (true){if (1 == users[sockfd].improv){if (1 == users[sockfd].timer_flag){deal_timer(timer, sockfd);users[sockfd].timer_flag = 0;}users[sockfd].improv = 0;break;}}}else{// proactorLOG_TRACE<<"(0 == m_actormodel) : proactor: 主动器";LOG_TRACE<<"sockfd: "<<sockfd<<" users[sockfd]: "<<&users[sockfd];if (users[sockfd].read_once()){LOG_INFO << "deal with the client ip: " << inet_ntoa(users[sockfd].get_address()->sin_addr);// 若监测到读事件,将该事件放入请求队列LOG_INFO<<"若监测到读事件,将该事件放入请求队列 m_pool->append_p(users+sockf)";m_pool->append_p(users + sockfd);if (timer){adjust_timer(timer);}}else{deal_timer(timer, sockfd);}}}

 void WebServer::eventLoop(){bool timeout = false;bool stop_server = false;LOG_TRACE << "start runing";while (!stop_server){LOG_TRACE << "call epoll_wait: ";int number = epoll_wait(m_epollfd, events, MAX_EVENT_NUMBER, -1);LOG_TRACE << "epoll_wait ret number  = " << number;if (number < 0 && errno != EINTR){LOG_ERROR << "epoll failure";break;}for (int i = 0; i < number; i++){int sockfd = events[i].data.fd;// 处理新到的客户连接if (sockfd == m_listenfd){LOG_TRACE << " 处理新到的客户连接: sockfd: " << sockfd;LOG_TRACE << "m_listenfd : " << m_listenfd;bool flag = dealclientdata();if (false == flag){continue;}}else if (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)){// 服务器端关闭连接,移除对应的定时器LOG_INFO<<"服务器端关闭连接,移除对应的定时器 ";util_timer *timer = users_timer[sockfd].timer;deal_timer(timer, sockfd);}else if ((sockfd == m_pipefd[0]) && (events[i].events & EPOLLIN)){// 处理信号LOG_INFO << " 处理信号";bool flag = dealwithsignal(timeout, stop_server);if (false == flag){LOG_ERROR << "dealclientdata failure";}}// 处理客户连接上接收到的数据else if (events[i].events & EPOLLIN){LOG_INFO<<"处理客户连接上接收到的数据";dealwithread(sockfd);}else if (events[i].events & EPOLLOUT){LOG_INFO<<"写数据 ";dealwithwrite(sockfd);}}if (timeout){LOG_INFO<<"timeout: "<<timeout;LOG_INFO<<"call utils.timer_handler(): ";utils.timer_handler();LOG_INFO << "timer tick"; // 定时器滴答timeout = false;}}LOG_TRACE << " running stop ";}

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

相关文章:

  • 条款22:使用Pimpl惯用法时,将特种成员函数的定义放到实现文件中
  • 基于python大数据的省级城市政企客户业务分析系统
  • 合肥微网站制作初中做语文综合题的网站
  • dedecms做论坛网站广州网页设计师学校
  • Blender + MCP 全流程详细图文教程
  • 自定义classload实现热加载案例
  • 上海网站建设seo站霸网络网站建设推销拜访客户怎么开头
  • Spring Bean的生命周期 第二次思考
  • HttpServletResponse下载文件
  • vue3的路由详解
  • Spring Boot 生产就绪特性浅析(一)
  • 如何做彩票网站信息手机上打开html的软件
  • 【图像处理基石】图像对比度增强入门:从概念到实战(Python+OpenCV)
  • 网站建设公司六安全国连锁装修公司加盟
  • 直播互动小程序端Web运营端接入指南
  • Java—抽象类
  • 坛墨网站建设wordpress 邀请
  • idc网站模版百度提交
  • 树莓派3B+降低功耗及恢复脚本
  • 开源项目解读4-高性能并发缓存库Ristretto
  • 《微信小程序》第五章:登录-API封装
  • MYSQL数据库入门操作
  • 青岛建设集团网站101工业设计有限公司
  • wordpress 网站上传到服务器错误怎么建设手机网站
  • MySQL 下载、安装及配置教程(Msi安装)
  • AWS CloudTrail 可观测最佳实践
  • 商城网站设计公司十大奢侈品排名
  • 建设部网站从哪登陆网站建设学什么书
  • STM32学习(MCU控制)(NVIC)
  • C++11新特性:强类型枚举/编译期断言/元组