【Linux网络编程】第十二弹---构建与优化HTTP请求处理:从HttpRequest到HttpServer的实战

✨个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】【Linux网络编程】

目录

1、HttpRequest类

1.1、基本结构

1.2、构造析构函数

1.3、反序列化函数

1.4、GetLine() 

1.5、打印函数

2、HttpServer类

2.1、HandlerHttpRequest()

3、测试 

3.1、测试结果(基本版本) 

3.2、代码优化一

3.3、测试结果(优化一) 

3.4、代码优化二

3.5、测试结果(优化二) 

4、增加路径字段

4.1、HttpRequest类 

4.2、解析请求行

4.3、获取url 和 path

4.4、HandlerHttpRequest()


1、HttpRequest类

1.1、基本结构

HttpRequest类基本结构包括请求行,请求报头,正文

class HttpRequest
{
private:// \r\n// \r\ndata// 获取一行信息std::string GetLine(std::string &reqstr);// 解析请求行void ParseReqLine();// 解析请求报头,以冒号加空格分隔void ParseReqHeader();
public:HttpRequest();// 反序列化void Deserialize(std::string &reqstr) ;// 打印请求信息void Print();~HttpRequest();
private:// 基本的httprequest的格式std::string _req_line;                 // 请求行std::vector<std::string> _req_headers; // 请求报头std::string _blank_line;               // 空行std::string _body_text;                // 正文
};

1.2、构造析构函数

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

const static std::string base_sep = "\r\n";HttpRequest() : _blank_line(base_sep)
{}~HttpRequest()
{}

1.3、反序列化函数

反序列化即将字符串转化成结构化字段

// 反序列化
void Deserialize(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()){_body_text = reqstr;}
}

1.4、GetLine() 

获取一行有效信息没找到分隔符返回空串,找到分隔符但是没有有效信息则返回分隔符

// \r\n
// \r\ndata
// 获取一行信息
std::string GetLine(std::string &reqstr)
{auto pos = reqstr.find(base_sep);if (pos == std::string::npos) // 没找到分隔符返回空return std::string();std::string line = reqstr.substr(0, pos);       // 截取一行有效信息reqstr.erase(0, line.size() + base_sep.size()); // 删除有效信息和分隔符return line.empty() ? base_sep : line;          // 有效信息为空则返回分隔符,不为空返回有效信息
}

1.5、打印函数

打印反序列化出来的字符串

// 打印请求信息
void Print()
{std::cout << "---------------------------" << std::endl;std::cout << "###" << _req_line << std::endl;for (auto &header : _req_headers){std::cout << "@@@" << header << std::endl;}std::cout << "***" << _blank_line;std::cout << ">>>" << _body_text << std::endl;
}

2、HttpServer类

2.1、HandlerHttpRequest()

std::string HandlerHttpRequest(std::string &reqstr)
{
#ifdef TESTstd::cout << "---------------------------------------------" << std::endl;std::cout << reqstr;// return std::string();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;
#elseHttpRequest req;         // 构建请求对象req.Deserialize(reqstr); // 反序列化字符串req.Print();             // 打印反序列的字符串return std::string();    // 保证编译通过
#endif
}

3、测试 

主函数

// ./httpserver 8888
int main(int argc, char *argv[])
{if (argc != 2){std::cerr << "Usage: " << argv[0] << "local-port" << std::endl;exit(0);}uint16_t port = std::stoi(argv[1]);HttpServer hserver;std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(std::bind(&HttpServer::HandlerHttpRequest, &hserver, std::placeholders::_1),port);tsvr->Loop();return 0;
}

3.1、测试结果(基本版本) 

注意:执行的方法都是HandlerHttpRequest(),且代码执行的是#else后的代码!

运行结果 

上面是基本的序列化,我们还可以进一步序列化,将请求行的成员都反序列化,因此请求类需要增加成员变量

3.2、代码优化一

HttpRequest类成员变量

HttpRequest类需要增加请求方法,统一资源定位符,版本三个成员变量

class HttpRequest
{
private:// 基本的httprequest的格式std::string _req_line;                 // 请求行std::vector<std::string> _req_headers; // 请求报头std::string _blank_line;               // 空行std::string _body_text;                // 正文// 更具体的属性字段,需要进一步反序列化std::string _method;  // 请求方法std::string _url;     // 统一资源定位符std::string _version; // 版本
};

