1.16.C++项目:仿muduo库实现并发服务器之HttpContext以及HttpServer模块的设计
文章目录
- 一、HttpContext模块
- 二、HttpServer模块
- 三、HttpContext模块实现思想
- (一)功能
- (二)意义
- (三)接口
- 四、HttpServer模块实现思想
- (一)功能
- (二)意义
- (三)分析
- 五、代码
一、HttpContext模块
要实现渐变的搭建HTTP服务器,所需要提供的要素和功能!
要素:
1. GET请求的路由映射表
2. POST请求的路由映射表
3. PUT请求的路由映射表
4. DELETE请求的路由映射表 —— 路由映射表记录对应请求方法的请求的处理函数映射关系
5. 高性能TCP服务器—— 进行连接的IO操作
6. 静态资源相对根目录 —— 实现静态资源的处理
接口:
服务器处理流程:
1. 从socket接受数据放到接受缓冲区
2. 调用onmessage回调函数进行业务处理
3. 对请求进行介意,得到了一个HTTPREQUEST结构,包含了所有的请求要素!
4. 进行请求的路由映射 —— 找到对应请求的处理方法1. 静态资源请求 —— 一些实体文件资源的请求 html,image将静态资源文件的数据读取出来,填充到HTTPresponse结构中2. 功能性请求 —— 在请求路由映射表中查找处理函数,找到了则执行函数具体的业务请求,并进行HTTPREsponse结构的数据填充
5. 对静态资源请求——功能性请求处理完毕后,得到一个填充了相应信息的httpResponse 的对象,组织http响应格式进行发送!
接口:
- 添加请求—— 处理函数映射信息(GET/POST/PUT/DELETE)
- 设置静态资源根目录
- 设置是否启动超时连接关闭
- 设置线程池中线程数量
- 启动服务器
- OnConnected - 用于给TcpServer设置协议上下文
- OnMessage - 用于进行缓冲区数据解析处理
- 获取上下文,进行缓冲区数据对象
- 请求的路由查
找 - 静态资源请求查找和处理
- 功能性请求查找和处理组织响应进程回复
二、HttpServer模块
三、HttpContext模块实现思想
(一)功能
记录HTTP请求的接受和处理进度。
(二)意义
有可能出现接收的数据并不是一条完整的HTTP请求数据,也就是请求的处理需要在多次受到数据之后才能处理完成,因此在每次处理的时候,就需要将进度处理记录下来,以便于下次从当前进度继续向下处理。
(三)接口
- 接收并且处理数据
- 接受请求行
- 解析请求行
- 接收头部
- 解析头部
- 接受正文
- 返回解析完成的请求信息
四、HttpServer模块实现思想
(一)功能
对于HTTP协议支持所有模块的整合
(二)意义
让HTTP服务器的搭建变得更加简单
(三)分析
HttpServer模块:用于实现HTTP服务器的搭建
表中记录了针对哪个请求,应该使用哪个函数来进行业务处理的映射关系
当服务器收到了一一个请求,就在请求路由表中,查找有没有对应请求的处理函数,如果有,则执行对应的处理函数即可说白了,什么请求,怎么处理,由用户来设定,服务器收到了请求只需要执行函数即可,这样做的好处:用户只需要实现业务处理函数,然后将请求与处理函数的映射关系,添加到服务器中而服务器只需要接收数据,解析数据,查找路由表映射关系,执行业务处理函数。
五、代码
- HttpResponse
class HttpResponse {public:int _statu;bool _redirect_flag;std::string _body;std::string _redirect_url;std::unordered_map<std::string, std::string> _headers;public:HttpResponse():_redirect_flag(false), _statu(200) {}HttpResponse(int statu):_redirect_flag(false), _statu(statu) {} void ReSet() {_statu = 200;_redirect_flag = false;_body.clear();_redirect_url.clear();_headers.clear();}//插入头部字段void SetHeader(const std::string &key, const std::string &val) {_headers.insert(std::make_pair(key, val));}//判断是否存在指定头部字段bool HasHeader(const std::string &key) {auto it = _headers.find(key);if (it == _headers.end()) {return false;}return true;}//获取指定头部字段的值std::string GetHeader(const std::string &key) {auto it = _headers.find(key);if (it == _headers.end()) {return "";}return it->second;}void SetContent(const std::string &body, const std::string &type = "text/html") {_body = body;SetHeader("Content-Type", type);}void SetRedirect(const std::string &url, int statu = 302) {_statu = statu;_redirect_flag = true;_redirect_url = url;}//判断是否是短链接bool Close() {// 没有Connection字段,或者有Connection但是值是close,则都是短链接,否则就是长连接if (HasHeader("Connection") == true && GetHeader("Connection") == "keep-alive") {return false;}return true;}
};
- HttpServer模块
class HttpServer {private:using Handler = std::function<void(const HttpRequest &, HttpResponse *)>;using Handlers = std::vector<std::pair<std::regex, Handler>>;Handlers _get_route;Handlers _post_route;Handlers _put_route;Handlers _delete_route;std::string _basedir; //静态资源根目录TcpServer _server;private:void ErrorHandler(const HttpRequest &req, HttpResponse *rsp) {//1. 组织一个错误展示页面std::string body;body += "<html>";body += "<head>";body += "<meta http-equiv='Content-Type' content='text/html;charset=utf-8'>";body += "</head>";body += "<body>";body += "<h1>";body += std::to_string(rsp->_statu);body += " ";body += Util::StatuDesc(rsp->_statu);body += "</h1>";body += "</body>";body += "</html>";//2. 将页面数据,当作响应正文,放入rsp中rsp->SetContent(body, "text/html");}//将HttpResponse中的要素按照http协议格式进行组织,发送void WriteReponse(const PtrConnection &conn, const HttpRequest &req, HttpResponse &rsp) {//1. 先完善头部字段if (req.Close() == true) {rsp.SetHeader("Connection", "close");}else {rsp.SetHeader("Connection", "keep-alive");}if (rsp._body.empty() == false && rsp.HasHeader("Content-Length") == false) {rsp.SetHeader("Content-Length", std::to_string(rsp._body.size()));}if (rsp._body.empty() == false && rsp.HasHeader("Content-Type") == false) {rsp.SetHeader("Content-Type", "application/octet-stream");}if (rsp._redirect_flag == true) {rsp.SetHeader("Location", rsp._redirect_url);}//2. 将rsp中的要素,按照http协议格式进行组织std::stringstream rsp_str;rsp_str << req._version << " " << std::to_string(rsp._statu) << " " << Util::StatuDesc(rsp._statu) << "\r\n";for (auto &head : rsp._headers) {rsp_str << head.first << ": " << head.second << "\r\n";}rsp_str << "\r\n";rsp_str << rsp._body;//3. 发送数据conn->Send(rsp_str.str().c_str(), rsp_str.str().size());}bool IsFileHandler(const HttpRequest &req) {// 1. 必须设置了静态资源根目录if (_basedir.empty()) {return false;}// 2. 请求方法,必须是GET / HEAD请求方法if (req._method != "GET" && req._method != "HEAD") {return false;}// 3. 请求的资源路径必须是一个合法路径if (Util::ValidPath(req._path) == false) {return false;}// 4. 请求的资源必须存在,且是一个普通文件// 有一种请求比较特殊 -- 目录:/, /image/, 这种情况给后边默认追加一个 index.html// index.html /image/a.png// 不要忘了前缀的相对根目录,也就是将请求路径转换为实际存在的路径 /image/a.png -> ./wwwroot/image/a.pngstd::string req_path = _basedir + req._path;//为了避免直接修改请求的资源路径,因此定义一个临时对象if (req._path.back() == '/') {req_path += "index.html";}if (Util::IsRegular(req_path) == false) {return false;}return true;}//静态资源的请求处理 --- 将静态资源文件的数据读取出来,放到rsp的_body中, 并设置mimevoid FileHandler(const HttpRequest &req, HttpResponse *rsp) {std::string req_path = _basedir + req._path;if (req._path.back() == '/') {req_path += "index.html";}bool ret = Util::ReadFile(req_path, &rsp->_body);if (ret == false) {return;}std::string mime = Util::ExtMime(req_path);rsp->SetHeader("Content-Type", mime);return;}//功能性请求的分类处理void Dispatcher(HttpRequest &req, HttpResponse *rsp, Handlers &handlers) {//在对应请求方法的路由表中,查找是否含有对应资源请求的处理函数,有则调用,没有则发挥404//思想:路由表存储的时键值对 -- 正则表达式 & 处理函数//使用正则表达式,对请求的资源路径进行正则匹配,匹配成功就使用对应函数进行处理// /numbers/(\d+) /numbers/12345for (auto &handler : handlers) {const std::regex &re = handler.first;const Handler &functor = handler.second;bool ret = std::regex_match(req._path, req._matches, re);if (ret == false) {continue;}return functor(req, rsp);//传入请求信息,和空的rsp,执行处理函数}rsp->_statu = 404;}void Route(HttpRequest &req, HttpResponse *rsp) {//1. 对请求进行分辨,是一个静态资源请求,还是一个功能性请求// 静态资源请求,则进行静态资源的处理// 功能性请求,则需要通过几个请求路由表来确定是否有处理函数// 既不是静态资源请求,也没有设置对应的功能性请求处理函数,就返回405if (IsFileHandler(req) == true) {//是一个静态资源请求, 则进行静态资源请求的处理return FileHandler(req, rsp);}if (req._method == "GET" || req._method == "HEAD") {return Dispatcher(req, rsp, _get_route);}else if (req._method == "POST") {return Dispatcher(req, rsp, _post_route);}else if (req._method == "PUT") {return Dispatcher(req, rsp, _put_route);}else if (req._method == "DELETE") {return Dispatcher(req, rsp, _delete_route);}rsp->_statu = 405;// Method Not Allowedreturn ;}//设置上下文void OnConnected(const PtrConnection &conn) {conn->SetContext(HttpContext());DBG_LOG("NEW CONNECTION %p", conn.get());}//缓冲区数据解析+处理void OnMessage(const PtrConnection &conn, Buffer *buffer) {while(buffer->ReadAbleSize() > 0){//1. 获取上下文HttpContext *context = conn->GetContext()->get<HttpContext>();//2. 通过上下文对缓冲区数据进行解析,得到HttpRequest对象// 1. 如果缓冲区的数据解析出错,就直接回复出错响应// 2. 如果解析正常,且请求已经获取完毕,才开始去进行处理context->RecvHttpRequest(buffer);HttpRequest &req = context->Request();HttpResponse rsp(context->RespStatu());if (context->RespStatu() >= 400) {//进行错误响应,关闭连接ErrorHandler(req, &rsp);//填充一个错误显示页面数据到rsp中WriteReponse(conn, req, rsp);//组织响应发送给客户端context->ReSet();buffer->MoveReadOffset(buffer->ReadAbleSize());//出错了就把缓冲区数据清空conn->Shutdown();//关闭连接return;}if (context->RecvStatu() != RECV_HTTP_OVER) {//当前请求还没有接收完整,则退出,等新数据到来再重新继续处理return;}//3. 请求路由 + 业务处理Route(req, &rsp);//4. 对HttpResponse进行组织发送WriteReponse(conn, req, rsp);//5. 重置上下文context->ReSet();//6. 根据长短连接判断是否关闭连接或者继续处理if (rsp.Close() == true) conn->Shutdown();//短链接则直接关闭}return;}public:HttpServer(int port, int timeout = DEFALT_TIMEOUT):_server(port) {_server.EnableInactiveRelease(timeout);_server.SetConnectedCallback(std::bind(&HttpServer::OnConnected, this, std::placeholders::_1));_server.SetMessageCallback(std::bind(&HttpServer::OnMessage, this, std::placeholders::_1, std::placeholders::_2));}void SetBaseDir(const std::string &path) {assert(Util::IsDirectory(path) == true);_basedir = path;}/*设置/添加,请求(请求的正则表达)与处理函数的映射关系*/void Get(const std::string &pattern, const Handler &handler) {_get_route.push_back(std::make_pair(std::regex(pattern), handler));}void Post(const std::string &pattern, const Handler &handler) {_post_route.push_back(std::make_pair(std::regex(pattern), handler));}void Put(const std::string &pattern, const Handler &handler) {_put_route.push_back(std::make_pair(std::regex(pattern), handler));}void Delete(const std::string &pattern, const Handler &handler) {_delete_route.push_back(std::make_pair(std::regex(pattern), handler));}void SetThreadCount(int count) {_server.SetThreadCount(count);}void Listen() {_server.Start();}
};
相关文章:

1.16.C++项目:仿muduo库实现并发服务器之HttpContext以及HttpServer模块的设计
文章目录 一、HttpContext模块二、HttpServer模块三、HttpContext模块实现思想(一)功能(二)意义(三)接口 四、HttpServer模块实现思想(一)功能(二)意义&#…...

ABAP 新增PO计划行时 新增行交货日期默认当前最大交期
ABAP 新增PO计划行时 新增行交货日期默认当前最大交期 DATA: ls_poitem TYPE mepoitem. DATA: ls_jhh TYPE meposchedule. DATA: ls_poitemc TYPE REF TO if_purchase_order_item_mm. DATA: is_persistent TYPE mmpur_bool. DATA: lt_eket TYPE TABLE OF eket. DATA: ls_e…...

VSCode怎么创建Java项目
首先安装好Java的开发环境:JDK在VSCode中安装适用于Java开发的插件。打开VSCode,点击左侧的扩展图标,搜索并安装Java Extension Pack插件。等待安装完成后,重启VSCode生效。创建一个新的Java项目,按下Ctrl Shift P&a…...

软件工程与计算(十四)详细设计中面向对象方法下的模块化
一.面向对象中的模块 1.类 模块化是消除软件复杂度的一个重要方法,每个代码片段相互独立,这样能够提高可维护性。在面向对象方法中,代码片段最重要的类,整个类的所有代码联合起来构成独立的代码片段。 模块化希望代码片段由两部…...

