【Linux网络】构建与优化HTTP请求处理 - HttpRequest从理解到实现

📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨

在这里插入图片描述

在这里插入图片描述

文章目录

  • 🏳️‍🌈一、HttpRequest 类
    • 1.1 基本结构
    • 1.2 构造函数、析构函数
    • 1.3 反序列化函数 Descrialize
    • 1.4 获取一行字符串 GetLine()
    • 1.5 打印方法 Print
    • 1.6 解析请求行 PraseReqLine
    • 1.7 解析请求头 void PraseHeader();
    • 1.8 增加路径字段
    • 1.9 测试
  • 🏳️‍🌈二 、整体代码
  • 👥总结


🏳️‍🌈一、HttpRequest 类

1.1 基本结构

我们结合这张图,构建出 http请求 的基本结构,并定义一些基本方法
在这里插入图片描述

const static std::string _base_sep = "\r\n";     // static 关键字使变量具有内部链接,仅当前翻译单元(源文件)可见。class HttpRequest {
private:std::string GetLine(std::string& reqstr); // 获取一行信息void PraseReqLine();                      // 解析请求行void PraseHeader();                       // 解析请求头
public:HttpRequest();void Descrialize(std::string& reqstr);void Print();~HttpRequest();private:std::string _req_line;                 // 请求行std::vector<std::string> _req_headers; // 请求报头std::string _blank_line;               // 空行std::string _req_body;                 // 请求体
};

1.2 构造函数、析构函数

构造函数初始化空行即可,因为空行是固定的,析构函数无需处理!

HttpRequest() : _blank_line(_base_sep) {}
~HttpRequest() {}

1.3 反序列化函数 Descrialize

我们这里是需要解析获取到的请求,所以用的方法自然是 反序列化

void Descrialize(std::string& reqstr) {// 基本的反序列化_req_line = GetLine(reqstr); // 读取第一行请求行// 请求报头std::string header;do {header = GetLine(reqstr);// 如果既不是空,也不是空行,就是请求报头,加入到请求报头列表中if (header.empty())break;else if (header == _base_sep)break;_req_headers.push_back(header);} while (true);// 正文if (!reqstr.empty())_req_body = reqstr;
}

1.4 获取一行字符串 GetLine()

// 获取一行信息
std::string GetLine(std::string& reqstr) {auto pos = reqstr.find(_base_sep);if (pos == std::string::npos)return "";std::string line = reqstr.substr(0, pos);  // 截取一行有效信息reqstr.erase(0, pos + _base_sep.length()); // 删除有效信息和分隔符return line.empty() ? _base_sep: line; // 有效信息为空则返回分隔符,否则返回有效信息
}

1.5 打印方法 Print

void Print() {std::cout << "----------------------------------------" << std::endl;std::cout << "请求行: " << _req_line << std::endl;std::cout << "请求报头: " << std::endl;for (auto& header : _req_headers) {std::cout << header << std::endl;}std::cout << "空行: " << _blank_line << std::endl;std::cout << "请求体: " << _req_body << std::endl;
}

当我们使用浏览器访问我们的服务器,就能够成功地将我们需要地所有信息给序列化出来,这里没有请求,所以请求体为空

在这里插入图片描述
在这里插入图片描述

1.6 解析请求行 PraseReqLine

我们已经知道请求行的组成如下,所以我们可以进一步细分 HttpRequest 类,增加响应的请求行的成员变量
在这里插入图片描述

std::string _method;  // 请求方法
std::string _url;     // 请求url
std::string _version; // 请求版本

将 _req_line(请求行)封装为字符串流,按空格分隔读取方法、路径、协议版本

// 解析请求行
void PraseReqLine() {// 以空格为分隔符,不断读取std::stringstream ss(_req_line);ss >> _method >> _url >> _version;
}

1.7 解析请求头 void PraseHeader();

在这里插入图片描述
我们可以知道请求报头中存在类似哈希表的 KV 结构,因此我们可以是使用一个 unordered_map,存储每一个键值对

在这里插入图片描述

根据我们之前获取到的请求报头,可以知道分隔符是 ": ",可以根据这个进行解析请求报头

// 解析请求头
void PraseHeader() {for (auto& header : _req_headers) {auto pos = header.find(':');if (pos == std::string::npos)continue;std::string k = header.substr(0, pos);std::string v = header.substr(pos + _line_sep.size());if (k.empty() || v.empty())continue;_headers_kv[k] = v;}
}

1.8 增加路径字段

**我们向服务器请求的时候,需要知道资源的路径,因此我们可以增加路径字段
**

  • 我们提供一个路径的前缀 wwwroot
  • 并且当这个用户访问的路径为 / 时,提供默认路径 default.html
