【Linux】HTTP协议1

news/2024/5/23 22:16:24

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:题目解析

在这里插入图片描述


目录

  • 👉🏻http概念初识
    • http协议格式
  • 👉🏻URL
  • 👉🏻简单实现http协议(版本一)
    • HttpProtocol.hpp
  • 👉🏻http协议(版本二,增设解析请求行)
    • HttpProtocol.hpp
    • Main.cc
    • 补充知识
      • rz命令
      • ifstream的seekg和tellg

👉🏻http概念初识

HTTP(Hypertext Transfer Protocol)是一种用于传输超文本数据(如HTML、CSS、JavaScript等)的应用层协议。它是万维网的基础,用于在客户端和服务器之间传输信息。以下是关于HTTP协议的一些重要信息:

特点:

  1. 无连接性(Connectionless)每个请求/响应对都是独立的,服务器在处理完一个请求后断开连接,等待下一个请求的到来。

  2. 无状态性(Stateless):每个请求之间没有关联,服务器不会保留之前请求的状态信息,每个请求都是独立处理的。

  3. 基于文本(Text-based):HTTP的请求和响应都是以纯文本的形式进行传输,易于阅读和调试。

  4. 灵活性(Flexibility):HTTP支持多种数据类型和方法,可以传输各种类型的数据,并支持多种应用场景。

工作原理:

  1. 客户端-服务器模型:HTTP是一种客户端-服务器模型的协议,客户端发送请求,服务器返回响应。

  2. 请求-响应模式:客户端向服务器发送请求,请求包括方法(GET、POST等)、URL、HTTP版本、请求头部等信息;服务器接收到请求后,返回相应的响应,包括状态码、响应头部、响应体等信息。

http底层的发送和传输协议就是依靠TCP协议实现的,http说白了就是在TCP协议的基础上增设和规范了数据的结构性。

请求方法:

  1. GET:请求指定的资源。
  2. POST:向指定资源提交数据进行处理请求。
  3. PUT:上传指定的URI,作为其表示。
  4. DELETE:请求服务器删除指定的资源。
  5. PATCH:对资源进行部分修改。
  6. HEAD:类似于GET请求,只不过服务器在响应中只返回首部,不返回实体的主体部分。

状态码:

  1. 1xx:信息性状态码。
  2. 2xx:成功状态码。
  3. 3xx:重定向状态码。
  4. 4xx:客户端错误状态码。
  5. 5xx:服务器错误状态码。

协议版本:

  1. HTTP/1.0:最初的版本,每次请求/响应建立一个新的连接。
  2. HTTP/1.1:持久连接,复用连接,支持管道化请求。
  3. HTTP/2.0:二进制协议,多路复用,头部压缩,服务器推送等特性。

安全性

  1. HTTPS:HTTP的安全版本,使用SSL/TLS协议进行加密通信,保护数据的安全性和完整性。

HTTP协议在互联网中扮演着重要的角色,它的简单性和灵活性使得它成为了Web开发中不可或缺的一部分。

http协议格式

http协议主要分两个部分:请求和响应,

📃请求

  • 首行: [方法] + [url] + [版本]
  • Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
  • Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Heade中会有一个 Content-Length属性来标识Body的长度
    在这里插入图片描述

📃响应

  • 首行: [版本号] + [状态码] + [状态码解释]
  • Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
  • Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中.
    在这里插入图片描述

👉🏻URL

URL(Uniform Resource Locator)是统一资源定位符的缩写,是用于标识互联网上资源位置的一种标识符。它由多个部分组成,用于定位资源的地址以及指定访问该资源的方式。

💦 URL的结构:

一个标准的URL通常由以下几个部分组成:

  1. 协议(Protocol):指定访问资源所使用的协议或者规则,如 HTTP、HTTPS、FTP 等。