商城免费搭建之java商城 开源java电子商务Spring Cloud+Spring Boot+mybatis+MQ+VR全景+b2b2c
1. 涉及平台 平台管理、商家端(PC端、手机端)、买家平台(H5/公众号、小程序、APP端(IOS/Android)、微服务平台(业务服务) 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…...

python教程:selenium WebDriver 中的几种等待
嗨喽,大家好呀~这里是爱看美女的茜茜呐 强制等待:sleep() import time sleep(5) #等待5秒设置固定休眠时间,单位为秒。 由python的time包提供, 导入 time 包后就可以使用。 缺点: 不智能,使用太多的sleep会影响脚本运行速度。…...

【MATLAB源码-第49期】基于蚁群算法(ACO)算法的栅格路径规划,输出最佳路径图和算法收敛曲线图。
操作环境: MATLAB 2022a 1、算法描述 蚁群算法是一种模拟自然界蚂蚁觅食行为的启发式优化算法。在蚁群系统中,通过模拟蚂蚁之间通过信息素沟通的方式来寻找最短路径。 在栅格路径规划中,蚁群算法的基本步骤如下: 1. 初始化: …...

LabVIEW生产者消费者架构
LabVIEW生产者消费者架构 生产者/消费者模式可以轻松地同时处理多个进程,同时还能以不同速率迭代。 缓冲通信 当多个进程以不同速度运行时,就适合采用进程间缓冲通信。有了足够大的缓冲区后,生产者循环可以以快于消费者循环的速度运行&…...

成都瀚网科技:如何有效运营抖店来客呢?
随着电子商务的快速发展和移动互联网的普及,越来越多的企业开始将目光转向线上销售渠道。其中,抖音成为备受关注的平台。作为中国最大的短视频社交平台之一,抖音每天吸引数亿用户,这也为企业提供了巨大的商机。那么,如…...

iMazing2.17.3免费苹果手机备份还原助手
强大的 iOS 设备管理软件不管是 iPhone、iPad 或 iPod Touch 设备,只要将 iOS 设备连接到计算机,就可以处理不同类型的数据。iMazing 功能强大、易于使用,可以帮助您安全备份任何 iPhone、iPad 或 iPod Touch,备受用户信赖。 现在…...

sql查询到了数据但是实体类个别字段为null(映射失败)
参考博客: sql查询到了数据但是实体类个别字段为null(映射失败)_sql语句查到值,加字段!null没值-CSDN博客 问题描述: 1.SQL查询到了数据,在idea调试output控制台窗口打印出SQL语句,字段有值; 2.接收实体对象字段无…...

配置VScode开发环境-CUDA编程
如果觉得本篇文章对您的学习起到帮助作用,请 点赞 关注 评论 ,留下您的足迹💪💪💪 本文主要介绍VScode下的CUDA编程配置,因此记录以备日后查看,同时,如果能够帮助到更多人…...

openGauss学习笔记-101 openGauss 数据库管理-管理数据库安全-客户端接入之用SSH隧道进行安全的TCP/IP连接
文章目录 openGauss学习笔记-101 openGauss 数据库管理-管理数据库安全-客户端接入之用SSH隧道进行安全的TCP/IP连接101.1 背景信息101.2 前提条件101.3 操作步骤 openGauss学习笔记-101 openGauss 数据库管理-管理数据库安全-客户端接入之用SSH隧道进行安全的TCP/IP连接 101.…...

STM32如何使用中断?
一:EXTI 简介 STM32F10x 外部中断/事件控制器(EXTI)包含多达 20 个用于产生事件/中断请求的边沿检测器。 EXTI 的每根输入线都可单独进行配置,以选择类型(中断或事件)和相应的触发事件(上升 沿触…...

用于物体识别和跟踪的下游任务自监督学习-2-(计算机视觉中的距离度量+损失函数)
2.4 计算机视觉中的距离度量 在深度学习和计算机视觉中,距离度量通常用于比较图像、视频或其他数据的特征或嵌入。根据具体任务和数据属性,可以使用不同类型的距离度量。下面介绍了深度学习和计算机视觉中使用的一些常见类型的距离度量。 余弦相似性距…...

热成像仪的工作原理及在工业设备状态监测中的应用
前面我们介绍过>>热分析技术在工业设备状态监测中的应用,下面我们将深入探讨热成像仪的工作原理及在工业设备状态监测中的应用。 近年来,热成像仪作为一种先进的检测工具,在工业设备状态监测领域得到了广泛的应用。热成像仪能够通过探测…...

什么是库存管理?无需Excel,2023年这几款大热库存管理软件你get了吗?
什么是库存管理?库存管理是什么意思?都有哪些好用的库存管理系统?相信这些都是大家非常关注的话题,这篇就跟大家详细唠唠什么是库存管理,都用什么系统可以进行库存管理,并且为大家盘点2023年这几款大热库存…...

c# xml 参数配置表的使用
使用简介 实际使用界面 配置表管理界面 进入 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms;…...

ubuntu20.04 nerf Instant-ngp
Instant-ngp linux ubuntu 20.04 GPU RTX3050Ti Instant-ngp官方文档地址 https://github.com/NVlabs/instant-ngp 参考链接Instant-ngp linux部署及使用 - 简书 Ubuntu20.04复现instant-ngp,自建数据集,导出mesh_XINYU W的博客-CSDN博客 步骤 安装基…...

隐写术--python隐写
0x00 背景 何为隐写术? 隐写术是一类可以隐藏自己写的一些东西的方法,是一门关于信息隐藏的技巧与科学。指的是采取一些不让除预期的接收者之外的任何人知晓信息的传递事件或者信息的内容的方法。 可参考 一文让你完全弄懂Stegosaurus - 知乎 本文要…...

MySQL的InnoDB存储引擎中的自适应哈希索引技术
一、自适应哈希索引的工作机制与优化策略 MySQL的InnoDB存储引擎使用了一种叫做自适应哈希索引(Adaptive Hash Indexes)的技术。在某些索引值被频繁访问的情况下,InnoDB会自动在内存中为这些值建立哈希索引,以加速查询操作。 何…...

交互设计主要做什么?新手入门必读
什么是交互设计?它涉及哪些内容?交互设计师是什么样的人群?他们到底是做什么的?他们身怀什么技能?他们工作的价值在哪里?交互设计行业的现状是怎样的?工作前景又是如何的? 如果你心…...

【深度学习实验】循环神经网络(三):门控制——自定义循环神经网络LSTM(长短期记忆网络)模型
目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. LSTM类 a.__init__(初始化) b. init_state(初始化隐藏状态) c. forward(前向传播) 2. RNNModel类 a.__init__&am…...

flutter 消息并发时处理,递归查询
收到新消息的时候执行receiveNewConversation方法 可以自己模拟一下两条数据插入,延时执行插入会话的操作 收到一条新的会话消息,先记录会话ID到列表,直到第一条处理完(插入数据库后清理这个会话ID),才处理…...

第五十八章 学习常用技能 - 查看查询缓存
文章目录 第五十八章 学习常用技能 - 查看查询缓存查看查询缓存建立索引使用调谐表工具 第五十八章 学习常用技能 - 查看查询缓存 查看查询缓存 对于 SQL(用作嵌入式 SQL 时除外),系统会生成可重用代码来访问数据,并将该代码放置…...

AI 辅助学 Java | 专栏 1 帮你学 Java
在利用 ChatGPT 辅助学 Java 之前,你得先知道,它到底能辅助你干什么?如何能帮你更好的学习 Java。 苍何:作为一个语言模型,你能给 Java 的初学者提供什么帮助?请罗列具体的点。 ChatGPT:当你是一个 Java 初学者时,我可以提供以下具体的帮助和指导: 基本语法和语言特…...

2023_Spark_实验十六:编写LoggerLevel方法及getLocalSparkSession方法
一、搭建Spark项目结构 在SparkProject模块的pom.xml文件中增加一下依赖,并等待依赖包下载完毕,如上图。 <!-- Spark及Scala的版本号 --><properties><scala.version>2.11</scala.version><spark.version>2.1.1</sp…...

彻底搞懂:防止表单重复提交,前端限制还是后端限制?
欢迎大家来到小米的技术分享专栏!今天我将为大家带来一个热门话题:如何有效地防止表单重复提交。在开发中,我们常常会遇到这样的问题:用户频繁点击提交按钮,导致数据重复提交,给系统和用户体验带来不必要的…...

OCPP1.6协议
目录 导言 功能简介 本地授权列表 类型 IdToken IdTagInfo 授权状态 协议指令 1、授权 1.1 说明 1.2 Authorize.req 1.3 Authorize.conf 1.4 JSON格式 1.5 代码 2、启动通知 2.1 说明 2.2 BootNotification.req 2.3 BootNotification.conf 2.4 JSON格式 2…...

【数据存储:小端模式和大端模式】
一、引言 在计算机科学中,数据存储模式是指如何将数据存储在计算机内存中的方式。小端模式和大端模式是两种主要的字节序方式,它们决定了字节在内存中的排列顺序。这种字节顺序的选择对于跨平台编程和数据传输至关重要。在这篇博客中,我们将…...