const static std::string _prefix_path = "wwwroot";          // 默认前缀路劲
const static std::string _default_path = "default.html";    // 默认路径

我们可以在构造函数时,给路劲添加上默认前缀路径

HttpRequest() : _blank_line(_base_sep), _path(_prefix_path) {}

我们在解析 请求行url 进行分析,判断是否为空,并且给 path 赋值

void PraseReqLine(){    // 以空格为分隔符,不断读取std::stringstream ss(_req_line);ss >> _method >> _url >> _version;     _path += _url;// 处理url,如果是根目录,则返回默认路径if(_url == "/")_path += _default_path;}

提供两个新方法用来获取当前的 url路径

std::string Url() {LOG(LogLevel::INFO) << "client want url : " << _url;return _url;
}
std::string Path() {LOG(LogLevel::INFO) << "client want url : " << _path;return _path;
}

1.9 测试

我们运行服务端后,再用浏览器访问我们的服务器,成功捕捉到了两次请求,

  • ​第一次请求​(端口 3362):
    浏览器主动请求你输入的 URL(如 http://119.91.133.45:8080/),服务端返回页面/default.html
  • 第二次请求​(端口 3361):
    浏览器 ​自动请求网站图标​ /favicon.ico,用于在标签页、书签栏显示小图标。若服务端未显式处理该请求,浏览器仍会尝试获取。
    在这里插入图片描述

🏳️‍🌈二 、整体代码

#pragma once#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <unordered_map>
#include "Log.hpp"using namespace LogModule;const static std::string _base_sep = "\r\n";     // static 关键字使变量具有内部链接,仅当前翻译单元(源文件)可见。
// const static std::string _base_sep = "\r\n";  // 默认具有外部链接,其他文件可通过 extern 引用。
const static std::string _line_sep = ": ";
const static std::string _prefix_path = "/wwwroot";          // 默认前缀路劲
const static std::string _default_path = "default.html";    // 默认路径namespace HttpServer{class HttpRequest{private:// 获取一行信息std::string GetLine(std::string& reqstr){auto pos = reqstr.find(_base_sep);if(pos == std::string::npos) return "";std::string line = reqstr.substr(0, pos);       // 截取一行有效信息reqstr.erase(0, pos + _base_sep.length());      // 删除有效信息和分隔符return line.empty() ? _base_sep : line;         // 有效信息为空则返回分隔符,否则返回有效信息}// 解析请求行void PraseReqLine(){    // 以空格为分隔符,不断读取std::stringstream ss(_req_line);ss >> _method >> _url >> _version;     _path += _url;// 处理url,如果是根目录,则返回默认路径if(_url == "/")_path = _default_path;}// 解析请求头void PraseHeader(){for(auto& header : _req_headers){auto pos = header.find(':');if(pos == std::string::npos)continue;std::string k = header.substr(0, pos);std::string v = header.substr(pos + _line_sep.size());if(k.empty() || v.empty()) continue;_headers_kv[k] = v;}}public:HttpRequest() : _blank_line(_base_sep), _path(_prefix_path) {}void Descrialize(std::string& reqstr){// 基本的反序列化_req_line = GetLine(reqstr);    // 读取第一行请求行// 请求报头std::string header;do{header = GetLine(reqstr);// 如果既不是空,也不是空行,就是请求报头,加入到请求报头列表中if(header.empty()) break;else if(header == _base_sep) break;_req_headers.push_back(header);}while(true);// 正文if(!reqstr.empty())_req_body = reqstr;// 进一步反序列化请求行PraseReqLine();// 分割请求报头,获取键值对PraseHeader(); }void Print(){std::cout << "----------------------------------------" <<std::endl;std::cout << "请求行: ###" << _req_line << std::endl;std::cout << "请求报头: " << std::endl;for(auto& header : _req_headers){std::cout << "@@@" << header << std::endl;}std::cout << "空行: " << _blank_line << std::endl;std::cout << "请求体: " << _req_body << std::endl;std::cout << "Method: " << _method << std::endl;std::cout << "Url: " << _url << std::endl;std::cout << "Version: " << _version << std::endl;}std::string Url(){LOG(LogLevel::INFO) << "client want url : " << _url;  return _url;}std::string Path(){LOG(LogLevel::INFO) << "client want path : " << _path;  return _path;}~HttpRequest() {}private:std::string _req_line;                      // 请求行std::vector<std::string> _req_headers;      // 请求报头std::string _blank_line;                    // 空行std::string _req_body;                      // 请求体std::string _method;                         // 请求方法std::string _path;   // 资源路径std::string _url;                            // 请求urlstd::string _version;                        // 请求版本std::unordered_map<std::string, std::string> _headers_kv; // 存储每行报文的哈希表};class HttpHandler{public:HttpHandler(){}std::string handle(std::string req){std::cout << "------------------------------------" << std::endl;std::cout << req;HttpRequest req_obj;req_obj.Descrialize(req);// req_obj.Print();std::string path = req_obj.Path();std::string url = req_obj.Url();std::string responsestr = "HTTP/1.1 200 OK\r\n";responsestr += "Content-Type: text/html\r\n";responsestr += "\r\n";responsestr += "<html><h1>hello linux,hello net!<h2></html>";return responsestr;}~HttpHandler(){}};
}

👥总结

本篇博文对 【Linux网络】构建与优化HTTP请求处理 - 从HttpRequest到HttpServer 做了一个较为详细的介绍,不知道对你有没有帮助呢

觉得博主写得还不错的三连支持下吧!会继续努力的~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/80615.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Day12(回溯法)——LeetCode51.N皇后39.组合总和

1 前言 今天刷了三道回溯法和一道每日推荐&#xff0c;三道回溯法也迷迷糊糊的&#xff0c;每日推荐把自己绕进去了&#xff0c;虽然是一道之前做过的题的变种。刷的脑子疼。。。今天挑两道回溯题写一下吧&#xff0c;其中有一道是之前做过的N皇后&#xff0c;今天在详细写一写…

初阶数据结构:二叉搜索树

目录 概念 性能 效率分析 二分缺陷 功能 插入 查找 删除 实现 应用 概念 二叉搜索树&#xff08;又称&#xff1a;二叉排序树&#xff09;&#xff0c;是由一些具有特别性质的二叉树衍变而来。 只要一棵二叉树具备以下性质&#xff0c;即可称作二叉搜索树。 【1】若…

详解springcloud gateway工作原理、断言、filter、uri、id、全局跨域、globalfilter等以及关键源码实现

1.gateway概念 网关就是当前微服务项目的"统一入口"程序中的网关就是当前微服务项目对外界开放的统一入口所有外界的请求都需要先经过网关才能访问到我们的程序提供了统一入口之后,方便对所有请求进行统一的检查和管理 2. 网关的主要功能 将所有请求统一经过网关网…

C#中的弱引用使用

弱引用&#xff08;Weak Reference&#xff09;是一种特殊的引用类型&#xff0c;它允许你引用一个对象&#xff0c;但不会阻止该对象被垃圾回收器&#xff08;GC&#xff09;回收。弱引用通常用于需要缓存或跟踪对象&#xff0c;但又不希望因保留引用而导致内存泄漏的场景。弱…

spring响应式编程系列:异步生产数据

目录 示例 大致流程 create new MonoCreate subscribe new LambdaMonoSubscriber monoCreate.subscribe accept success onNext 时序图 类图 数据发布者 MonoCreate 数据订阅者 LambdaMonoSubscriber 订阅的消息体 DefaultMonoSink 本篇文章我们来研究如何将…

MCP Python SDK构建的**SQLite浏览器**的完整操作指南

以下是使用MCP Python SDK构建的SQLite浏览器的完整操作指南&#xff1a; 一、环境准备 安装依赖 # 安装MCP SDK及SQLite支持 pip install mcp sqlite3创建测试数据库 sqlite3 test.db <<EOF CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT); IN…

【Python爬虫基础篇】--3.cookie和session

目录 1.cookie 1.1.定义 1.2.参数 1.3.分类 2.session 3.使用cookie登录微博 4.使用session登录 1.cookie 由于http是一个无状态的协议&#xff0c;请求与请求之间无法相互传递或者记录一些信息&#xff0c;cookie和session正是为了解决这个问题而产生。 例子&#xff1…

风车邮箱系统详细使用指南:Windows与Ubuntu双平台解析

风车邮箱系统V1.2使用手册 风车邮箱系统详细使用指南&#xff1a;Windows与Ubuntu双平台解析 前言 在日常网络活动中&#xff0c;我们经常需要一个临时邮箱来注册各类网站或接收验证码&#xff0c;但不想使用自己的真实邮箱。「风车无线邮箱系统」作为一款优秀的临时邮箱工具…

同样的接口用postman/apifox能跑通,用jmeter跑就报错500

之前没用过jmeter,第一次用调试压测脚本遇到了问题 一样的接口用postman能跑通&#xff0c;用jmeter跑就报错500&#xff0c;百度很多文章都说是该接口需要加一个‘内容编码’改成utf-8,我加了还是不行 后来我就想到apifox好像有隐藏的header&#xff0c;然后开始比较apifox的…

1656打印路径-Floyd回溯/图论-链表/数据结构

蓝桥账户中心 1.税收&#xff1a; “城市的税收”&#xff1a;所以是中介点的税收&#xff0c;经过该点后加上 2.路径&#xff1a; 用数组存储前驱节点从而串成链表 pre[ i ][ j ]代表的是从 i 到 j 的最短路径上 j 的前驱节点是什么 那么便可以pre[ i ][ j ]k 把k加入pa…

Eigen矩阵操作类 (Map, Block, 视图类)

1. Map 类&#xff1a;内存映射&#xff08;零拷贝操作&#xff09; 核心功能 将现有的 C/C 数组或缓冲区映射为 Eigen 矩阵/向量&#xff0c;不复制数据&#xff0c;直接操作原内存。 模板参数 cpp Map<Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>&…

多系统安装经验,移动硬盘,ubuntu grub修改/etc/fstab 移动硬盘需要改成nfts格式才能放steam游戏

总结&#xff1a;我硬盘会自动挂载&#xff0c;直接格式化nfts&#xff0c;steam就能装里面了 机械硬盘装系统真的不行&#xff0c;超级慢游戏还跑不了 --------------------------------------------------------------------底下都不用看 笔记本一个系统&#xff0c;移动硬盘…

JFLAP SOFTWARE 编译原理用(自动机绘图)

csdn全是蛆虫&#xff0c;2mb的软件&#xff0c;都在那里搞收费&#xff0c;我就看不惯&#xff0c;我就放出来&#xff0c;那咋了&#xff01;&#xff01;&#xff01; https://pan.baidu.com/s/1IuEfHScynjCCUF5ScF26KA 通过网盘分享的文件&#xff1a;JFLAP7.1.jar 链接: h…

[Windows] Disk Sorter文件分类管理软件 v16.7.18

[Windows] Disk Sorter文件分类管理 链接&#xff1a;https://pan.xunlei.com/s/VOOl0sDntAdHvlMkc7N0ZOD-A1?pwd966n# Disk Sorter是一个功能强大的文件分类管理软件&#xff0c;允许对本地磁盘、网络共享、NAS设备和企业存储系统中的文件进行分类&#xff0c;并且支持生成…

STM32提高篇: 蓝牙通讯

STM32提高篇: 蓝牙通讯 一.蓝牙通讯介绍1.蓝牙技术类型 二.蓝牙协议栈1.蓝牙芯片架构2.BLE低功耗蓝牙协议栈框架 三.ESP32-C3中的蓝牙功能1.广播2.扫描3.通讯 四.发送和接收 一.蓝牙通讯介绍 蓝牙&#xff0c;是一种利用低功率无线电&#xff0c;支持设备短距离通信的无线电技…

6.1.多级缓存架构

目录 一、多级缓存基础与核心概念 缓存的定义与价值 • 缓存的应用场景&#xff08;高并发、低延迟、减轻数据库压力&#xff09; • 多级缓存 vs 单级缓存的优劣对比 多级缓存核心组件 • 本地缓存&#xff08;Caffeine、Guava Cache&#xff09; • 分布式缓存&#xff08;…

MySQL的MVCC【学习笔记】

MVCC 事务的隔离级别分为四种&#xff0c;其中Read Committed和Repeatable Read隔离级别&#xff0c;部分实现就是通过MVCC&#xff08;Multi-Version Concurrency Control&#xff0c;多版本并发控制&#xff09; 版本链 版本链是通过undo日志实现的&#xff0c; 事务每次修改…

基于OpenMV+STM32+OLED与YOLOv11+PaddleOCR的嵌入式车牌识别系统开发笔记

基于OpenMV、STM32与OLED的嵌入式车牌识别系统开发笔记 基于OpenMV、STM32与OLED的嵌入式车牌识别系统开发笔记系统架构全景 一、实物演示二、OpenMV端设计要点1. 硬件配置优化2. 智能帧率控制算法3. 数据传输协议设计 三、PyTorch后端核心实现&#xff1a;YOLOv11与PaddleOCR的…

C#中常见的设计模式

文章目录 引言设计模式的分类创建型模式 (Creational Patterns)1. 单例模式 (Singleton)2. 工厂方法模式 (Factory Method)3. 抽象工厂模式 (Abstract Factory)4. 建造者模式 (Builder) 结构型模式 (Structural Patterns)5. 适配器模式 (Adapter)6. 装饰器模式 (Decorator)7. 外…

Nacos简介—3.Nacos的配置简介

大纲 1.Nacos生产集群Web端口与数据库配置 2.Nacos生产集群的Distro协议核心参数 3.Nacos打通CMDB实现跨机房的就近访问 4.Nacos基于SPI动态扩展机制来获取CMDB的数据 5.基于Nacos SPI机制开发CMDB动态扩展 6.Nacos基于CMDB来实现多机房就近访问 7.Nacos生产集群Prometh…