http://表示的是协议名称,表示请求时需要使用的协议,通常使用的是HTTP协议或安全协议HTTPS。HTTPS是以安全为目标的HTTP通道,在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性

  1. 主机名(Host):指定资源所在的主机或者服务器的域名或者IP地址。

  2. 端口号(Port):指定访问资源所使用的端口号。通常,如果使用默认端口号,则可以省略。

  3. 路径(Path):指定服务器上资源的路径。它是资源在服务器上的具体位置。

  4. 查询字符串(Query String):用于传递参数给服务器。它以 ? 开始,多个参数之间用 & 分隔。

  5. 片段标识符(Fragment Identifier):标识资源中的特定部分。它以 # 开始,用于定位文档中的特定位置。
    在这里插入图片描述

💦示例:

考虑以下示例URL:

https://www.example.com:8080/path/to/resource?param1=value1&param2=value2#section3
  • 协议(Protocol):HTTPS

  • 主机名(Host):www.example.com(服务器地址)

  • 端口号(Port):8080

  • 路径(Path):/path/to/resource(在服务器下的资源路径)

  • 查询字符串(Query String):param1=value1&param2=value2

  • 片段标识符(Fragment Identifier):section3


域名 www.example.com 可以分为以下三个部分:

  1. 子域名(Subdomain):在这个示例中,“www” 是子域名。它位于主域名之前,用于表示特定的服务、部门或者功能。通常,“www” 是用于指示该域名用于网站服务的子域名,但也可以使用其他的子域名,比如 “blog”、“shop” 等。

  2. 主域名(Domain):在示例中,“example.com” 是主域名。它是一个在互联网上唯一的名称,用于表示一个网站或者服务的整体身份。主域名通常由注册商注册,并且需要进行域名解析以映射到相应的IP地址。

  3. 顶级域名(Top-Level Domain,TLD):在示例中,“.com” 是顶级域名。顶级域名是域名体系中的最高层次,用于表示域名的类型或者所属的组织、地理位置等信息。常见的顶级域名包括 “.com”、“.net”、“.org” 等通用顶级域名,以及各个国家/地区的国家顶级域名(如 “.cn”、“.uk” 等)。

综上所述,www.example.com 这个域名可以分为 “www”(子域名)、“example”(主域名)和 “.com”(顶级域名)三个部分。

💦用途:

URL是互联网上资源的唯一标识符,用于在Web浏览器中定位和访问网页、图像、文件、视频等资源。通过URL,用户可以直接访问并获取到网络上的各种资源。

总之,URL是互联网上资源的地址标识符,它由多个部分组成,用于指定资源的位置和访问方式。

👉🏻简单实现http协议(版本一)

HttpProtocol.hpp

