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

项目中HTTP协议处理部分

  • EPOLLIN:表示监控文件描述符(如套接字)的可读事件。当该文件描述符有数据可读时(如客户端发送了 HTTP 请求),epoll 会将其标记为就绪状态,触发后续处理。

  • EPOLLET:启用边缘触发(Edge Triggered)模式。这是 epoll 的高效工作模式,仅在文件描述符状态从 “不可读” 变为 “可读” 时触发一次事件(而非水平触发的持续触发)。配合非阻塞 IO 使用,可减少事件通知次数,提升性能(需在代码中一次性读完所有数据)。

  • EPOLLRDHUP:监控对方关闭连接或半关闭连接的事件。当客户端主动关闭连接时,epoll 会捕获该事件,便于及时清理连接资源,避免无效的读写操作。

这三个标志通过位或(|)组合,表示 epoll 实例需要同时监控:

  1. 目标文件描述符的可读数据(EPOLLIN);
  2. 以边缘触发模式处理事件(EPOLLET);
  3. 对方关闭连接的事件(EPOLLRDHUP)。
 void addfd(int epollfd, int fd, bool one_shot, int TRIGMode){LOG_INFO << "epollfd: " << epollfd << " fd: " << fd << " one_shot: " << one_shot << " TRIGMode: " << TRIGMode;struct epoll_event event;event.data.fd = fd;if (1 == TRIGMode){event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;}else{event.events = EPOLLIN | EPOLLRDHUP;}if (one_shot){event.events |= EPOLLONESHOT;}LOG_TRACE << "call  epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event)";epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);setnonblocking(fd);}

addfd 函数,用于将文件描述符(通常是套接字)注册到 epoll 实例中,实现对该文件描述符的事件监控。

函数作用

将指定的文件描述符(fd)添加到 epoll 内核事件表(由 epollfd 标识),并配置监控的事件类型、触发模式等,同时将文件描述符设置为非阻塞模式,是 epoll 事件驱动模型中的核心配置函数。

参数说明

  • epollfdepoll 实例的文件描述符(通过 epoll_create 创建),代表要操作的内核事件表。
  • fd:需要添加到 epoll 监控的目标文件描述符(如客户端连接的套接字)。
  • one_shot:布尔值,标识是否启用 EPOLLONESHOT 模式(事件只触发一次)。
  • TRIGMode:触发模式(0 表示水平触发 LT,1 表示边缘触发 ET)。

