ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

【Linux网络编程】--HTTP应用层协议

2026/8/21 9:37:28 拓冰建站 浏览量
【Linux网络编程】--HTTP应用层协议 HTTP协议前面我们知道对于协议是一种约定应用层协议我们程序员是可以自己进行定制的不过未来我们大部分还是使用前面的大佬们已经定义好的一些现成的又非常好用的应用层协议可以供给我们直接参考使用HTTP协议就是其中之一在互联网世界中,HTTP(HyperText Transfer Protocol超文本传输协议)是一个很重要的协议其定义了客户端与服务器之间是如何进行通信的以交换或传输超文本就比如HTML文档。HTTP协议是客户端和服务器之间通信的基础客户端通过HTTP协议向服务器发送请求服务器收到请求后会进行处理并且返回响应。HTTP协议是一个无连接无状态的协议即每次请求都需要去建立新的连接且服务器不会保存客户端的状态信息我们平时在刷抖音短视频的时候发现一个好玩的要分享那么有时候会是分享一个链接那么这个链接我们称为URL。URL我们将其称为统一资源定位符。DeepSeek 开放平台https://platform.deepseek.com/usage如上就是一个URL。但是我们发现上面的URL是以HTTPS开始的其实HTTP和HTTPS之间的区别就是HTTP在传输我们的数据内容的时候其是以明文的方式传输的而HTTPS在通信的时候是会对数据进行加密的。然后//后面到.com这部分就是我们要访问的域名。域名其实就是IP地址首先确定一点的是我们访问目标服务器我们一定是拿IP地址去访问的。未来我们用户通过浏览器去访问服务器那么我们将这个域名发送过去会有一个域名解析的服务器。我们的浏览器不会先和我们的目标服务器进行发送而是会先发送给域名解析服务器然后解析到对应的IP地址后然后才会发起请求。然后对于域名解析服务器其会内置到我们的浏览器中所以对于其我们不需要在找IP地址。域名是方便我们用户使用如果只能使用IP地址的话那么我们要对那些IP地址进行记忆我们人最适合看的是自然语言。所以总结就是域名就是IP如下所示端口号我们一般都是进行省略的。浏览器没有将端口号将其回显给我们对于一些服务器其端口号一般都是固定的。当我们通过浏览器进行资源申请的时候那么对应的服务器端要给我们的用户返回一个网页。那么其实际上是返回一个网页文件的那么我们的服务器是如何找到对应的文件呢所以我们的用户在申请的时候要提供文件路径。HTTP本质上就是获取对应服务器上的文件内容。所以整个URL就有如下三个信息IPPort目标文件路径那么就可以确定全网中唯一的一个文件了。实质上我们用户上网的行为就如下两个1、将自己的数据发送到网络中2、将网络中的数据进行获取那么当我们未来要给我们的服务器传递信息那么就要将片段标识符写入。这叫做URL传参。下面我们通过代码来完成对HTTP的协议进行编写来加深我们的理解HTTP.hpp然后我们上面提到我们人上网的两个行为一个就是从网络上获取到信息还有就是向网络中发送数据。那么我们的代码中就进行一个HTTP请求服务然后还有HTTP应答服务。然后我们还需要一个HTTP协议。HTTP底层浏览器底层用的都是TCP socket用的都是ipport进程间通信机制。下面我们来可靠HTTP的请求格式和应答格式请求格式如上所示。我们就看上面的这个抽象图首先第一行是请求行从第二行开始到这个空行我们称为请求报头空行后面就是请求正文。不过正文部分是可以不带的。HTTP中其的请求信息都是以行为单位保存的。所以我们发现每一个请求数据后面都有个换行符。然后请求行中又使用的空格进行分割分为三部分。第一个请求方法一般就为GET和POST第二个就是我们的URL。第三个是我们要使用的HTTP的版本。然后请求报头其是类似我们的KV结构的信息。所以未来我们的客户端发送的信息要为如上的结构然后我们的服务器端收到信息后就要将其进行处理。其结构如下那么未来对于HTTP的信息其就是一个字符串信息。那么未来我们对其处理按照换行符进行处理然后进行序列化和反序列化处理。然后我们又可以将请求行和请求报头部分称为报头部分然后请求正文称为有效载荷。然后在整个的协议报文中可以通过空行进行分离。在极端情况下对于请求报头和请求正文我们都可以为空但是请求行和空行都必须含有。那么我们的接收端接收HTTP格式的数据的时候那么要咋确定读取完全部的信息其实就当读取到一个空行的时候那么就可以确定报头部分的信息都完全读取完。然后对于正文部分我们咋知道要读多长的数据呢在我们的报头中存在一个属性这个属性叫做Content-Lengthlen\r\n那么这个len就是正文部分的长度。然后对于数据的反序列化也很容易实现了不需要借助别的工具可以自主完成。在HTTP协议中无论成功还是失败如果网络没有问题 服务器没有挂掉那么都必须要有应答。所以在应答的HTTP信息中会有状态码的信息还有状态码描述。如上是应答的HTTP的格式。下面我们来完成对读取到的HTTP信息进行序列化和反序列化处理。如上就是HTTP服务器端对于用户发起的HTTP请求进行处理。那么其会有三种情况1、返回的为空那么就没有完整行2、返回的是\r\n那么说明我们读取到了HTTP报文中的空行3、如果不是上面的二者之一那么说明我们分析到了一行具体内容那么代码雏形如下#pragma once #include iostream #include string #include sstream #include unordered_map #include Logger.hpp using namespace NS_LOG_MODULE; // 分割符 const std::string linesep \r\n; const std::string spacesep ; const std::string headersep : ; class Util { public: static std::string ReadLine(std::string str) { auto pos str.find(linesep); if (pos std::string::npos) { return std::string(); } std::string line str.substr(0, pos); if (line.empty()) { return linesep; } str.erase(0, line.size() linesep.size()); return line; } }; class HttpRequest { private: bool ParseReqLine(std::string httpstr) { std::string req_line Util::ReadLine(httpstr); if (req_line.empty() || req_line linesep) { return false; } std::stringstream ss(req_line); ss _method _uri _version; return true; } bool ParseHeaderkv(std::string httpstr) { std::string header_line; do { header_line Util::ReadLine(httpstr); if (header_line.empty()) { return false; } if (header_line ! linesep) { // 以:为分隔符进行KV分割 auto pos header_line.find(headersep); if (pos std::string::npos) { return false; } std::string key header_line.substr(0, pos); std::string value header_line.substr(pos headersep.size()); _header[key] value; } } while (header_line ! linesep); return true; } bool ParseText(std::string httpstr) { //先判断是否有正文 if(_header.find(Content-Length)_header.end()) { _text; } else { int content_lenstd::stoi(_header[Content-Length]); _texthttpstr.substr(0,content_len); httpstr.erase(0,content_len); } } public: // 反序列化处理 bool Deserialize(std::string httpstr) { // 1、解析请求行 // 只有当读取的是完整行才进行解析 bool n ParseReqLine(httpstr); (void)n; // LOG(LogLevel::DEBUG)_method_method; // LOG(LogLevel::DEBUG)_uri_uri; // LOG(LogLevel::DEBUG)_version_version; // 2、解析报头 n ParseHeaderkv(httpstr); // for (auto header : _header) // { // std::cout header.first ## header.second \r\n\r\n; // } // std::cout start| httpstr |end; //3、解析正文部分 nParseText(httpstr); (void)n; return true; } private: // 结构化字段 std::string _method; std::string _uri; std::string _version; std::unordered_mapstd::string, std::string _header; std::string _blank_line; std::string _text; }; class HttpResponce { public: private: }; class HttpProtocol { public: HttpProtocol() { } std::string HandlerHttpRequest(std::string req) { // std::cout#######################std::endl; // std::coutreqstd::endl; // return hello world; // 1、先保证报文完整性 // 就主要确认是否存在空行 // 2、对读取到的HTTP信息进行序列化处理--将字节流信息转化为结构化信息 HttpRequest httpreq; httpreq.Deserialize(req); return std::string(); } ~HttpProtocol() { } private: };HTTP常见Header我们以上面的图为例。Host:这个字段就是用来确认这个请求是不是要让我当前机器进行处理的Content-Length:这个就是确认我们的请求中是否有正文部分然后就是有的话那么正文长度。Content-Tyep:其代表的是我们正文部分的数据类型User-Agent:表示我们浏览器相关的信息这个通常是用来证明我的请求是不是一个HTTP请求HTTP RESPONSE:前面我们提到HTTP超文本请求的本质其实就是通过网络服务器将Linux机器上特定目录下的文件以http应答的方式返回给我们的客户端(浏览器或者app)所以当我们服务器端接收到客户端的HTTP请求后就要根据其传入的uri给其返回对应的文件。如果其没有传入对应的路径那么我们就是将对应机器的根目录的文件进行回应那么这个我们也称为首页。不过我们给用户应答的部分也应该符合HTTP格式的那么其基本格式如下那么我们的应答结构如下下面我们就要根据客户端传入的HTTP信息对我们要应答的做一个序列化处理。代码如下bool Build(HttpRequest *req) { std::string target_filereq-Uri(); _textUtil::ReadFile(target_file); if(_text.empty()) { _code404; return false; } else { _code200; _code_descOK; return true; } } std::string Serialize() { //请求报头 std::string respstr_version; respstrspacesep; respstrstd::to_string(_code); respstrspacesep; respstr_code_desc; respstrlinesep; //添加报头 for(auto header:_header) { std::string lineheader.firstheadersepheader.secondlinesep; respstrline; } //添加空行 respstr_blank_line; //添加正文部分 respstr_text; return respstr; }HTTP的状态码:上面在服务器应答服务的时候我们看到其返回的HTTP信息中存在一个状态码和状态码描述那么其大致有如下这些状态码。下面我们可以将状态码的部分植入到我们的代码中类似一个映射表。对于3XX类型的状态码其是包含重定向相关的状态码。一个网页其不一定只是一个文件可能会有一个网页多种资源构成首页中的一张网页可能会和很多中资源管理HTTP浏览器需要把首页要的资源全部通过HTTP获取。前面我们对于数据的读取方式我们设置的是以文本方式进行读取那么这个方式是有问题的因为数据的类型有很多比如说图片视频等所以我们要将其设置成二进制的读取方式。浏览器是如何知道我们收到的response正文部分具体是什么格式的文件呢在我们的HTTP请求报头中存在一个Content-Type那么其就是表示的我们的数据类型。那么我们又是如何知道返回的资源是啥格式的呢其可以通过资源名称的后缀进行区分。HTTP的方法前面我们建立的HTTP请求和应答大致还是建立了一个IO这样然后获取的资源也都是一些静态资源那么如果我们需要通过动态方式进行那么就要使用到一些HTTP的方法。我们该如何理解浏览器(客户端)给httpserver提交参数的过程呢在我们的前端网页中有一个表单那么用户会对表单进行填写然后浏览器会自动去提取表单中的内容然后去构建带参数的http request。