#pragma once#include<iostream>
#include<string>
#include<vector>using namespace std;
const string HttpSep = "\r\n";class HttpRequest 
{
public:HttpRequest():_req_blank(HttpSep){}bool Getline(string& str,string* line)//获取请求行的内容{//1.找到内容停止符的位置auto pos = str.find(HttpSep);if(pos==string::npos) return false;//2.截取正文*line = str.substr(0,pos);//3.读取完后,将str中已提取的内容清空str.erase(0,pos+HttpSep.size());return true;}//反序列化void Deserialize(string& request){//1.先读请求行string line;bool ok = Getline(request,&line);if(!ok) return;//如果读不到\r\n则退出_req_line = line;//2.再读报文每一行的内容while(true){bool ok = Getline(request,&line);//再接着往下读取if(ok&&line.empty()){//如果读到了,但是内容为空//则剩下的内容为_req_content = request;}else if(ok&&!line.empty()){_req_header.push_back(line);}elsebreak;}}void DebugHttp(){std::cout << "_req_line" << _req_line << std::endl;for(auto &line : _req_header){std::cout << "---> " << line << std::endl;}std::cout << "_req_blank: " << _req_blank << std::endl;std::cout << "_req_content: " << _req_content << std::endl;}
private:string _req_line;//请求行vector<string> _req_header;//用来存储请求数据中所有的请求行数据string _req_content;//报文内容string _req_blank;//空行};

最后去网页访问我们的服务器,就可以得到这个
在这里插入图片描述

👉🏻http协议(版本二,增设解析请求行)

此次版本,我们主要给Http协议中增设解析请求行的功能,
将请求行解析为三部分:

  • method:请求方法
  • URL:资源地址
  • http_version:http的版本

而后我们还要再对URL地址进行解析,可以得到

  • path:资源的路径
  • suffix:资源的后缀名

文件目录如下:
在这里插入图片描述

HttpProtocol.hpp

#pragma once#include <iostream>
#include <string>
#include <vector>#include <sstream>using namespace std;
const string HttpSep = "\r\n";const string homepage = "index.html";
const string wwwroot = "./wwwroot"; // 根目录class HttpRequest
{
public:HttpRequest() : _req_blank(HttpSep), _path(wwwroot){}~HttpRequest(){}bool Getline(string &str, string *line) // 获取请求行的内容{// 1.找到内容停止符的位置auto pos = str.find(HttpSep);if (pos == string::npos)return false;// 2.截取正文*line = str.substr(0, pos);// 3.读取完后,将str中已提取的内容清空str.erase(0, pos + HttpSep.size());return true;}void Deserialize(string &request){// 1.先读请求行string line;bool ok = Getline(request, &line);if (!ok)return; // 如果读不到\r\n则退出_req_line = line;// 2.再读报文每一行的内容while (true){bool ok = Getline(request, &line); // 再接着往下读取if (ok && line.empty()){// 如果读到了,但是内容为空// 则剩下的内容为_req_content = request;}else if (ok && !line.empty()){_req_header.push_back(line);}elsebreak;}}void ParseReqLine(){stringstream ss(_req_line);ss >> _method >> _url >> _http_version;if (_url == "/"){// 如果是在当前下目录,则直接令它的路径资源终点为index.html_path += _url;_path += homepage;}else{_path += _url;}}void ParseSuffix(){auto pos = _path.rfind(".");if (pos == string::npos)_suffix = ".html";else_suffix = _path.substr(pos);}void Parse(){// 1.解析请求行ParseReqLine();// 2.解析资源后缀ParseSuffix();}void DebugHttp(){std::cout << "_req_line" << _req_line << std::endl;for (auto &line : _req_header){std::cout << "---> " << line << std::endl;}std::cout << "_req_blank: " << _req_blank << std::endl;std::cout << "_req_content: " << _req_content << std::endl;std::cout << "Method: " << _method << std::endl;std::cout << "url: " << _url << std::endl;std::cout << "http_version: " << _http_version << std::endl;}// 接下来再整几个返回解析后内容的函数接口string Url(){return _url;}string Path(){return _path;}string Suffix(){return _suffix;}private:string _req_line;           // 请求行vector<string> _req_header; // 用来存储请求数据中所有的请求行数据string _req_content;        // 报文内容string _req_blank;          // 空行// 解析之后的内容string _method;       // 请求方法string _url;          // 资源路径string _http_version; // http版本string _path;         // 资源的具体文件路径位置,path包含在url中,所以需要从url中解析string _suffix;       // 请求资源的后缀
};

Main.cc

#include <iostream>
#include <memory>
#include <string>
#include <unistd.h>
#include <fstream>
#include "TcpServer.hpp"
#include "HttpProtocol.hpp"
string SuffixToType(const std::string &suffix) // 返回后缀名对应的http-type
{if (suffix == ".html" || suffix == ".html")return "text/html";else if (suffix == ".png")return "image/png";else if (suffix == ".jpg")return "image/jpeg";else{return "text/html";}
}
string GetFileContent(const string &path)
{// 为了中间不出现字符解码出现问题// 我们读取文件内容,而是以二进制形式读取ifstream in(path, std::ios::binary); // 二进制读写if (!in.is_open())return "";in.seekg(0, in.end);       // 位置移到文件末尾int filesize = in.tellg(); // 读取文件当前位置之前的大小in.seekg(0, in.beg);       // 读完大小后回到开头string content; // 存储读到的二进制内容content.resize(filesize);in.read((char *)content.c_str(), filesize);in.close();return content;
}
// request: 我们认为我们已经读到了一个完整的请求了
std::string HandlerHttpRequest(std::string &request)
{HttpRequest req;// 1.反序列化+解析http信息+打印http信息req.Deserialize(request);req.Parse();req.DebugHttp();// 2.获取请求URL中的文件资源,即文件的内容string content = GetFileContent(req.Path());// 3.打印看看解析后,资源文件的后缀和对应的http格式类型std::cout << "suffix: " << req.Suffix() << " Type: " << SuffixToType(req.Suffix()) << std::endl;if (!content.empty()){//返回http状态+请求到的资源信息+Content-Typestd::string httpstatusline = "Http/1.0 200 OK\r\n";std::string httpheader = "Content-Length: " + std::to_string(content.size()) + "\r\n";httpheader += "Content-Type: " + SuffixToType(req.Suffix()) + "\r\n"; // 正文的类型httpheader += "\r\n";std::string httpresponse = httpstatusline + httpheader + content;return httpresponse;}return "";
}// ./server port
int main(int argc, char *argv[])
{if (argc != 2){std::cout << "Usage : " << argv[0] << " port" << std::endl;return 0;}uint16_t localport = std::stoi(argv[1]);std::unique_ptr<TcpServer> svr(new TcpServer(localport, HandlerHttpRequest));svr->Loop();return 0;
}

实现效果如下:
在这里插入图片描述
在这里插入图片描述

补充知识

rz命令

rz 命令是用于在 Linux 系统中接收文件的命令,通常用于在终端下通过串口或者 SSH 连接从另一台计算机发送文件到本地计算机。这个命令通常与 sz 命令一起使用,sz 用于发送文件,rz 用于接收文件。

使用 rz 命令时,通常会通过串口或者 SSH 连接到远程计算机,然后在远程计算机上执行 sz 命令发送文件,最后在本地计算机上执行 rz 命令来接收文件。

要使用 rz 命令接收文件,你需要先确保你的 Linux 系统上已经安装了支持该命令的终端软件,例如 minicomscreenpicocom 等,以及 szrz 命令所在的软件包。

在使用 rz 命令接收文件时,默认情况下文件会保存到当前工作目录下。如果你想要控制 rz 命令将接收的文件保存到特定的目录下,你可以在执行 rz 命令之前先切换到你想要保存文件的目录。

ifstream的seekg和tellg

在 C++ 中,ifstream 是用于从文件中读取数据的输入流类,seekgtellgifstream 类的成员函数,用于在文件中定位和查询当前读取位置的偏移量。

  • seekg: seekg 函数用于在文件中定位读取位置。它有多个重载形式,可以设置文件读取位置的绝对位置或相对位置。通常情况下,seekg 函数的参数为一个 streampos 类型的偏移量,表示从文件开始位置的偏移量,但也可以使用 ios_base::seekdir 类型的参数指定相对于当前位置或者文件末尾的偏移量。

  • tellg: tellg 函数用于查询当前读取位置的偏移量。它返回一个 streampos 类型的值,表示当前读取位置相对于文件开始位置的偏移量。

这两个函数通常用于实现文件的随机访问,可以在文件中自由移动读取位置。下面是一个简单的示例:

#include <iostream>
#include <fstream>int main() {std::ifstream file("example.txt", std::ios::binary); // 打开一个二进制文件用于读取// 获取当前读取位置的偏移量std::streampos currentPosition = file.tellg();std::cout << "Current position: " << currentPosition << std::endl;// 移动读取位置到文件末尾file.seekg(0, std::ios::end);// 获取文件末尾的偏移量std::streampos endPosition = file.tellg();std::cout << "End position: " << endPosition << std::endl;// 将读取位置移动回文件开头file.seekg(0, std::ios::beg);// 读取文件内容// ...return 0;
}