代码逻辑解析

  1. 初始化 epoll_event 结构体

    struct epoll_event event;
    event.data.fd = fd;  // 绑定要监控的文件描述符,方便后续处理时识别
    

    epoll_event 是 epoll 机制的核心结构体,data.fd 用于存储待监控的文件描述符,后续 epoll_wait 返回就绪事件时,可通过该字段定位具体的文件描述符。

  2. 配置事件类型(根据触发模式 TRIGMode

    • 边缘触发模式(ET,TRIGMode=1

      event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
      
      • EPOLLIN:监控文件描述符的可读事件(如客户端发送数据)。
      • EPOLLET:启用边缘触发模式,仅在文件描述符状态从 “不可读” 变为 “可读” 时触发一次事件(需配合非阻塞 IO 一次性读完数据)。
      • EPOLLRDHUP:监控对方关闭连接的事件(如客户端断开连接)。
    • 水平触发模式(LT,TRIGMode=0

      event.events = EPOLLIN | EPOLLRDHUP;
      

      不启用 EPOLLET,默认水平触发模式:只要文件描述符有数据可读,就会持续触发事件。

  3. 启用 EPOLLONESHOT 模式(可选)

    if (one_shot) {event.events |= EPOLLONESHOT;  // 位或操作添加该事件标志
    }
    

    EPOLLONESHOT 确保某个文件描述符的事件只会被一个线程处理一次。若需要再次监控,需通过 epoll_ctl 重新设置事件(避免多线程同时处理同一连接的事件,导致数据混乱)。

  4. 将文件描述符添加到 epoll 实例

    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    

    调用 epoll_ctl 系统调用,以 EPOLL_CTL_ADD 操作为标志,将配置好的 event 注册到 epollfd 对应的内核事件表中,完成监控注册。

  5. 设置文件描述符为非阻塞模式

    setnonblocking(fd);
    

    调用自定义函数 setnonblocking 将 fd 设为非阻塞模式,配合 epoll 的 ET 模式使用时,可确保高效读取所有数据(避免阻塞在 recv/read 调用上)。

MYSQL 结构体

  • 定义在 MySQL 客户端开发库的头文件 <mysql/mysql.h> 中,是 MySQL C API 的核心结构体,用于表示一个数据库连接的上下文信息(如连接状态、服务器信息、查询结果等)。
  • 通过该结构体指针,可调用 MySQL 提供的 API 函数(如 mysql_query 执行 SQL 语句、mysql_store_result 获取查询结果等)完成数据库操作。
// 从状态机,用于分析出一行内容// 返回值为行的读取状态,有LINE_OK,LINE_BAD,LINE_OPENhttp_conn::LINE_STATUS http_conn::parse_line(){LOG_TRACE << "in parse_line";char temp = '\0';LOG_TRACE << "m_checked_idx: " << m_checked_idx << " m_read_idx: " << m_read_idx;LOG_TRACE << "m_read_buf: " << m_read_buf;for (; m_checked_idx < m_read_idx; ++m_checked_idx){temp = m_read_buf[m_checked_idx];//LOG_INFO << "m_checked_idx: " << m_checked_idx << " temp: " << static_cast<int>(temp) << " " << temp;if (temp == '\r'){LOG_TRACE << " if (temp == '\r')";LOG_TRACE << " if (m_read_buf[m_checked_idx + 1])" << static_cast<int>(m_read_buf[m_checked_idx + 1]);if ((m_checked_idx + 1) == m_read_idx){return LINE_OPEN;}else if (m_read_buf[m_checked_idx + 1] == '\n'){m_read_buf[m_checked_idx++] = '\0';m_read_buf[m_checked_idx++] = '\0';LOG_TRACE << "ret LINE_OK: "<<m_read_buf;return LINE_OK;}}else if (temp == '\n'){LOG_TRACE << "else if (temp == '\n') ";if (m_checked_idx > 1 && m_read_buf[m_checked_idx - 1] == '\r'){m_read_buf[m_checked_idx - 1] = '\0';m_read_buf[m_checked_idx++] = '\0';return LINE_OK;}LOG_TRACE << "return LINE_BAD";return LINE_BAD;}}return LINE_OPEN;}/*
这段代码是 http_conn 类中的 parse_line 方法,作用是解析 HTTP 请求中的一行数据,判断当前读取的缓冲区内容是否构成完整的 HTTP 协议行(以 \r\n 为结束标志),并返回行的解析状态(LINE_OK、LINE_BAD、LINE_OPEN)。
核心逻辑与解析规则
HTTP 协议规定,请求行和请求头的每行数据必须以 \r\n(回车 + 换行)作为结束符。parse_line 方法通过遍历读取缓冲区(m_read_buf),根据字符判断行的完整性和合法性,具体流程如下:
遍历缓冲区:通过循环遍历 m_read_buf 中从 m_checked_idx 到 m_read_idx 的数据(m_checked_idx 是已检查的位置,m_read_idx 是缓冲区中实际数据的末尾位置)。
判断 \r 字符:当遇到 \r(回车)时:
若下一个位置(m_checked_idx + 1)已到达缓冲区末尾(m_read_idx),说明数据不完整,返回 LINE_OPEN(需要继续读取)。
若下一个字符是 \n(换行),则符合 \r\n 结束符规范:
将 \r 替换为 \0(字符串结束符),m_checked_idx 后移一位。
将 \n 替换为 \0,m_checked_idx 再后移一位。
返回 LINE_OK(成功解析一行)。
判断 \n 字符:当遇到 \n(换行)时:
若前一个字符是 \r(即实际是 \r\n,但可能因缓冲区拆分导致 \r 在上一次读取中),则同样替换 \r 和 \n 为 \0,返回 LINE_OK。
若前一个字符不是 \r,则不符合 HTTP 行结束规范,返回 LINE_BAD(行格式错误)。
遍历结束仍无完整行:若遍历完当前缓冲区数据仍未找到完整的 \r\n,返回 LINE_OPEN(需要继续读取更多数据)。
关键变量作用
m_read_buf:存储从客户端读取的 HTTP 请求数据的缓冲区。
m_checked_idx:记录已检查过的缓冲区位置,避免重复解析。
m_read_idx:记录缓冲区中已读取数据的末尾位置(有效数据长度)。
temp:当前遍历到的字符。
返回值含义
LINE_OK:成功解析出一行完整的 HTTP 数据(符合 \r\n 结束规范)。
LINE_BAD:行格式错误(如仅以 \n 结束,无 \r)。
LINE_OPEN:数据不完整,需继续读取更多数据才能判断行的完整性。
总结
parse_line 是 HTTP 请求解析的基础工具方法,通过识别 \r\n 结束符判断行的完整性,为后续解析请求行(parse_request_line)、请求头(parse_headers)提供了结构化的输入(以 \0 结尾的字符串),确保 HTTP 协议解析的正确性。
*/

 m_start_linem_checked_idx 和 m_read_buf 的关系,我们可以通过一个具体的 HTTP 请求解析过程来举例说明:

假设场景

客户端发送的 HTTP 请求数据(存储在 m_read_buf 中)如下:

plaintext

GET /index.html HTTP/1.1\r\n
Host: localhost:8080\r\n
Connection: keep-alive\r\n
\r\n

(注:\r\n 是 HTTP 协议中每行的结束标志)

变量含义与解析过程

  1. m_read_buf:存储从客户端读取的原始请求数据的缓冲区,上述请求数据会被完整存入其中。

  2. m_checked_idx:记录已经解析过的位置(当前检查到的索引),初始值为 0,随着解析推进不断递增。

  3. m_start_line:记录当前正在解析的 “行” 在 m_read_buf 中的起始索引,每解析完一行后会更新到下一行的起始位置。

解析步骤演示

初始状态
  • m_read_buf:存储上述 HTTP 请求数据(假设索引从 0 开始)。
  • m_checked_idx = 0(尚未开始解析)。
  • m_start_line = 0(当前行的起始位置为 0)。
第一步:解析请求行(第一行 GET /index.html HTTP/1.1\r\n
  • 程序通过 parse_line() 函数查找行结束标志 \r\n,发现第一行的 \r\n 位于索引 23-24(假设)。
  • 解析完成后,m_checked_idx 会移动到 25(跳过 \r\n,指向第二行的起始位置)。
  • 此时 m_start_line 仍为 0(表示当前解析的行从 0 开始)。
  • 解析完这一行后,m_start_line 被更新为 25(下一行的起始位置)。
第二步:解析头部字段(第二行 Host: localhost:8080\r\n
  • parse_line() 继续从 m_checked_idx=25 开始查找,发现第二行的 \r\n 位于索引 45-46。
  • 解析完成后,m_checked_idx 移动到 47
  • 此时 m_start_line=25(当前行的起始位置),解析完成后更新为 47
第三步:解析头部字段(第三行 Connection: keep-alive\r\n
  • 同理,解析完成后 m_checked_idx 移动到该行 \r\n 之后的位置(假设为 68),m_start_line 更新为 68
第四步:解析空行(\r\n
  • 最后一行是 \r\n(表示头部结束),解析完成后 m_checked_idx 移动到整个请求的末尾,m_start_line 也随之更新。

总结三者关系

  • m_read_buf 是整个请求数据的 “容器”。
  • m_start_line 标记 “当前正在解析的行” 的起点。
  • m_checked_idx 标记 “已经解析到的位置”,也是下一行解析的起点。

通过这三个变量的配合,程序能够从 m_read_buf 中 “逐行” 提取数据(请求行、头部字段等),实现对 HTTP 请求的分步解析。例如,当需要获取当前行的内容时,直接通过 m_read_buf + m_start_line 即可定位到该行的起始地址,直到 m_checked_idx 所标记的结束位置。

struct iovec m_iv[2];

这段代码在 http_conn 类的私有成员中声明了一个包含 2 个 struct iovec 元素的数组 m_iv,主要用于高效地集中管理 HTTP 响应数据的缓冲区,配合 writev 系统调用一次性发送多个不连续的缓冲区数据。

关键说明:

  1. struct iovec 结构体

    • 定义于 <sys/uio.h>,包含两个成员:iov_base(指向数据缓冲区的起始地址)和 iov_len(缓冲区的字节长度)。
    • 作用是将分散的内存缓冲区 “拼接” 成一个逻辑上连续的缓冲区,便于通过 writev 系统调用一次性写入(避免多次系统调用的开销)。
  2. m_iv[2] 的具体用途

    • 数组长度为 2,通常用于分别存储 HTTP 响应的头部信息文件内容(或响应体):
      • m_iv[0]:关联响应头部缓冲区(如 m_write_buf),存储状态行、响应头、空行等元数据。
      • m_iv[1]:关联响应体数据(如通过 mmap 映射的文件内容 m_file_address),存储实际的资源数据(如 HTML 内容、图片二进制数据等)。
  3. 与发送流程的关联

    • 在构建响应时,m_iv_count 会记录实际使用的 iovec 数量(通常为 2,即头部 + 体部)。
    • 调用 writev(m_sockfd, m_iv, m_iv_count) 时,系统会按顺序发送 m_iv[0] 和 m_iv[1] 指向的数据,实现 “零拷贝” 或高效批量发送,减少 I/O 操作次数,提升性能。

m_iv 是 HTTP 服务器优化响应发送效率的关键结构,通过整合分散的响应数据缓冲区,实现一次系统调用完成多段数据的发送。

http_conn 类的私有成员中声明了整数变量 m_iv_count,用于记录 m_iv 数组中实际使用的 struct iovec 结构体元素数量。

具体说明:

  1. 与 m_iv 的关联

    • m_iv 是一个包含 2 个 struct iovec 元素的数组,用于管理 HTTP 响应的分散缓冲区(如响应头部和响应体)。
    • m_iv_count 则标记当前实际使用了 m_iv 中的多少个元素(通常为 1 或 2)。
  2. 取值场景

    • 当响应仅包含头部信息(如某些错误响应)时,m_iv_count 可能为 1(仅使用 m_iv[0] 存储头部)。
    • 当响应包含头部和文件内容(如正常的静态资源请求)时,m_iv_count 为 2(m_iv[0] 存头部,m_iv[1] 存文件数据)。
  3. 在发送流程中的作用

    • 调用 writev 系统调用发送响应时,m_iv_count 作为第三个参数传入,告知系统需要发送的缓冲区数量,确保只发送有效数据,避免处理未使用的缓冲区,提升 I/O 效率。

m_iv_count 是 m_iv 数组的 “有效长度标记”,用于准确控制多缓冲区数据的发送范围,是高效处理 HTTP 响应发送的重要辅助变量。

enum LINE_STATUS //用于表示 HTTP 请求数据行的解析状态,主要在解析请求行和请求头时判断一行数据是否完整或有效。{LINE_OK = 0,LINE_BAD, // 坏的LINE_OPEN // 当前读取的缓冲区数据不完整};

枚举类型 LINE_STATUS,用于表示 HTTP 请求数据行的解析状态,主要在解析请求行和请求头时判断一行数据是否完整或有效。

各枚举值的含义:

  1. LINE_OK:表示成功解析出一行完整的 HTTP 数据(以 \r\n 作为行结束符)。例如,请求行 GET /index.html HTTP/1.1\r\n 或请求头 Host: example.com\r\n 被完整解析后,会返回此状态。

  2. LINE_BAD:表示解析到的行格式错误(不符合 HTTP 协议规范的行结束格式)。例如,行结束符不是 \r\n(如仅 \n 或其他字符),此时服务器可判定为无效请求,后续可能返回 BAD_REQUEST 错误。

  3. LINE_OPEN:表示当前读取的缓冲区数据中,一行数据尚未完整(未检测到 \r\n 结束符)。这种情况通常需要继续读取更多数据到缓冲区,直到凑齐完整的一行或判定为错误。

应用场景:

在 parse_line 方法中,会根据 m_read_buf 中的数据判断行的状态:

  • 若检测到完整的 \r\n,返回 LINE_OK,并更新解析索引(m_checked_idx 和 m_start_line),以便后续处理该行数据(如解析请求方法、URL 等)。
  • 若未检测到 \r\n 且缓冲区已读满,可能返回 LINE_BAD 或 LINE_OPEN(取决于是否仍有数据待读取)。
  • 若数据不完整(未读满缓冲区且无 \r\n),返回 LINE_OPEN,等待下一次数据到达后继续解析。

LINE_STATUS 是 HTTP 请求解析过程中用于跟踪行数据完整性的状态标记,是逐步解析请求行、请求头的基础。

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

相关文章:

  • 二元锦标赛:进化算法中的选择机制及其应用
  • 2026新选题-基于Python的老年病医疗数据分析系统的设计与实现(数据采集+可视化分析)
  • Linux权限核心:chmod命令终极指南(文字与数字法详解)
  • 太原网站建设总部地址青岛seo推广专员
  • 藏语自然语言处理入门 - 4 找相似的句子
  • ubuntu 环境
  • php网站开发 多少钱服务器网站建设软件有哪些
  • Python 图像中矩形四角二维坐标和归一化一维坐标相互转换
  • 做电商网站有什么用万网网站建设教程
  • 网站 设计风格wordpress 加链接地址
  • 中山市企业网站seo营销工具wordpress 搜索 自定义字段
  • 05、Python从入门到癫狂:数据库操作与其他
  • 网页制作报价徐州seo外包平台
  • jQuery简化了事件的绑定和解除,常用的方法有.on()和.off()
  • [Dify] 知识库切片逻辑解析:段落切分 vs 语义块切分,该怎么选?
  • [Windows] 发票识别工具。支持xml、pdf、ofd文件
  • 流量安全——基于Sentinel实现限流,熔断,降级
  • Semaphore GUI 详细介绍
  • 中山网站优化营销做专业课视频课的网站
  • 元表纪基于一个Excel表实现一键发货、打印面单
  • 企业外贸网站建设建设一个直播网站多少钱
  • 网站建设需要基础吗电子商务网站建设与管理是什么
  • 【LeetCode - 每日1题】换水问题1
  • 资深面试之MySQL 问题及解答(一)
  • 自定义脚手架
  • 云空间布置网站seo顾问赚钱吗
  • 网络设备中的硬件转发和软件转发
  • 永州建设网站公司网站开发费的税率是多少
  • js时间格式转化器
  • 攻防世界-Web-Web_php_unserialize