跳到主要内容
HTTP 应用层协议详解:从请求响应到实战编程 | 极客日志
C++ 大前端
HTTP 应用层协议详解:从请求响应到实战编程 综述由AI生成 HTTP 协议是客户端与服务器通信的基础。 HTTP 协议的基本概念、URL 编码、请求与响应格式(请求行、报头、正文)、常见请求方法(GET/POST)、状态码及 Header 字段。同时讲解了连接类型(长/短连接)、Cookie 与 Session 机制以解决无状态问题。最后通过 C++ 代码示例演示了如何构建一个简单的 HTTP 服务器,实现静态资源访问、重定向及动态交互功能。
RefactorPro 发布于 2026/4/6 更新于 2026/5/21 20 浏览本篇摘要
本篇将介绍何为 HTTP 协议,以及它的请求与答复信息的格式(请求行,请求包头,正文等),对一些比较重要的部分来展开讲解,其他不常用的即一概而过,从静态网页到动态网页的过渡,最后底层基于 TCP 实现简单的 HTTP 服务器的代码编写构建一个简单的网页(包含对应的跳转,重定向,动态交互等功能),采取边讲解 http 结构边用代码形成效果展示的形式进行讲解。
本文将要介绍的内容的大致流程图如下:
一· 认识 HTTP
在互联网世界中,HTTP(HyperText Transfer Protocol,超文本传输协议)是一个至关重要的协议。它定义了客户端(如浏览器)与服务器之间如何通信,以交换或传输超文本(如 HTML 文档)。
HTTP 协议是客户端与服务器之间通信的基础。
客户端通过 HTTP 协议向服务器发送请求,服务器收到请求后处理并返回响应。HTTP 协议是一个无连接、无状态的协议,即每次请求都需要建立新的连接,且服务器不会保存客户端的状态信息(后面我们详细讲解)。
我们要明白 :
我们上网要么从远端拿数据,要么上传数据到远端(数据:短视频,视频,网页,图片,音频)。
本质 :
访问远端 linux 服务器的某个文件等(底层走的还是 TCP 那套逻辑)。
1.1 认识 URL
下面就拿一个示例 URL 为例说明:
https://example.com/path?spm =1010.2135 .3001.5343
详细解释如图:
解释 :我们输入网址进行访问 (http 协议)就是拿着指定 ip+port 连接到具体主机的具进程(类似我们之前写的服务器);然后浏览器把路径按照规定进行编码(特殊字符)然后这个符合要求的 uri(路径)发送给指定进程;再由这个进程执行完(找到对应内容或者进行对应方法执行)把结果返回来!
陪一张标准的介绍 :
1.2 urlencode 与 urldecode
这里有些字符不能随意出现。比如,某个参数中需要带有这些特殊字符(/ 或者?), 就必须先对特殊字符进行转义。
将需要转码的字符转为 16 进制,然后从右到左,取 4 位 (不足 4 位直接处理),每 2 位做一位,前面加上%,编码成%XY 格式。
1.3 HTTP 协议请求与响应格式
注意 :http 协议,序列和反序列化用的是特殊字符进行子串拼接,且不依赖任何第三方库。
http 请求,携带的数据 (可选,不是所有的 http 请求,都会携带数据)。
我们之后拆解进行解析的时候,把它看做一个长的字符串!
认识 http 的请求以及回复
首先浏览器把对应的域名进行转化成 ip 然后–>对应的 ip+port 就可以找到对应的服务器端进程了;接着把后面跟的具体路径 (uri) 内容 (也可以空) 等进行填充请求报头;是 get 还是 post(请求方法);然后按照上面的 request 的格式打包之后成一个长的字符串;然后发送给服务端;服务端就要进行解析;根据 uri 具体位置把内容拿出来放到自己的响应正文里 (html);最后序列化发回去;剩下就是浏览器提取进行先关转化!!!
粗力度说一下如何序列化和反序列化
解析 request
这段字符串由浏览器构建。
报头就是利用哈希表的 K-V 结构储存。
我们直接对这个长串进行找\r\n(第一个) 来成功提取请求行,把请求方法/版本/uri 解析出来(第一个都找不到就是无效的 http 请求了)。
然后依次一行行读取,发现是空行,然后找到对应的 Content-Length 的长度,对应读取下一行(千万不要再继续按照上面要求读取因为最后一行无\r\n)
构建 response
查找 uri 获得对应的响应正文。
根据相关信息填写请求行以及报头。
拼接成长字符串。
1.4 剖析 request 与 response 的组成部分
认识请求方法: 这里我们主要介绍的是 POST 和 GET 方法,代码演示也是用他俩。
GET 代码书写也是先以 get 为例,简单来说就是获取对应资源!
POST 当然了,还有 PUT(推送数据到服务端)HEAD(只返回相应头)DELETE(删除服务端数据)OPTIONS(查询 URL 指定资源)等,这些不常用,这里就不讲解了。
认识 URI 这个路径就是 uri,然后服务端就会对应位置找到资源,返回给我们:
注意:'/ '不是 Linux 根目录,是 Linux 服务器的一个 web 目录(wwwroot),资源都放在这个目录下(如果路径不是/,服务器自动拼接首页)。
我们在浏览器段请求的时候,首页作为站点的入口,一个网站就是一颗多叉树,点击链接的时候,浏览器会形成新的访问地址,发起二次请求。
然后我们进行点击,他就会访问到我们 web 目录 (wwwroot) 的 home.html 内容发给浏览器进行转义,浏览器自动识别点击处封装的链接然后自动发送请求 :
uri 就是对应服务器的某一 web 目录下的一个路径;当发起一定请求;服务端就会根据 uri(特定) 配合 request 的正文进行内容提取;然后构成响应正文最后发给浏览器进行转义!
认识 HTTP 版本
这里就简单理解成你是什么版本然后告诉服务端给你提供什么版本的服务。
比如你是微信低版本的客户端;然后给服务端发起请求:服务端识别到你的 http 的版本是低的就提供给你低版本服务(这里对于一些 app 应用也可以理解成是被限制的浏览器,其实也是发送 http 的网页请求)。
认识状态码描述 状态码 英文名称 含义解释 200 OK 请求成功,服务器返回对应数据 201 Created 请求成功且创建了新资源 204 No Content 请求成功,但无内容返回 301 Moved Permanently 资源永久重定向到新地址 302 Found 资源临时重定向到新地址 304 Not Modified 资源未修改,可直接使用缓存 400 Bad Request 客户端请求语法错误 401 Unauthorized 请求需要身份认证,未授权 403 Forbidden 服务器拒绝请求,无访问权限 404 Not Found 服务器找不到请求的资源 500 Internal Server Error 服务器内部错误,处理请求失败 502 Bad Gateway 网关错误,代理服务器获取响应失败 503 Service Unavailable 服务器暂时不可用,可能因维护或过载
最常见的状态码:
比如 200(OK),404(NotFound),403(Forbidden),302(Redirect 重定向), 504(Bad Gateway),我们暂时只需要了解这些即可。
响应文也可以是无内容的,如果是有内容那么一定会在 Content-length 加上长度再给用户的;因此用户就找空格然后看对应的 Content-length 大小进行提取响应正文进行转义即可。
字段名 含义 Content-Type 数据类型(如 text/html 等) Content-Length Body 的长度(单位为比特) Host 客户端告知服务器,所请求的资源是在哪个主机的哪个端口上 User-Agent 声明用户的操作系统和浏览器版本信息 referer 当前页面是从哪个页面跳转过来的 Location 搭配 3xx 状态码使用,告诉客户端接下来要去哪里访问 Cookie 用于在客户端存储少量信息,通常用于实现会话(session)的功能
下面我们就先来认识 Content-Length,Content-type,Connection 这几个报头为主来讲解:
Content-Type:数据类型 (text/html 等) 对应的后缀比如 png mp4 等都对应的有不同类型 (这里我们直接截取后缀进行判断添加即可)。
Content-Length: Body 的长度这里可以考虑读取文件的时候调用:1·c++文件操作/2·系统自带的 stat 函数。
那么下面我们根据这俩个报头给我们之前写的 http 答复添加上:
服务端收到答复通过解析获得的路径去读取文件:如果读到就构建响应正文 + 正确码以及解释 + 报头之长度,正文类型等;如果失败就把路径修改成对应的 404 所在的文件,让它作为正文被返回,同理报头等那些也都是 404 对应相关的了。
读文件的时候(有可能不都是文本,比如二进制的图片之类的) 进行二进制读取,一口气读完。
static bool readfile (std::string &text, std::string tar) {
std::ifstream in (tar.c_str()) ;
if (!in.is_open ())
return false ;
int size = filesize (tar);
text.resize (size);
in.read ((char *)text.c_str (), size);
return true ;
}
static int filesize (std::string file) {
struct stat buff;
int s = stat (file.c_str (), &buff);
return buff.st_size;
}
就是每次浏览器开一个新页面就会先向对应的服务端请求 (/favicon.ico 一个文件) 里面是那个小图标。
此外还建议,读取的时候数组开大一点(因为这里我们是默认它可以把请求都读上来的,忽略了其他情况)
浏览器会请求这个图标也就是会也就是 Accept 请求头会有对应的关于/favicon.ico 对应的类型;然后如果我们还是上面的逻辑找不到就 404 的话(之前就可以,但是现在我们报头会返回类型这样就导致类型不匹配了,因此会出现问题)那么请求如果是它,此时我们在答复的时候就必须返回对应的类型的响应正文以及对应的 Content-type 是 image/x-icon 这个类型,否则浏览器就会爆出类型不匹配从而出错,因此问题就在于这个请求头的 Accept 与答复的 Content-type 及响应正文之间的不匹配问题,故对于这个图标的请求我们采取忽略不处理,'不理这个请求图标的进程了!'
我们对服务端收到的 uri 为这个路径的请求直接忽略即可 (这里浏览器多个请求访问其实是多个多个不同进程去访问的,也就是 ip 同 port 不同,因此服务端直接对发它的进程忽略不回复;接着对其他进程的请求再做答复即可)。
对于客户端(浏览器)请求如果对应的报头比如 content-type 等;是空的那么服务端返回的如果不是空是可以匹配的(客户端默认)﹔但是如果客户端发送的正文已知一些报头的类型;但是服务端返回来的是不匹配的;客户端直接报错 (浏览器无法解释)。
下面我们根据我们制作的基于 http 的小网站的请求验证一下:
HTTP 中的 connection 字段是 HTTP 报文头的一部分,它主要用于控制和管理客户端与服务器之间的连接状态。
HTTP/1.1:在 HTTP/1.1 协议中,默认使用持久连接。当客户端和服务器都不明确指定关闭连接时,连接将保持打开状态,以便后续的请求和响应可以复用同一个连接。
HTTP/1.0:在 HTTP/1.0 协议中,默认连接是非持久的。如果希望在 HTTP/1.0 上实现持久连接,需要在请求头中显式设置 Connection:keep-alive。
如何使用的:Connection:keep-alive:表示希望保持连接以复用 TCP 连接,Connection:close:表示请求/响应完成后,应该关闭 TCP 连接。
短连接:就是每次都要 connect 和 accept(比如我们自己实现的那个简单 http 服务器)。
长连接:就是只需要 connect 和 accept 一次,客户端和服务端就直接收发消息即可。
访问 qq 新闻官网,使用 1.0 版本,就不支持长连接而且失败,因为 telnet 只支持 http 但是访问你的它是支持 https:
这里比如百度就支持了 http;而且发送的 1.1 默认就是长连接,双方达成协议:
发送请求的时候,前提是客户端(浏览器)要支持长连接,然后发送 1.1,告诉服务端要进行长连接,最后协商后才是长连接,否则就默认短连接了 (这里注意互相支持的协议应该相同)。
基于我们模拟实现的简单的 http 服务器访问与应答原理分析: 首先我们访问对应的 ip+8080 默认进入我们的/:web 的首页也就是 index:
我们点击就自然会进行一下页面跳转;比如点击登录,此时浏览器就会识别到然后重新给服务端发送对应的 href 对应的请求,访问对应文件:
下面比如说我们点击了 自动打款这个选项:浏览器就会自动找对应 web 首页的 1 文件;服务端解析后发现找不到就直接构建 404 文件返回给浏览器然后被它转义,于是我们就看到了:
不过,目前我们访问的资源全是以静态资源形式呈现的 (文件)。
给我们之前的 http 服务器简单添加了两个报头分别是 Content-type 和 Content-length;其次就是对于那个小图标特判一下,接著裁是理解不同 htm 文件里的的转关系:其实就是浏览器再次选一个进程向同一个服务端访间;有的是直接在当前页面展现出来,有的就是直接刷新当前页面进行显示(比如 a 标签),还有的是跳转新页面进行展示–>归根结底,还是浏览器重新构建请求发送给服务器而已!
telnet 与 wget 指令
telnet 使用 telnet 前提是安装后,把自己对应服务器的端口开放,防火墙关闭!
首先,我们需要知道:
首先如果没有开放云服务器的端口的话拿公网的话,访向云服务器会被拒绝;因此之前 tcp/udp 模拟拿的比如是本地环回就是本地通信不涉及网络;如果是子网 ip 的话;是云服务器下发的 ip(云服务器所在局域对应的内部 ip),云服务器默认是接收的。
如果自己的云服务器开通端口后就允许外部的设备拿着对应的公网 ip+port(对应自己云服务器主机标志) 进行访问,由于存在风险;故需要对应的云服务器开放对应端口来支持访问才行。
因为我们开放了云服务器端口,因此被允许了,然后发送请求找到云服务器内执行的 main 程序,进行底层的 tcp 连接:成功后就可以进行发送请求,即基于 tcp 的通信了。
使用注意事项:使用的时候就是 telnet ip port 然后 ctrl+] 然后再回车即输入访问的网址或者请求即可,q 为退出。
这里我们就用 telnet 进行连接我们的 http 服务器了。
进行访问首页:
wget
解释:获取对应的网址对应的云服务器内的 web 首页里对应文件内容 + 名称,下载到本地(爬虫) 一些网站有反爬机制明确禁止。
安装指令:
sudo apt-get update
sudo apt-get install wget
再探状态码:重定向之永久重定向 (301)+ 临时重定向 (302)
比如学校南门开了个超市,但是超市临时搬到了北门,因此就会在原来南门的位置搞个告示,这样学生们就看到了直接去北门了–>(临时重定向)302+location! 但是如果超市由于北门生意好,就搬到北门不回来了,因此它就会在原来南门的地方贴个告示说永久到北门–>(永久重定向)301+location!
下面我们画个 client-server 图形象理解下:
这里就是客户 (浏览器) 给服务端发送访问 s1 的请求;但是原先的位置的内容不存在 (被服务端重定向了);因此当 client 访问 s1 资源的时候,服务端判断重定向返回对应的 s2 地址给 client(然后 client 拿到后就去访问),这样就完成了重定向!
这里永久重定向主要是针对搜索引擎的,因为它要保证拿到对应公司网址的是最新的,因此需要记录下来之前被重定向的资源的新地址!!!
比如这里我们汉语搜索 qq 新闻,浏览器搜索引擎要能保证上面的这点,及时抓取对应的网址:
这里只要我们搜索这个路径的资源就会被永久重定向:301
if (_route == "./wwwroot/redirect" ) {
setcode (301 );
setheader ("Location" , "https://example.com/page" );
return true ;
}
对于 301 的永久重定向也是一样的,只不过这个浏览器不会记录,但是永久的会记录,到时候直接去那个地方即可 (我们的属于短连接,每次请求完毕都会断开) 效果不明显。
telnet 一下也发现被返回的 location,然后浏览器拿着这个 location 的新地址去访问新的地方:
if (!ans) {
setcode (302 );
setheader ("Location" , "http://123.249.104.207:8080/404.html" );
}
我们访问的我们自己服务端没有的路径文件,就会被返回 302 重定向:
重定向就是在 response 的 Location 处放上对应的网址,还有些其他操作等等,没有响应正文 。
重识请求方法之 GET 与 POST
GET:获取资源(图片,视频,音频,网页… 静态资源),这里也可以上传类似 post 功能但是,uri 是含参数的!
POST:上传资源,比如登录的时候上传账号 + 密码;然后服务端对应根据它进行相应的服务–>动态的 (进行了交互)!
这里就是我们登录的首页,我们输入账号和密码就是上传数据就会和服务端进行交互,这里用的是 post 请求,服务端通过对应的正文拿到账号和密码,后执行 login 服务 (服务端自己定义的),比如访问对应账号密码下的资源。
1· 下面我们看一下利用 post 的请求与应答:
此时,浏览器会自动识别我们的输入然后进行转化成正文,构建请求发送,接着服务端收到后会执行 login 对应的服务然后返回正文。
我们设置的这个服务是只要密码正确就会进行视频播放贡面,因此我们判断到是 post 请求后直接提取正文把密码拿到,然后进行对应 login 服务(RESTful 风格的网络接口!) 进行匹配构建应答即可!
这里之前我们用的 get 都是静态的 (没有交互的),也就是直接访问对应路径下的文件,但是下面我们就要给它对应的 uri 加上参数!
这里修改成 get 那么浏览器识别输入后就会按照 get 请求构建请求:
这里也是同理我们从 uri 的参数中提取对应的账号密码传给 login 服务,让它匹配进行构建答复即可:
获得静态网页或者资源,加上参数可以获取动态资源。
提交参数以 uri 形式提交。
GET 提交参数,不能过程,一般有长度限制。
参数会回显(比如账号密码)。
获得动态资源。
提交参数以正文形式提交。
正文传递,意味着长度可以很长。
不回显,比较私密。
但是 ,它俩都不是安全的,以明文形式在网络中传递,抓包就能获取,其中,https(有加密)>post 动态交互安全>get 动态交互!
fiddler 使用验证 GET 与 POST 的不安全性
fiddler:一个抓取构建好的报文的软件,fiddler 抓到的报文,是已经被浏览器构建完毕的 http 请求,就是将来要发送到网络中的。
下面我们模拟下这个过程 :
说自了就是这个 fiddler 带着浏览器的请求以及服务器对应的应答在网络中传输 (相当于浏览器和服务器之间通信的信使)
它的存在不就暴露了某些信息吗 (尤其是 post 的比如登录功能等)
首先,我们在官网安装好 fiddler 软件,然后启动:
我们可以看到虽然 post 不会在网址栏回显但是这样一抓取不就暴露密码了吗,因此说 get 和 post 都是不安全的(对 http 而言)!
先进行清空,然后默认是可以抓取 http 的包的!
一般这种密码登录类似功能在网络中传播都是被加密的–>用的是 https,下面我们抓取下:
fiddler 默认是只抓取 http,对于 https 我们要自己设置:
我们用用一个使用 https 协议发送请求的登录页面验证下:
重新认识 HTTP HTTP 协议是一个无连接、无状态的协议,即每次请求都需要建立新的连接,且服务器不会保存客户端的状态信息。
1· 为什么说 http 是无连接?
可以理解成 http 位于应用层,是一个应用层协议,底层的网络层是基于 tcp 实现的,也就是 tcp 负责 accept 和 connect,即对于长短连接是其底层 tcp 的说法,而它是只负责 response 和 request 的,即是没有连接而言。
http 本质就是一种文件的服务器,比如每次我们请求它就是分析处理给我们找到对应文件然后发回来,自身是不会记录任何数据的,更不会记录客户端的信息 (比如登录账号访问,那么你每次访问都要登录,但是这样就造成了麻烦)。
浅浅认识 cookie 和 session
比如每次我们访问网站看视频都需要登录,此时我们第一次登录后,就可以看视频了,但是下一次希望不在登录就可以看,因此这就是 cookie 机制 (第一次登录后,服务器验证成功后,把对应的账号密码 cookie 返回浏览器储存,然后下次继续访问网站看视频,浏览器默认发送上对应的 cookie,浏览器验证后成功就直接重定向到视频页面而不是登录页面)!
如何 edge 查找 cookie 信息:这里是储存在文件里:
一般储存在浏览器的文件或者内存中,但是文件中的可能性更大!
但是这样有可能就会出现黑客盗取对应浏览器中的 cookie 信息,导致账号密码被盗!
因此下面 session 出现了 。
同理,再次访问,浏览器会带上 sessionid 去访问某资源,当服务端看到这一资源的 id 或者没有和对应 cookie 匹配,此时就重新登陆否则直接拿资源给它!
此时就是,客户端第一次请求服务端,服务端把客户对应的账号和密码和一些其他信息进行一定编码,然后得到一个 id,服务端拿着这个 id 给对应的用户建立一个记录(用户访问的一些资源),然后发给浏览器作为它的 cookie,当下一次浏览器访问这个服务器的时候,就会自动带上这个 id,然后服务器收到这个 id 就能找到对应的资源记录了!
比如用其他浏览器访问这个服务器,cookie 中没有 id 就会自然重新建立了,但是如果是被黑客盗取了,然后把这个 id 发给服务器,这样它就能访问之前用户访问的资源了,但是我们确实避免了黑客通过单纯 cookie 盗号行为!
这样解决了黑客盗号问题,但是黑客还是可以访问的,因此就是服务端的保护措施了:比如过期时间,异常检测,ip 溯源,地址变更等!
因此 http 引入的 cookie 与 session 就减低了它的无连接,无状态的弊端!
如果想更深入了解 cookie 与 session,后续将更新专门一篇讲解它俩!
二· 基于所学 http 相关概念实现简单的 http 服务器
2.1 视频效果展示 下面我们基于上面所叙述的 http 方面的知识来简单实现的 http 版服务器,具有跳转,重定向,动态交互等功能,然后在登录页面密码只要是 666 就能正确登录并跳转视频播放页面,否则就 404 页面!
2.2 源码
三· 本篇小结
本篇学习了相关 http 协议的内容,明白了它的概念,结构等等,以及自己手搓实现了简单版的 http 服务器,当然前提还得是对 http 相关知识掌握牢固,其次就是考验代码能力了,博主在学习 http 专题时候历经一个星期多,从学习到编写对应代码,也陷入过对应的几个小时找 bug 环节,但是最终由于那份坚持还是完成了任务,此篇,博主通过自己整理的笔记再一次书写成博客耗时半天,重温了一遍知识就是很爽~,也希望对大家学习 http 有帮助!
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online