TinyWebServer源码解析——HTTP机制

TinyWebServer源码解析——HTTP机制

项目地址
https://github.com/qinguoyi/TinyWebServer.git
尊重开源精神,保障作者权益,内容均为原创,如有雷同,纯属巧合

笔者能力有限,我认为这个部分还有更多的技术栈能够写出来,但是我的能力做不到了,如果以后有机会,我会再写一个修订版。敬请期待!

定义了一个http_conn类

实现了一个基于Linux epoll I/O 多路复用模型的高性能 HTTP 服务器核心模块


http_conn类
封装内容
  1. void init()函数初始化连接
  2. void close_conn()函数关闭连接
  3. void process()主处理函数,由epoll触发后调用
  4. bool read_once()函数一次性读取数据
  5. bool write()函数发送响应数据
  6. HTTP_CODE process_read()函数驱动状态机解析HTTP请求
  7. bool process_write()函数生成HTTP响应
  8. bool add_response()该函数格式化字符串并追加到写缓冲区
  9. bool add_content()函数添加内容到响应体
  10. bool add_status_line()函数添加状态行
  11. bool add_headers()函数添加通用响应头
  12. bool add_content_length()函数单独添加content_length头
  13. bool add_linger()函数添加Connection: keep-aliveclose
  14. bool add_blank_line()函数表示响应头结束
  15. LINE_STATUS parse_line()函数从m_read_buf中查找完整一行
  16. HTTP_CODE parse_request_line()函数解析请求行
  17. HTTP_CODE parse_headers()函数解析单个请求头字段
  18. HTTP_CODE parse_content()函数处理POST请求体
  19. HTTP_CODE do_request()函数决定返回什么内容
  20. void initmysql_result()函数一次性从数据库加载所有用户到全局

事件驱动(Event-Driven)
组成:epoll+非阻塞I/O模型

程序不主动轮询或阻塞等待 I/O,而是注册对某些“事件”的兴趣,当事件发生时,由系统通知程序进行处理