反序列化函数

反序列化函数需要增加解析请求行函数

// 反序列化
void Deserialize(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()){_body_text = reqstr;}// 再进一步反序列化ParseReqLine();   // 解析请求行
}

解析请求行函数

请求行的成员之间使用空格分隔,因此可以使用字符串流对象,直接进行流提取

// 解析请求行
void ParseReqLine()
{std::stringstream ss(_req_line);   // 以空格为分隔符 cin >>ss >> _method >> _url >> _version; // 以空格为分隔符依次将请求行的内容赋值给成员变量
}

打印请求信息

// 打印请求信息
void Print()
{std::cout << "---------------------------" << std::endl;std::cout << "###" << _req_line << std::endl;for (auto &header : _req_headers){std::cout << "@@@" << header << std::endl;}std::cout << "***" << _blank_line;std::cout << ">>>" << _body_text << std::endl;std::cout << "Method: " << _method << std::endl;std::cout << "Url: " << _url << std::endl;std::cout << "Version: " << _version << std::endl;
}

3.3、测试结果(优化一) 

浏览器输入 42.193.244.117:8888

注意:IP是自己服务器的公网IP,并且需要启动可执行程序! 

运行结果

浏览器输入 42.193.244.117:8888/a/b/c/d/e/f/html

运行结果

上面将请求行的成员都反序列化我们还可以进一步序列化,将请求报头进行反序列化,照样需要增加成员变量,此处我们使用哈希表存储,因为每行报文都是以冒号+空格分隔的值

3.4、代码优化二

HttpRequest类成员变量 

HttpRequest类增加一个KV形式的哈希表即可!

class HttpRequest
{
private:// 基本的httprequest的格式std::string _req_line;                 // 请求行std::vector<std::string> _req_headers; // 请求报头std::string _blank_line;               // 空行std::string _body_text;                // 正文// 更具体的属性字段,需要进一步反序列化std::string _method;  // 请求方法std::string _url;     // 统一资源定位符std::string _version; // 版本std::unordered_map<std::string, std::string> _headers_kv; // 存储每行报文的哈希表
};

反序列化函数 

该反序列函数需要加前面的基础上继续解析请求报头

// 反序列化
void Deserialize(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()){_body_text = reqstr;}// 再进一步反序列化ParseReqLine();   // 解析请求行ParseReqHeader(); // 解析请求报头
}

解析请求报头函数

遍历请求报头成员变量,以行分隔符查找有效信息找到且分隔符前的有效信息不为空且分隔符后的有效信息不为空,则将KV值插入到哈希表中