这段代码演示了如何使用 seekgtellg 函数在文件中定位和查询读取位置的偏移量。


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长

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


http://www.mrgr.cn/p/53360465

相关文章

深入了解Redis内存淘汰策略中的LRU算法应用

LRU算法简析 LRU&#xff08;Least Recently Used&#xff0c;最近最少使用&#xff09;算法是一种常见的内存淘汰策略&#xff0c;它根据数据的访问时间来决定哪些数据会被淘汰。LRU算法的核心思想是&#xff1a;最久未被访问的数据&#xff0c;被认为是最不常用的数据&#…

ClickHouse 数据类型、表引擎与TTL

文章目录 数据类型注意事项 表引擎1.TinyLog 引擎2.MergeTree 引擎3.ReplacingMergeTree 引擎4.AggregatingMergeTree 引擎5.SummingMergeTree 引擎6.CollapsingMergeTree 引擎7.Distributed 引擎 TTL列级 TTL表级TTL 数据类型 ClickHouse 数据类型Java 数据类型数据范围UInt8…

Flutter应用下拉菜单设计DropdownButtonFormField控件介绍

文章目录 DropdownButtonFormField介绍使用方法重点代码说明属性解释 注意事项 DropdownButtonFormField介绍 Flutter 中的 DropdownButtonFormField 是一个用于在表单中选择下拉菜单的控件。它是 DropdownButton 和 TextFormField 的组合&#xff0c;允许用户从一组选项中选择…