核心组件:
组件作用
epollLinux 高效 I/O 多路复用机制(替代 select/poll)
非阻塞 socket避免 read/write 阻塞线程
事件循环(Event Loop)主线程不断调用 epoll_wait() 等待事件
回调/处理函数事件触发时执行的逻辑(如 http_conn::process()
具体体现:
  • 主线程使用epoll_wait()监听多个socket事件
  • 当某个客户端socket可读或者可写,epoll通知服务器
  • 非阻塞socket+ET/LT模式
状态机(State Machine)

HTTP 请求是分阶段、异步到达的(可能分多个 TCP 包),状态机能增量解析,避免等待完整数据,提升响应速度和内存效率

状态任务
解析请求行找到第一行 "GET ..."
解析请求头逐行读取 Host:, Content-Length:
解析请求体如果是 POST,读取 username=... 这部分内容
将复杂的任务拆分为小步骤
体现位置:HTTP请求解析过程
具体体现:
  • 定义枚举状态CHECK_STATE
enumCHECK_STATE{ CHECK_STATE_REQUESTLINE =0,//解析请求行 CHECK_STATE_HEADER,//解析请求头 CHECK_STATE_CONTENT //解析请求体};
  • process_read()状态转移
http_conn::HTTP_CODE http_conn::process_read(){ LINE_STATUS line_status = LINE_OK; HTTP_CODE ret = NO_REQUEST;char*text =0;while((m_check_state == CHECK_STATE_CONTENT && line_status == LINE_OK)||((line_status =parse_line())== LINE_OK)){ text =get_line();//获取当前行 m_start_line = m_checked_idx;LOG_INFO("%s", text);switch(m_check_state){case CHECK_STATE_REQUESTLINE:{ ret =parse_request_line(text);//可能转移到HEADERif(ret == BAD_REQUEST)return BAD_REQUEST;break;}case CHECK_STATE_HEADER:{ ret =parse_headers(text);//可能转移到CONTENT或完成if(ret == BAD_REQUEST)return BAD_REQUEST;elseif(ret == GET_REQUEST){returndo_request();}break;}case CHECK_STATE_CONTENT:{ ret =parse_content(text);//完成if(ret == GET_REQUEST)returndo_request(); line_status = LINE_OPEN;break;}default:return INTERNAL_ERROR;}}return NO_REQUEST;}
零拷贝(Zero-Copy)
组成:mmap()+writev()发送文件
具体体现
  • 传统发送文件需要数据从磁盘 → 内核 → 用户 → 内核 → 网卡,4次上下文切换,4次数据拷贝
  • 零拷贝实现
// 1. mmap 将文件映射到用户空间(实际是内核页缓存的映射)  m_file_address =(char*)mmap(0, m_file_stat.st_size, PROT_READ, MAP_PRIVATE, m_file_fd,0);// 2. 构造 iovec 数组(响应头 + 文件内容)  m_iv[0].iov_base = m_write_buf;// 响应头(小)  m_iv[0].iov_len = m_write_idx; m_iv[1].iov_base = m_file_address;// 文件内容(大)  m_iv[1].iov_len = m_file_stat.st_size;// 3. 一次系统调用发送全部  ssize_t ret =writev(m_sockfd, m_iv,2);
  • CUP不参与数据的搬运,在用户态与内核态之间无冗余拷贝
  • mmap本身不是零拷贝,需要配合writev/sendfile才能实现高效传输
三者联系:

MySQLStatic Filehttp_connepoll (事件驱动)ClientMySQLStatic Filehttp_connepoll (事件驱动)Clientalt[解析完成且是静态文件][CGI 请求]发送 HTTP 请求(分包)触发 EPOLLIN 事件状态机解析(逐行)mmap 映射触发 EPOLLOUT(若需)writev(mmap地址) → 零拷贝发送查询/插入(非零拷贝路径)write() 发送动态内容


HTTP请求(HTTP Request)

进入网页时,浏览器向服务器发送一个请求

结构:
GET /index.html HTTP/1.1 ← 请求行(方法 + 路径 + 协议版本) Host: example.com ← 请求头(Header) User-Agent: Chrome/120 Connection: keep-alive ← 空行(分隔头和体) username=admin&password=123 ← 请求体(Body,仅 POST/PUT 有) 
  • GET请求通常没有请求体,参数在URL中
  • POST请求由请求体
响应头(Response Header)

服务器在返回内容前,先发送一些元信息(metadata),告诉浏览器“接下来要发什么

实例:
HTTP/1.1 200 OK ← 状态行(协议 + 状态码 + 描述) Content-Type: text/html ← 响应头 Content-Length: 1024 Connection: keep-alive Set-Cookie: sessionid=abc123 ← 空行(头结束) <html>... </html> ← 响应体 
常见头字段及作用:
头字段作用
Content-Type告诉浏览器内容类型(text/html, image/png, application/json
Content-Length响应体有多少字节
Connection是否保持连接(keep-aliveclose
Location用于重定向(302 状态码)
响应体(Response Body)

服务器真正返回给你的内容

实例:
<html> <head><title>Hello</title></head> <body><h1>Welcome!</h1></body> </html> 
mmap映射(Memory Mapping)

一种将文件直接映射到内存的技术。你可以像访问数组一样访问文件内容,而无需调用 read()

  • 传统方式:
char buffer[4096];int fd =open("a.jpg", O_RDONLY);while(read(fd, buffer,4096)>0){send(socket, buffer,...);// 数据:磁盘 → 内核 → 用户 → 内核 → 网卡(多次拷贝) }
  • mmap方式:
int fd =open("a.jpg", O_RDONLY);structstat sb;fstat(fd,&sb);char* data =(char*)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd,0);// 现在 data 就是文件内容! send(socket, data, sb.st_size,...);// 内核直接从页缓存发数据,无需用户缓冲区
优势:
  • 文件不经过用户空间缓冲区
  • CPU不参与数据搬运
  • 适合大文件传输
协作关系:

DBFile服务器(http_conn)浏览器DBFile服务器(http_conn)浏览器alt[请求静态文件(如 /image.jpg)][请求登录(POST /login)]发送 HTTP 请求(请求行+头+体)用状态机逐步解析请求用 mmap 映射文件到内存发送响应头 + mmap 的内容(响应体)查询数据库发送 JSON 响应头 + {"status":"ok"}(响应体)

术语一句话解释
HTTP 请求客户端问服务器:“我要什么?”
响应头服务器说:“我要给你的是 HTML,共 1024 字节,保持连接”
响应体服务器真正给你的内容(网页、图片、数据)
状态机服务器“边收边猜”客户端到底想干嘛(因为数据可能分批到)
mmap 映射服务器快速读大文件的“黑科技”,避免慢速拷贝

函数返回为HTTP_CODE
  • 特殊含义:
enumHTTP_CODE{ NO_REQUEST,// 请求不完整,需要继续读 GET_REQUEST,// GET 请求,可以处理 BAD_REQUEST,// 400 错误:语法错误 NO_RESOURCE,// 404 错误:文件不存在 FORBIDDEN_REQUEST,// 403 错误:无权限 FILE_REQUEST,// 静态文件请求,准备发送 INTERNAL_ERROR,// 500 错误:服务器内部错误 CLOSED_CONNECTION // 连接已关闭};
  • 语义相比于int或者bool,更加清晰
  • 统一错误处理机制
  • 支持更多中错误类型
  • 大量枚举的存在,增加代码容错率,以及更加清晰的状态返回参数

Read more

智能车竞赛实战:如何用地瓜机器人打造智慧医疗解决方案(附完整代码)

智能车竞赛实战:基于地瓜机器人的智慧医疗系统开发指南 在当今技术驱动的医疗创新浪潮中,智能车竞赛为大学生创客提供了绝佳的实践平台。地瓜机器人作为一款开源硬件平台,其灵活的可扩展性和丰富的传感器生态,使其成为开发智慧医疗解决方案的理想选择。本文将深入探讨如何从零开始构建一套完整的智慧医疗系统,涵盖硬件选型、算法设计到实战优化的全流程。 1. 硬件架构设计与环境搭建 构建智慧医疗系统的第一步是搭建可靠的硬件基础。地瓜机器人平台的核心优势在于其模块化设计,允许开发者根据具体需求灵活配置传感器和执行机构。 1.1 核心硬件选型建议 对于医疗应用场景,我们需要特别关注数据的准确性和系统的稳定性。以下是经过实战验证的硬件配置方案: * 主控单元:推荐使用地瓜机器人V3.2开发板,其搭载的STM32H743芯片提供充足的算力资源 * 环境传感器: * 温湿度:SHT31高精度数字传感器(±1.5%RH精度) * 空气质量:SGP30 VOC传感器 * 医疗监测模块: * 红外测温:MLX90614非接触式传感器 * 心率血氧:MAX30102光电传感器

手把手用ROS实现Ego-Planner动态避障:无人机撞树问题终结方案

手把手用ROS实现Ego-Planner动态避障:无人机撞树问题终结方案 你是否曾满怀期待地启动无人机,看着它在仿真环境中流畅起飞,却在下一秒“砰”地一声撞上突然出现的障碍物,仿真画面定格,留下一串令人沮丧的报错信息?在复杂、非结构化的真实飞行场景中,比如在枝叶交错的林间穿行,或在有行人、车辆移动的城区执行任务,传统的全局规划器往往显得力不从心。它们规划的路径可能全局最优,但面对瞬息万变的局部环境,反应速度跟不上变化,导致“撞树”成了家常便饭。今天,我们不谈空洞的理论对比,而是聚焦于一个能真正解决这个痛点的方案——Ego-Planner,并带你一步步在ROS和Gazebo搭建的仿真世界里,亲手实现一个能“眼观六路、随机应变”的无人机大脑。 本文面向的是已经具备一定ROS和无人机仿真基础,正被动态避障问题困扰的开发者、研究者或高级爱好者。我们将彻底抛开宏观的算法优劣论述,直接深入到代码配置、参数调优和实战排错层面。你将看到的不是“Ego-Planner实时性更好”这样的结论,而是“如何设置距离场梯度计算的网格分辨率”、“碰撞反作用力系数调到多少能让无人机既灵活又稳定”的具体操作。我们

VRM4U插件完整指南:在Unreal Engine 5中高效处理VRM模型

VRM4U插件完整指南:在Unreal Engine 5中高效处理VRM模型 【免费下载链接】VRM4URuntime VRM loader for UnrealEngine4 项目地址: https://gitcode.com/gh_mirrors/vr/VRM4U 还在为Unreal Engine 5中VRM模型导入的各种技术问题而烦恼吗?今天我要为你详细介绍一款能够彻底优化VRM工作流程的专业工具——VRM4U插件!这款专为UE5设计的VRM文件导入解决方案,让你能够专注于创意实现,而不是技术细节。 项目核心价值:为什么VRM4U是你的最佳选择 VRM4U插件不仅仅是一个格式转换器,它是一套完整的3D角色处理生态系统。通过智能化的技术实现,它解决了VRM模型在UE5环境中面临的多重挑战。 核心问题解决方案: * 自动化的材质系统转换 * 完整的骨骼结构映射 * 动画数据的无缝衔接 * 跨平台性能优化 快速入门:5分钟完成插件配置 获取插件资源 首先需要下载VRM4U插件,使用以下命令获取完整代码库: git clone https://gitcode

OFA-VE在AR内容生成中的应用:实时验证虚拟物体与现实图像逻辑关系

OFA-VE在AR内容生成中的应用:实时验证虚拟物体与现实图像逻辑关系 1. 引言:当虚拟遇见现实,如何确保它们“合情合理”? 想象一下,你正在开发一款增强现实(AR)应用,用户可以通过手机摄像头,在自家的客厅里“放置”一个虚拟的沙发。听起来很酷,对吧?但问题来了:如果用户家的客厅里已经摆满了家具,这个虚拟沙发应该放在哪里才显得真实、不突兀?是悬浮在半空,还是稳稳地落在地板上?它会不会和现实中的茶几“穿模”? 这就是AR内容生成中一个核心且棘手的挑战:逻辑一致性。虚拟物体不仅要“看起来”在现实场景中,更要“在逻辑上”与现实场景融为一体。传统方法往往依赖复杂的3D场景重建和物理引擎计算,过程繁琐且对硬件要求高。 今天,我们要介绍一个能优雅解决这个问题的“智能裁判”——OFA-VE。它不是一个AR开发工具,而是一个尖端的多模态推理系统。它的核心能力是进行“视觉蕴含”分析,简单来说,就是判断一段文字描述是否符合一张图片所展现的事实。 我们将深入探讨,如何利用OFA-VE的这种能力,为AR内容生成流程注入“逻辑验证”