const static std::string line_sep = ": "; // 行分隔符// 解析请求报头,以冒号加空格分隔
void ParseReqHeader()
{for (auto &header : _req_headers){auto pos = header.find(line_sep);if (pos == std::string::npos)continue;std::string k = header.substr(0, pos);                // 截取key值std::string v = header.substr(pos + line_sep.size()); // 截取value值if (k.empty() || v.empty())continue;_headers_kv.insert(std::make_pair(k, v)); // 将对应的kv值插入到哈希表中}
}

打印请求信息函数

// 打印请求信息
void Print()
{std::cout << "---------------------------" << std::endl;std::cout << "###" << _req_line << std::endl;for (auto &header : _req_headers){std::cout << "@@@" << header << std::endl;}std::cout << "***" << _blank_line;std::cout << ">>>" << _body_text << std::endl;std::cout << "Method: " << _method << std::endl;std::cout << "Url: " << _url << std::endl;std::cout << "Version: " << _version << std::endl;for (auto head_kv : _headers_kv){std::cout << ")))" << head_kv.first << "->"<< head_kv.second << std::endl;}
}

3.5、测试结果(优化二) 

注意:执行的方法都是HandlerHttpRequest(),且代码执行的是#else后的代码!

运行结果 

4、增加路径字段

我们向服务器请求的时候,需要知道资源的路径,因此我们可以增加路径字段(根目录为wwwroot/),当url为 / 时,默认访问wwwroot/index.html

4.1、HttpRequest类 

HttpRequest类增加路径成员,并将路径初始化为web根目录

const static std::string prefixpath = "wwwroot"; // web根目录class HttpRequest
{
public:HttpRequest() : _blank_line(base_sep), _path(prefixpath){}
private:// 基本的httprequest的格式std::string _req_line;                 // 请求行std::vector<std::string> _req_headers; // 请求报头std::string _blank_line;               // 空行std::string _body_text;                // 正文// 更具体的属性字段,需要进一步反序列化std::string _method; // 请求方法std::string _url;    // 统一资源定位符std::string _path;   // 资源路径std::string _version;                                     // 版本std::unordered_map<std::string, std::string> _headers_kv; // 存储每行报文的哈希表
};

4.2、解析请求行

解析请求行除了解析url之外还需要解析path,path 默认直接使用path + url即可,但是当url 为 / 时,path 还需要加上默认访问文件 index.html

// 解析请求行
void ParseReqLine()
{std::stringstream ss(_req_line);   // 以空格为分隔符 cin >>ss >> _method >> _url >> _version; // 以空格为分隔符依次将请求行的内容赋值给成员变量_path += _url;// 只有web根目录返回index.htmlif (_path[_path.size() - 1] == '/'){_path += homepage;}
}

4.3、获取url 和 path

获取url 和 path 直接返回成员变量即可!

std::string Url()
{LOG(DEBUG, "Client Want url %s\n", _url.c_str());return _url;
}
std::string Path()
{LOG(DEBUG, "Client Want path %s\n", _path.c_str());return _path;
}

4.4、HandlerHttpRequest()

std::string HandlerHttpRequest(std::string &reqstr)
{HttpRequest req;         // 构建请求对象req.Deserialize(reqstr); // 反序列化字符串std::string url = req.Url();std::string path = req.Path();return std::string();    // 保证编译通过
}

运行结果 

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

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

相关文章

使用k6进行kafka负载测试

1.安装环境 kafka环境 参考Docker搭建kafka环境-CSDN博客 xk6-kafka环境 ./xk6 build --with github.com/mostafa/xk6-kafkalatest 查看安装情况 2.编写脚本 test_kafka.js // Either import the module object import * as kafka from "k6/x/kafka";// Or in…

Linux内存管理 --- 进程创建虚拟地址的过程

文章目录 前言一、进程虚拟地址空间二、进程号1的创建过程2.1 kernel_init2.2 kernel_execve2.2.1 alloc_bprm2.2.2 bprm_stack_limits2.2.3 copy_string_kernel2.2.4 bprm_execve 2.3 bprm_execve2.3.1 prepare_binprm2.3.2 load_binary2.3.3 interpreter 三、load_elf_binary…

uniapp blob格式转换为video .mp4文件使用ffmpeg工具

前言 介绍一下这三种对象使用场景 您前端一旦涉及到文件或图片上传Q到服务器&#xff0c;就势必离不了 Blob/File /base64 三种主流的类型它们之间 互转 也成了常态 Blob - FileBlob -Base64Base64 - BlobFile-Base64Base64 _ File uniapp 上传文件 现在已获取到了blob格式的…

springboot447教师薪酬管理系统(论文+源码)_kaic

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;老师信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广…

三、ubuntu18.04安装docker

1.使用默认ubuntu存储库安装docker 更新软件存储库 更新本地软件数据库确保可以访问最新版本。打开终端输入&#xff1a;sudo apt-get update 卸载旧版本的docker 建议继续之前卸载任何旧的docker软件。打开终端输入&#xff1a;sudo apt-get remove docker docker-engine …

Java JDK8之前传统的日期时间-Date、SimpleDateFormat、Calendar

1. Date (1) Date代表的是日期和时间 (2) 常见构造器和常用方法 构造器说明public Date()创建一个Date对象&#xff0c;代表系统当前日期和时间public Date(long time)根据传入的时间毫秒值创建一个Date对象 方法说明public long getTime()返回从1970.1.1 00:00:00到此时的毫…

powershell(1)

免责声明 学习视频来自 B 站up主泷羽sec&#xff0c;如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识&#xff0c;以下代码、网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0c;否则后果自负。 泷羽sec官网&#xff1a;http…

matlab绘图时设置左、右坐标轴为不同颜色

目录 一、需求描述 二、实现方法 一、需求描述 当图中存在两条曲线&#xff0c;需要对两条曲线进行分别描述时&#xff0c;应设置左、右坐标轴为不同颜色&#xff0c;并设置刻度线&#xff0c;且坐标轴颜色需要和曲线颜色相同。 二、实现方法 1.1、可以实现&#xff1a; 1…

初学stm32 --- 窗口看门狗

STM32F1 窗口看门狗 窗口看门狗&#xff08;WWDG&#xff09;通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。除非递减计数器的值在 T6 位&#xff08;WWDG->CR 的第六位&#xff09;变成 0 前被刷新&#xff0c;看门狗电…

Kafka Streams 在监控场景的应用与实践

作者&#xff1a;来自 vivo 互联网服务器团队- Pang Haiyun 介绍 Kafka Streams 的原理架构&#xff0c;常见配置以及在监控场景的应用。 一、背景 在当今大数据时代&#xff0c;实时数据处理变得越来越重要&#xff0c;而监控数据的实时性和可靠性是监控能力建设最重要的一环…

论文笔记:是什么让多模态学习变得困难?

整理了What Makes Training Multi-modal Classification Networks Hard? 论文的阅读笔记 背景方法OGR基于最小化OGR的多监督信号混合在实践中的应用 实验 背景 直观上&#xff0c;多模态网络接收更多的信息&#xff0c;因此它应该匹配或优于其单峰网络。然而&#xff0c;最好的…

【鸿蒙实战开发】HarmonyOS状态管理之@Link

前言 在前面两篇状态管理相关的文章中&#xff0c;我们分别讲解了 State 和 Prop 两个状态管理装饰器的作用和基本使用。State 状态管理装饰器是最基本的状态管理装饰器&#xff0c;组件使用其修饰的变量&#xff0c;组件的更新可以随着变量的变化而更新&#xff1b;Prop 状态…

Nginx常用配置详解(1)

Nginx常用配置详解 一、全局块&#xff08;main&#xff09;配置 在Nginx的配置文件中&#xff0c;最外层的部分是全局块。这部分配置通常会影响Nginx服务器整体的运行参数。 worker_processes 作用&#xff1a;这个配置指令用于指定Nginx工作进程的数量。工作进程是Nginx处理…

NVIDIA发布紧凑型生成式AI超级计算机:性能提升,价格更低

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

jmeter连接mysql

查询mysql数据库版本 SELECT VERSION(); 下载jmeter mysql 驱动jar包&#xff0c;版本低于mysql版本&#xff0c;放在jmeter的lib 路径下 MySQL :: Download MySQL Connector/J (Archived Versions) 添加JDBC Connection Configuration 填写 variable name 及数据库信息 注意…

STM32二刷学习笔记--GPIO

文章目录 GPIO使用详解GPIO基本结构GPIO工作模式简单示例推挽输出LED闪烁按键控制LED闪烁**LED控制函数****按键控制函数** GPIO使用详解 在STM32开发中&#xff0c;GPIO&#xff08;通用输入输出&#xff09;是与外设接口的基础模块。通过GPIO&#xff0c;我们可以连接各种外…

PCDN之网心云

PCDN之网心云 前言 利用闲置宽带赚取收益 CDN原理 使用你的设备缓存加速资源 当别人需要访问资源时会就近分配访问到你缓存资源实现边缘加速的效果 推荐宽带上行 >10MB 磁盘大于60G 否则收益可能不是很高 注册网心云账号 打开如下网址注册 务必填写邀请码 否则会少几块…

FPGA-PS端编程1:

目标 在小梅哥的zynq 7015上&#xff0c;完成以下目标&#xff1a; 读取 S1 按键的电平&#xff0c; 当 S1 按键为按下状态时&#xff0c;驱动 PS LED 以 1S 的频率闪烁(注意理解 1S 的频率闪烁和 1S的时间翻转两种描述之间的差别)&#xff0c; 当 S1 释放后&#xff0c;停止…

ArcGIS计算土地转移矩阵

在计算土地转移矩阵时&#xff0c;最常使用的方法就是在ArcGIS中将土地利用栅格数据转为矢量&#xff0c;然后采用叠加分析计算&#xff0c;但这种方法计算效率低。还有一种方法是采用ArcGIS中的栅格计算器&#xff0c;将一个年份的地类编号乘以个100或是1000再加上另一个年份的…

51c大模型~合集91

我自己的原文哦~ https://blog.51cto.com/whaosoft/12848734 #工业界主流大语言模型后训练(Post-Training)技术总结 本文整理工业界主流开源LLM的后训练方案&#xff0c;着重介绍训练算法和数据处理部分 今年工业界陆续开源了多款优秀的大语言模型&#xff0c;并放出了技术…