HTTP 应用层协议详解与简易服务器实现
介绍 HTTP 协议基础,包括 URL、请求响应格式、方法(GET/POST)、状态码及 Header。通过 C++ 代码演示如何构建简易 HTTP 服务器,实现静态资源访问、重定向及动态交互。同时讲解长连接、Cookie/Session 机制及安全性(HTTP vs HTTPS)。适合希望深入理解 Web 通信原理及底层实现的开发者。

介绍 HTTP 协议基础,包括 URL、请求响应格式、方法(GET/POST)、状态码及 Header。通过 C++ 代码演示如何构建简易 HTTP 服务器,实现静态资源访问、重定向及动态交互。同时讲解长连接、Cookie/Session 机制及安全性(HTTP vs HTTPS)。适合希望深入理解 Web 通信原理及底层实现的开发者。

本文介绍 HTTP 协议基础,包括 URL、请求响应格式、方法(GET/POST)、状态码及 Header。通过 C++ 代码演示如何构建简易 HTTP 服务器,实现静态资源访问、重定向及动态交互。同时讲解长连接、Cookie/Session 机制及安全性(HTTP vs HTTPS)。


本文将要介绍的内容的大致流程图如下:

我们要明白:
本质:
其实就是我们平时说的网址!
详细解释如图:

解释: 输入网址进行访问 (http 协议) 就是拿着指定 ip+port 连接到具体主机的具进程;然后浏览器把路径按照规定进行编码(特殊字符),然后这个符合要求的 uri(路径) 发送给指定进程;再由这个进程执行完(找到对应内容或者进行对应方法执行)把结果返回来!
标准介绍:

比如:

下面简单认识下 http 结构:

下面形象看一下:

先放图:

原理解释:

如何进行解析出来:

剩下的就交给浏览器自己转义即可!

这里我们主要介绍的是 POST 和 GET 方法,代码演示也是用他俩。
当然了,还有 PUT(推送数据到服务端)、HEAD(只返回相应头)、DELETE(删除服务端数据)、OPTIONS(查询 URL 指定资源)等,这些不常用,这里就不讲解了。
陪一张图:

下面我们利用图片通俗易懂来认识下:

输入对应的 web 根目录(/)

这个路径就是 uri,然后服务端就会对应位置找到资源,返回给我们:

因此:
下面我们来验证下:
我们先输入网址进入网页:


然后比如点击了主页:

总结下:
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 | 服务器暂时不可用,可能因维护或过载 |
首先我们先认识下客户端收到答复是如何解析的:

如何提取相应正文:
陪一张 header 表:
| 字段名 | 含义 |
|---|---|
| Content-Type | 数据类型(如 text/html 等) |
| Content-Length | Body 的长度(单位为比特) |
| Host | 客户端告知服务器,所请求的资源是在哪个主机的哪个端口上 |
| User-Agent | 声明用户的操作系统和浏览器版本信息 |
| referer | 当前页面是从哪个页面跳转过来的 |
| Location | 搭配 3xx 状态码使用,告诉客户端接下来要去哪里访问 |
| Cookie | 用于在客户端存储少量信息,通常用于实现会话(session)的功能 |
下面我们就先来认识 Content-Length,Content-type,Connection 这几个报头为主来讲解:
那么下面我们根据这俩个报头给我们之前写的 http 答复添加上:
大致思路:
注意:
// 读取对应路径下的 html:
static bool readfile(std::string &text, std::string tar) {
// 1·文本读取(比如读取图片等可能会遇到\n 等符号导致读取中断直接换行等当前行后面内容就无法读到,导致错误):
// std::ifstream in(tar.c_str());
// if (!in.is_open()) return false;
// std::string line;
// while (std::getline(in, line)) {
// text += line;
// }
// in.close();
// return true;
// 2·二进制读取:
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(string file) {
// c++ 文件 io 方法:
// ifstream in(file);
// if (!in.is_open()) return 0;
// in.seekg(0, in.end);
// int size = in.tellg();
// in.seekg(0, in.beg);
// in.close();
// return size;
// 系统调用:
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 及响应正文之间的不匹配问题,故对于这个图标的请求我们采取忽略不处理,'不理这个请求图标的进程了!'
因此:
根据此处小结一下:
下面我们根据我们制作的基于 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 一次,客户端和服务端就直接收发消息即可。

下面我们举些例子来看一下:
我们自己写的 http 服务器它就是短连接。
请求:


可以看到每次都要重新连接,故最后还是短连接!


总结下:
发送请求的时候,前提是客户端(浏览器)要支持长连接,然后发送 1.1,告诉服务端要进行长连接,最后协商后才是长连接,否则就默认短连接了 (这里注意互相支持的协议应该相同)。
首先我们访问对应的 ip+8080 默认进入我们的/:web 的首页也就是 index:



video 页面

不过,目前我们访问的资源全是以静态资源形式呈现的 (文件)。
小结一下:
给我们之前的 http 服务器简单添加了两个报头分别是 Content-type 和 Content-length;其次就是对于那个小图标特判一下,接著裁是理解不同 htm 文件里的的转关系:其实就是浏览器再次选一个进程向同一个服务端访问;有的是直接在当前页面展现出来,有的就是直接刷新当前页面进行显示(比如 a 标签),还有的是跳转新页面进行展示–>归根结底,还是浏览器重新构建请求发送给服务器而已!
使用 telent 前提是安装后,把自己对应服务器的端口开放,防火墙关闭!
首先,我们需要知道:
进行安装 telnet:
sudo apt install telnet

client 界面:

server 界面:



sudo apt-get update
sudo apt-get install wget
演示效果:下面我们抓取 - 下 qq 新闻页面:

进行爬取:
爬取结果:
比如学校南门开了个超市,但是超市临时搬到了北门,因此就会在原来南门的位置搞个告示,这样学生们就看到了直接去北门了–>(临时重定向)302+location! 但是如果超市由于北门生意好,就搬到北门不回来了,因此它就会在原来南门的地方贴个告示说永久到北门–>(永久重定向)301+location!

这里永久重定向主要是针对搜索引擎的,因为它要保证拿到对应公司网址的是最新的,因此需要记录下来之前被重定向的资源的新地址!!!

下面那我们实现的 http 网站演示下效果:
if(_route == "./wwwroot/redirect"){
setcode(301);
// 永久重定向:第一次被浏览器记录保存,剩下的再访问就直接到 location 目的网址访问
setheader("Location", "https://example.com/new_location");
return true;
}
效果:

被永久重定向了:


if(!ans){
setcode(302);
// 临时重定向,每次访问到指定网址就拿到对应的 location 里的网址进行访问,浏览器不进行保存!
setheader("Location", "http://123.249.104.207:8080/404.html");
}


telent 查看:
因此总结下:
重定向就是在 response 的 Location 处放上对应的网址,还有些其他操作等等,没有响应正文。

举个例子:


1· 下面我们看一下利用 post 的请求与应答:
请求:

应答:

下面看下我们的效果:
进行跳转:

输入密码:

2·GET 请求与应答验证:
这里修改成 get 那么浏览器识别输入后就会按照 get 请求构建请求:

效果:

应答:

看一下我们抓包的结果:请求:

应答:

GET 与 POST 特点总结:
GET:
POST:
但是,它俩都不是安全的,以明文形式在网络中传递,抓包就能获取,其中,https(有加密)>post 动态交互安全>get 动态交互!
fiddler:一个抓取构建好的报文的软件,fiddler 抓到的报文,是已经被浏览器构建完毕的 http 请求,就是将来要发送到网络中的。
它的存在不就暴露了某些信息吗 (尤其是 post 的比如登录功能等)
使用流程:
先进行清空,然后默认是可以抓取 http 的包的!


一般这种密码登录类似功能在网络中传播都是被加密的–>用的是 https,下面我们抓取下:
fiddler 默认是只抓取 http,对于 https 我们要自己设置:



显示抓包结果:

`因此可以发现 https 是更加安全的!
我们在上面说了这样一句话:
HTTP 协议是一个无连接、无状态的协议,即每次请求都需要建立新的连接,且服务器不会保存客户端的状态信息。
1· 为什么说 http 是无连接?
可以理解成 http 位于应用层,是一个应用层协议,底层的网络层是基于 tcp 实现的,也就是 tcp 负责 accept 和 connect,即对于长短连接是其底层 tcp 的说法,而它是只负责 response 和 request 的,即是没有连接而言。
2· 为什么说 http 无状态?
http 本质就是一种'文件'的服务器,比如每次我们请求它就是分析处理给我们找到对应文件然后发回来,自身是不会记录任何数据的,更不会记录客户端的信息 (比如登录账号访问,那么你每次访问都要登录,但是这样就造成了麻烦)。
cookie:比如每次我们访问网站看视频都需要登录,此时我们第一次登录后,就可以看视频了,但是下一次希望不在登录就可以看,因此这就是 cookie 机制 (第一次登录后,服务器验证成功后,把对应的账号密码 cookie 返回浏览器储存,然后下次继续访问网站看视频,浏览器默认发送上对应的 cookie,浏览器验证后成功就直接重定向到视频页面而不是登录页面)!
如图:

如何 edge 查找 cookie 信息:这里是储存在文件里:



点开就能看到对应信息:

小结下:
session:

同理,再次访问,浏览器会带上 sessionid 去访问某资源,当服务端看到这一资源的 id 或者没有和对应 cookie 匹配,此时就重新登陆否则直接拿资源给它!
此时就是,客户端第一次请求服务端,服务端把客户对应的账号和密码和一些其他信息进行一定编码,然后得到一个 id,服务端拿着这个 id 给对应的用户建立一个记录(用户访问的一些资源),然后发给浏览器作为它的 cookie,当下一次浏览器访问这个服务器的时候,就会自动带上这个 id,然后服务器收到这个 id 就能找到对应的资源记录了!
比如用其他浏览器访问这个服务器,cookie 中没有 id 就会自然重新建立了,但是如果是被黑客盗取了,然后把这个 id 发给服务器,这样它就能访问之前用户访问的资源了,但是我们确实避免了黑客通过单纯 cookie 盗号行为!
这样解决了黑客盗号问题,但是黑客还是可以访问的,因此就是服务端的保护措施了:比如过期时间,异常检测,ip 溯源,地址变更等!
因此 http 引入的 cookie 与 session 就减低了它的无连接,无状态的弊端! 如果想更深入了解 cookie 与 session,有机会博主后再次更新专门一篇讲解它俩!
下面我们基于上面所叙述的 http 方面的知识来简单实现的 http 版服务器,具有跳转,重定向,动态交互等功能,然后在登录页面密码只要是 666 就能正确登录并跳转视频播放页面,否则就 404 页面!
简单 HTTP 服务器功能测试效果


微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online