LINUX网络编程:http

ops/2024/9/19 18:56:14/ 标签: http, 网络协议, 网络

目录

http%E8%AF%B7%E6%B1%82%E7%9A%84%E5%AD%97%E6%AE%B5-toc" style="margin-left:0px;">1.认识http请求的字段

2.HTTP请求类

3.认识HTTP应答字段

4.HTTP应答类

5.源代码


协议就是一种约定,http也并不例外,使用http也无非就是,定义一个http请求的结构体,将结构体序列化为字符串,发送给服务器,服务器接收字符串,将字符串反序列化为结构化的数据,处理这些数据,将结果返回给客户端。

http%E8%AF%B7%E6%B1%82%E7%9A%84%E5%AD%97%E6%AE%B5">1.认识http请求的字段

这就是一个完整的http请求,这请求是由浏览器发送。

https://i-blog.csdnimg.cn/direct/d351f99d40f44564bcd1f79b3455f076.png" width="1200" />

https://i-blog.csdnimg.cn/direct/eeaa52f7ea1841879b60e158efbc7425.png" width="1105" />

两张图对照着看

请求方法:GET

URI:/ 是请求服务器的根目录(请求服务器的哪一个目录或者服务)

HTTP版本:HTTP/1.1 

Host,Connettion,User_Agent等等都是浏览器添加的请求报头。

因为有一个空行并且请求正文是空的,所以报头后面有两个空行。

2.HTTP请求类

因为请求是浏览器发送的,所以http请求类是不需要反序列化的,将http发送给我的请求序列化即可。

http请求发过来就是一个长字符串,我们要做的就是将字符串一个一个的解析出来。

1.第一步以"\r\n"为分隔符,将每一行拆分出来。

2.读到的第一行一定是状态行,将状态行的每个字段都拆出来。

如果请求方法是GET还有判断URI中是否有参数,因为GET方法提交表单的时候,参数是在URI中的,参数通常以?分割。

就像这样49.233.244.186:8888/login?user=41234&password=1234123

49.233.244.186:8888/login?user=41234&password=1234123将参数分离之后还需要判断URI请求的服务或者目录是否存在,如果URI访问的目录或者服务不存在返回404页面。

3.读到空行之前,读到的一定是请求报头,以": "为分隔符,将报头的key和value分离出来,并存储到unordered_map中。

4.读到空行之后说明,从此刻开始,剩下的只有正文部分了,直接保存起来即可。

3.认识HTTP应答字段

https://i-blog.csdnimg.cn/direct/0b2ae1105b22401fa46665d3fd74520a.png" width="1200" />

HTTP状态码:对请求的应答结果,可以去查一下http状态码表。

响应正文:例:可以返回一个网页,或图片,浏览器就会对你返回的内容做解析和渲染。

响应报头:这也是有一张表的,可以去查看一下,如果响应正文不是空的,响应报头就必须添加Content-Lenth,Content-Type,这样浏览器才知道你返回的内容到底是什么。

4.HTTP应答类

判断http请求是资源还是服务

1.如果http请求的是一个服务器的资源,要以二进制的方式将服务器的资源保存到字符串中,因为资源可能是图片,可能是视频。

判断读到的内容是否为空,如果为空说明,http请求了一个不存在的资源,这时候需要将状态码设置为404,构建应答,返回404页面。

不为空就添加对应的状态码,

添加响应头"Content-Type"和Content-Length,

添加响应正文

将应答返回

2.如果http请求是一个服务,需要将这个服务回调出去,将服务于http协议解耦。

5.源代码