.net Core8 WebApi性能测试

测试电脑锐龙4600H node.js版本20.x bun.js 版本 1.15 .net Core 版本 8

js逆向实战之企名片返回数据解密

url:https://www.qimingpian.com/finosda/project/pinvestment 分析过程抓流量包,发现回显数据都是加密的。想要找到解密逻辑,可以参考上一篇文章的思路,直接搜索拦截器。有五处,只需要看响应拦截器即可。第一处响应拦截器可以看到e.data,有经验的人大概就可以判断出来解…

游戏工作室为什么要使用海外住宅IP防封?

当谈到游戏工作室时&#xff0c;它们通常以多开游戏账号来获取收益为主要目标。这种商业模式在游戏产业中已经成为一个独特而且颇具潜力的领域。然而&#xff0c;随之而来的是防封问题&#xff0c;特别是当游戏工作室试图通过多开账号来赚取更多收益时。因此&#xff0c;我们有…

基于JAVA实现的贪吃蛇小游戏

JAVA贪吃蛇小游戏实现: 贪吃蛇曾经在我们的童年给我们带来了很多乐趣。贪吃蛇这款游戏现在基本上没人玩了&#xff0c;甚至在新一代人的印象中都已毫无记忆了。。。但是&#xff0c;这款游戏可以在一定程度上锻炼自己的编程能力。 目前这个版本只是一个测试版本&#xff0c;所以…

Colored Balls

这道题目的模型倒是可以记住 我们发现这个配对很像摩尔投票,所以考虑原数列是否有主元素 对于一个集合,我们选出其中最大的\(a_i\),假设剩余的\(a\)的和小于等于\(a_i\)(此时有主元素),那么比较显而易见的就是最终会分出\(a_i\)个组;否则的话,我们考虑下界\(\lceil \fr…

Python多线程编程深度探索:从入门到实战

title: Python多线程编程深度探索:从入门到实战 date: 2024/4/28 18:57:17 updated: 2024/4/28 18:57:17 categories:后端开发tags:多线程 并发编程 线程安全 Python 异步IO 性能优化 实战项目第1章:Python基础知识与多线程概念 Python简介: Python是一种高级、通用、解释型…

不同状态空间模型的实验对比(二)

对五个下游任务进行了实验比较&#xff0c;包括单/多标签分类、视觉对象跟踪、像素级分割、图像到文本生成和人/车辆再识别。 论文&#xff1a;https://arxiv.org/abs/2404.09516 作者单位&#xff1a;安徽大学、哈尔滨工业大学、北京大学更多相关工作将在以下GitHub上不断更新…

想要应聘前端工程师——学习路线指南

前端工程师学习路线 按照前端岗位需求,以优先学习工作更需要,面试更常考的内容为原则,由浅入深,层层铺垫,与时俱进,可以较容易地总结出前端学习路线图: HTML / CSS / JavaScript 基础学习 《Web 入门》 MDN 权威入门指南,HTML / CSS / JavaScript 快速上手 《CSS 世界…

containerd 配置使用私有镜像仓库 harbor

前言 ​当要从非安全的镜像仓库中进行 Pull、Push 时,会遇到 x509: certificate signed by unknown authority 错误提示; 这是由于镜像仓库是可能是 http 服务,或者 https 的证书是自签名的就会出现这个问题。 Containerd 可以配置为连接到私有镜像仓库,并使用仓库在每个节…

uniapp 实现复选下拉框

预览图相关代码 <template><view class="uni-stat__select"><span v-if="label" class="uni-label-text">{{label + :}}</span><view class="uni-stat-box" :class="{uni-stat__actived: current}&q…

提示词优化的自动化探索:Automated Prompt Engineering

编者按&#xff1a; 作者在尝试教授母亲使用 LLM 完成工作任务时&#xff0c;意识到提示词的优化并不像想象中简单。提示词的自动优化对于经验并不丰富的提示词撰写者很有价值&#xff0c;他们没有足够的经验去调整和改进提供给模型的提示词&#xff0c;这引发了对自动化提示词…

Redis底层数据结构之SDS

目录 一、概述二、SDS结构三、为什么使用SDS redis底层数据结构已完结&#x1f44f;&#x1f44f;&#x1f44f;&#xff1a; ☑️redis底层数据结构之SDS☑️redis底层数据结构之ziplist☑️redis底层数据结构之quicklist☑️redis底层数据结构之Dict☑️redis底层数据结构之I…

web开发中特殊字符的对应值与转义字符

原文链接:https://www.cnblogs.com/greatverve/archive/2011/07/18/web-char.html URL中的特殊字符URL中的特殊字符是不能再URL中直接传递的,需要进行编码。编码的格式为:%加字符的ASCII码,即一个百分号%,后面跟对应字符的ASCII(16进制)码值。 例:要传递字符串“this%…

Java的运算符

前言 由于Java的运算符大部分和C语言类似&#xff0c;所以这里只会提及与C语言不同的要点~~ 算术运算符 基本四则运算 和C语言一样&#xff0c;Java也有 - * / %&#xff08;加、减、乘、除、取模&#xff09;的算术运算符。 注意Java的取模运算符&#xff08;%&#xff09;可…

微信小程序使用echarts组件实现饼状统计图功能

微信小程序使用echarts组件实现饼状统计图功能 使用echarts实现在微信小程序中统计图的功能&#xff0c;具体的实现步骤思路可进我主页查看我的另一篇博文https://blog.csdn.net/weixin_45465881/article/details/138171153进行查看&#xff0c;本篇文章主要使用echarts组件实…

java自动生成pojo,springboot自动生成pojo

第一步 pom引入依赖 <dependencies><!-- mybatis-generator --><dependency><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-core</artifactId><version>1.3.7</version></dependency>&…

SAP MM 定义物料类型的属性配置里的New entries按钮

SAP MM 定义物料类型的属性配置里的New entries按钮在SAP的很多后台配置界面上都有New Entries(新条目)按钮,方便企业用户可以根据企业特有业务需求来增加新的配置条目。事实上,并不是所有的配置界面里,都能很随意很方便的允许企业用户点击’New Entries’按钮来做定制配置的…