#pragma once
#include <iostream>
#include "log.hpp"
#include <vector>
#include <string>
#include <unordered_map>
#include <fstream>
#include <sstream>
static const std::string sep = "\r\n";// http分隔符
static const std::string blank = " "; //空格
static const std::string headerSep = ": ";//请求头响应头分隔符
static const std::string webroot = "./webroot";//服务器的根目录
static const std::string httpVersion = "http/1.0";//http版本
static const std::string suffixSep = ".";//文件类型分隔符
static const std::string defaultpage = "index.html";//主页
static const std::string argsep = "?";//get提交表单分隔符class httpReq;
class httpRep;
//using fun_t = std::function<std::shared_ptr<httpRep> (std::shared_ptr<httpReq>&)>;using fun_t = std::function<std::shared_ptr<httpRep>(std::shared_ptr<httpReq>)>;
class httpReq
{
private:std::string getOneLine(std::string &reqstr) // 获取请求的一行{if (reqstr.empty()) // 判断请求是否为空{Log(Error, "reqstr empty");return std::string();}auto pos = reqstr.find(sep);if (pos == std::string::npos) // 判断是否找到{Log(Error, "reqstr not found sep");return std::string();}std::string line = reqstr.substr(0, pos);reqstr.erase(0, pos + sep.size());return line.empty() ? sep : line; // 如line为空说明读到空行}public:httpReq(const std::string root = webroot, const std::string blanksep = sep): _root(root), _blankLine(blanksep){}void serialize(){}bool parseLine() // 解析请求行{if (_requestLine.empty()){return false;}// 解析请求行std::stringstream ss(_requestLine); // 1以空格为分隔符自动解析字符串ss >> _reqMethod >> _url >> _httpVersion;_path += webroot;// 解析参数if (strcasecmp(_reqMethod.c_str(), "get") == 0){//Log(Debug, "before url %s", _url.c_str());auto pos = _url.find(argsep);if (pos != std::string::npos) // 有参数{   _args = _url.substr(pos + argsep.size());//Log(Info, "args: %s", _args);_url.resize(pos);}//Log(Debug, "after url %s", _url.c_str());}_path += _url;if (_path[_path.size() - 1] == '/')//如果请求的是根目录,跳转到主页{_path += defaultpage;}for (auto s : _requestHeader){auto pos = s.find(headerSep);std::string k = s.substr(0, pos);std::string v = s.substr(pos + headerSep.size());_kv.insert(std::make_pair(k, v));}return true;}bool Deserialize(std::string &reqstr) // 反序列化{_requestLine = getOneLine(reqstr);while (true){std::string line = getOneLine(reqstr);if (line.empty()) // getOneline 失败{break;}else if (line == sep) // 读到空行{_text = reqstr;break;}else // 读到请求头{_requestHeader.push_back(line);}}return parseLine();}bool isServiceReq() //是否为路径服务请求{return !_text.empty() || !_args.empty();}const std::string &path(){return _path;}const std::string &method(){return _reqMethod;}const std::string &text(){return _text;}const std::string &args(){return _args;}void pprint(){std::cout << "###" << _reqMethod << std::endl;std::cout << "###" << _url << std::endl;std::cout << "###" << _path << std::endl;std::cout << "###" << _httpVersion << std::endl;for (auto it : _kv){std::cout << "@@@" << it.first << ": " << it.second << endl;}std::cout << "$$$" << _text << std::endl;}std::string getSuffix()//获取返回资源的类型{if (_path.empty()){return std::string();}auto pos = _path.rfind(suffixSep);if (pos == std::string::npos){return ".html";}else{return _path.substr(pos);}}private:std::string _requestLine;                // http请求第一行std::vector<std::string> _requestHeader; // http请求头std::string _blankLine;                  // 空行std::string _text;                       // http正文// 解析出每行具体内容std::string _root;                                // web的根目录std::string _reqMethod;                           // 请求方法std::string _url;                                 // urlstd::string _httpVersion;                         // http版本std::string _path;                                // 访问资源的路径std::string _args;                                // 请求的参数std::unordered_map<std::string, std::string> _kv; // 请求头的kv模型
};class httpRep
{
public:httpRep(const std::string version = httpVersion, const std::string b = sep): _httpVersion(version), _blankLine(b){}void addStatusLine(const int code, const std::string &descrip)//添加状态行{_statuCode = code;_codeDescripetion = descrip;}void addHander(const std::string k, const std::string v)//添加响应头{_kv.insert(std::make_pair(k, v));}void addText(std::string &text)//添加响应文{_text = text;}std::string Serialize(){//序列化状态行_statusLine = _httpVersion + blank + std::to_string(_statuCode) + blank + _codeDescripetion + sep;//序列化响应头for (auto it : _kv){_respondHeader += it.first;_respondHeader += headerSep;_respondHeader += it.second;_respondHeader += sep;}//构建应答std::string respond;respond += _statusLine;respond += _respondHeader;respond += sep;respond += _text;// std::cout << respond << std::endl;// Log(Info, "%s", _statusLine.c_str());// Log(Info, "%s", _respondHeader.c_str());return respond;}void pprint(){std::cout << "###" << _httpVersion << std::endl;std::cout << "###" << _statuCode << std::endl;std::cout << "###" << _codeDescripetion << std::endl;std::cout << "###" << _text << std::endl;}private:// 构建响应必要的字段std::string _httpVersion;                         // http版本int _statuCode;                                   // 状态码std::string _codeDescripetion;                    // 状态码描述std::unordered_map<std::string, std::string> _kv; // 响应报头的kv模型// 构建响应的必要行std::string _statusLine;    // 状态行std::string _respondHeader; // 请求头std::string _blankLine;     // 空行std::string _text;          // 正文
};class Factor
{
public:static std::shared_ptr<httpReq> BuildHttprequest() //使用智能指针构建应答{return std::make_shared<httpReq>();}static std::shared_ptr<httpRep> BuildHttprepond(){return std::make_shared<httpRep>();}
};class httpserver
{
public:httpserver(){_mime.insert(std::make_pair(".html", "text/html"));_mime.insert(std::make_pair(".jpg", "image/jpeg"));_mime.insert(std::make_pair(".png", "application/x-plt"));_Statuscode_Descripetion.insert(std::make_pair(100, "continue"));_Statuscode_Descripetion.insert(std::make_pair(200, "ok"));_Statuscode_Descripetion.insert(std::make_pair(301, "Moved Permanently")); // 永久重定向_Statuscode_Descripetion.insert(std::make_pair(302, "Found"));             // 临时重定向_Statuscode_Descripetion.insert(std::make_pair(400, "Bad Request"));_Statuscode_Descripetion.insert(std::make_pair(404, "Not Found"));_Statuscode_Descripetion.insert(std::make_pair(404, "Not Found"));}std::string readFileContent(const std::string &path, int &filesize){// std::cout<< path <<std::endl;std::ifstream in(path, std::ios::binary);if (!in.is_open()){return std::string();}in.seekg(0, in.end);filesize = in.tellg();in.seekg(0, in.beg);// std::cout << filesize << std::endl;if (filesize < 0){filesize = 0;}std::string content;content.resize(filesize);in.read((char *)content.c_str(), filesize);in.close();return content;}void addhander(std::string path, fun_t handler){std::string tmp = webroot + path;_funcs.insert(std::make_pair(tmp, handler));}#define VERSION_1std::string httpHandler(std::string req){
#ifdef VERSION_1std::cout << req << std::endl;auto request = Factor::BuildHttprequest();request->Deserialize(req);//Log(Info, "method %s", request->method().c_str());// Log(Info,"s","redir ----------------------");//  if(request->path() == "./webroot/redir")//进行重定向//  {//      code = 302;//      respond->addHander("Location", "https://www.csdn.net/?spm=1011.2266.3001.4476");//      respond->addStatusLine(code, _Statuscode_Descripetion[code]);// }if (request->isServiceReq())//当前请求的是一个服务{Log(Info, "method %s", request->method().c_str());Log(Info, "method %s", request->path().c_str());auto response = _funcs[request->path()](request);return response->Serialize();}else // 当前请求的是一个服务器资源{int code = 200;int filesize = 0;auto respond = Factor::BuildHttprepond();std::string content = readFileContent(request->path(), filesize);if (content.empty())//没有读到任何内容,说明请求不存在{code = 404;respond->addStatusLine(code, _Statuscode_Descripetion[code]);respond->addHander("Content-Type", ".html");std::string content404 = readFileContent("./webroot/404.html", filesize);respond->addText(content404);}else//请求存在{respond->addStatusLine(code, _Statuscode_Descripetion[code]);/std::string suffix = request->getSuffix();//获取资源的后坠respond->addHander("Content-Type", _mime[suffix]);respond->addHander("Content-Length", std::to_string(filesize));respond->addText(content);}return respond->Serialize();}#elsestd::cout << "version control" << endl;
#endif}private:std::unordered_map<std::string, std::string> _mime;            // 文件后缀,对应的content type类型std::unordered_map<int, std::string> _Statuscode_Descripetion; // 状态码 对应的描述std::unordered_map<std::string, fun_t> _funcs;                  // 将请求回调出去
};


http://www.ppmy.cn/ops/112114.html

相关文章

SpringBoot2:web开发常用功能实现及原理解析-上传与下载

文章目录 一、上传文件1、前端上传文件给Java接口2、Java接口上传文件给Java接口 二、下载文件1、前端调用Java接口下载文件2、Java接口下载网络文件到本地3、前端调用Java接口下载网络文件 一、上传文件 1、前端上传文件给Java接口 Controller接口 此接口支持上传单个文件和…

【Kubernetes】常见面试题汇总(二十)

目录 62.简述 Kubernetes Pod 如何实现对节点的资源控制&#xff1f; 63.简述 Kubernetes Requests 和 Limits 如何影响 Pod 的调度&#xff1f; 64.简述 Kubernetes Metric Service &#xff1f; 62.简述 Kubernetes Pod 如何实现对节点的资源控制&#xff1f; - Kubernete…

自定义spring security的安全表达式

在Spring Security中&#xff0c;DefaultMethodSecurityExpressionHandler是处理方法安全表达式的默认处理器。如果你想注册自定义的安全表达式方法&#xff0c;你需要创建一个自定义的表达式处理器&#xff0c;继承自DefaultMethodSecurityExpressionHandler&#xff0c;并重写…

数组学习内容

动态初始化 只给长度&#xff0c;数据类型【】 数组名new 数据类型【数组长度】 内存图

TensorFlow 2.0 快速入门——手把手学会训练模型

目录 文章灵感 快速入门 代码解释 快速入门代码 加载数据集 构建机器学习模型 损失函数和编译模型 训练并评估模型 修改模型以返回概率 运行效果解释 我的修改 保存我们训练好的模型 代码解释 1. 加载 MNIST 数据集 2. 数据预处理&#xff08;归一化&am…

matlab fid = fopen(file_nav,‘rt‘);语句解释

fopen 函数 fopen 是一个 MATLAB 函数&#xff0c;用于打开文件并返回一个文件标识符&#xff08;file identifier&#xff09;&#xff0c;该标识符用于后续的文件操作。 参数 file_nav file_nav 是一个变量&#xff0c;包含要打开的文件的名称或路径。它通常是一个字符串&…

微信小程序页面制作——婚礼邀请函(含代码)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

第十章 【后端】环境准备(10.9)——Navicat

10.9 Navicat Navicat Premium 官网 下载 下载地址:https://www.navicat.com.cn/download/navicat-premium-lite 安装 一路“下一步”即可。 连接 `MySql’

SOMEIP_ETS_113: SD_Empty_Options_Array

测试目的&#xff1a; 验证DUT能够拒绝一个选项数组长度为0的SubscribeEventgroup消息&#xff0c;并以SubscribeEventgroupNAck作为响应。 描述 本测试用例旨在确保DUT遵循SOME/IP协议&#xff0c;当接收到一个选项数组长度为0的SubscribeEventgroup消息时&#xff0c;能够…

Google推出Data Commons解决AI“幻觉”

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

MATLAB系列06:复数数据、字符数据和附加画图类

MATLAB系列06&#xff1a;复数数据、字符数据和附加画图类 6. 复数数据、字符数据和附加画图类6.1 复数数据6.1.1 复变量&#xff08; complex variables&#xff09;6.1.2 带有关系运算符的复数的应用6.1.3 复函数&#xff08; complex function&#xff09;6.1.4 复数数据的作…

您的计算机已被.lcrypt勒索病毒感染?恢复您的数据的方法在这里!

导言 在网络安全领域&#xff0c;勒索病毒已经成为一种威胁极大的恶意软件&#xff0c;其中.lcrypt勒索病毒&#xff08;.lcrypt ransomware&#xff09;是最近出现的一种新的变种。它以加密用户数据并要求赎金为手段&#xff0c;严重影响个人和组织的日常运营。本文91数据恢复…

Django学习实战篇四(适合略有基础的新手小白学习)(从0开发项目)

前言&#xff1a; 在本章中&#xff0c;我们开始编写面向用户的界面&#xff0c;其中只涉及简单的HTML结构&#xff0c;不会做太多美化&#xff0c;目的就是把后台创建的数据展示到前台。 从技术上来讲&#xff0c;这一节将涉及Django 中function view和 class-based view 的用…

堆排序,快速排序

目录 1.堆排序 2.快速排序 1.hoare版本 2.挖坑法 3.前后指针法 注意点 1.堆排序 void Swap(int* a, int* b) {int tmp *a;*a *b;*b tmp; } void adjustdown(int* a, int n, int parent) {int child parent * 2 1;while (child < n){if (child 1 < n &&am…

raksmart的G口大流量服务器怎么样?

RAKsmart的G口大流量服务器以其高性能、高可用性、灵活配置和全球覆盖等特点&#xff0c;成为许多企业和个人用户的理想选择。以下是对raksmart G口大流量服务器的具体介绍&#xff1a; 1. 服务特点&#xff1a; RAKsmart提供多种类型的G口大流量服务器&#xff0c;包括流媒体专…

华为ensp中vlan与静态路由技术的实现

vlan 同一网段的设备&#xff0c;可以互通&#xff1b; 虚拟局域网&#xff1a;将局域网从逻辑上划分为多个局域网&#xff0c;不同通过vlan编号区分&#xff1b; 实现网络隔离。提高了网络安全性&#xff1b; vlan编号为12位&#xff1b; 范围1-4094可以用来配置 默认处于…

【Qt系列样式表】探索Qt Widget的艺术化设计与应用(Macos风格)(持续更新中...)

✨✨ Rqtz 个人主页 : 点击✨✨ &#x1f308;Qt系列专栏:点击 &#x1f388;PyQt系列专栏:点击&#x1f388; &#x1f388;Qt智能车上位机专栏: 点击&#x1f388; &#x1f388;Qt串口助手专栏:点击&#x1f388; &#x1f4ab;宗旨:共享IT之美,共创机器未来 目录 界面…

[羊城杯 2020]Blackcat1

知识点&#xff1a;数组加密绕过 进入页面熟悉的web三部曲&#xff08;url地址&#xff0c;web源代码&#xff0c;web目录扫描&#xff09; url地址没有什么东西去看看源代码. 这有一个mp3文件点一下看看. 在最后面发现了 PHP源码. if(empty($_POST[Black-Cat-Sheriff]) || em…

SpringMVC的初理解

1. SpringMVC是对表述层&#xff08;Controller&#xff09;解决方案 主要是 1.简化前端参数接收( 形参列表 ) 2.简化后端数据响应(返回值) 1.数据的接受 1.路径的匹配 使用RequestMapping(可以在类上或在方法上)&#xff0c;支持模糊查询&#xff0c;在内部有method附带…

【无人机设计与控制】四旋翼无人机俯仰姿态保持模糊PID控制(带说明报告)

摘要 为了克服常规PID控制方法在无人机俯仰姿态控制中的不足&#xff0c;本研究设计了一种基于模糊自适应PID控制的控制律。通过引入模糊控制器&#xff0c;实现了对输入输出论域的优化选择&#xff0c;同时解决了模糊规则数量与控制精度之间的矛盾。仿真结果表明&#xff0c;…