概述
本文介绍 HTTP 协议及其请求与响应信息的格式(请求行、请求包头、正文等),重点讲解重要部分,从静态网页到动态网页的过渡,最后基于 TCP 实现简单的 HTTP 服务器代码编写(构建一个简单的网页,包含跳转、重定向、动态交互等功能),采取边讲解 HTTP 结构边用代码形成效果展示的形式进行讲解。


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

一· 认识 HTTP
- 在互联网世界中,HTTP(HyperText Transfer Protocol,超文本传输协议)是一个至关重要的协议。它定义了客户端(如浏览器)与服务器之间如何通信,以交换或传输超文本(如 HTML 文档)。
- HTTP 协议是客户端与服务器之间通信的基础。
- 客户端通过 HTTP 协议向服务器发送请求,服务器收到请求后处理并返回响应。HTTP 协议是一个无连接、无状态的协议,即每次请求都需要建立新的连接,且服务器不会保存客户端的状态信息。
我们要明白:
- 我们上网要么从远端拿数据,要么上传数据到远端(数据:短视频,视频,网页,图片,音频)。
本质:
- 访问远端 Linux 服务器的某个文件等(底层走的还是 TCP 那套逻辑)。
1.1 认识 URL
其实就是我们平时说的网址!
详细解释如图:

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

1.2 urlencode 与 urldecode
- 这里有些字符不能随意出现。比如,某个参数中需要带有这些特殊字符(/ 或者?),就必须先对特殊字符进行转义。
- 将需要转码的字符转为 16 进制,然后从右到左,取 4 位 (不足 4 位直接处理),每 2 位做一位,前面加上%,编码成%XY 格式。
比如:

1.3 HTTP 协议请求与响应格式
下面简单认识下 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
- 用途:用于请求 URL 指定的资源。
- 示例:GET /index.html HTTP/1.1。
- 特性:指定资源经服务器端解析后返回响应内容。
- form 表单:https://www.runoob.com/html/html-forms.html。
代码书写也是先以 get 为例,简单来说就是获取对应资源!
POST
- 用途:用于传输实体的主体,通常用于提交表单数据。
- 示例:POST /submit.cgi HTTP/1.1。
- 特性:可以发送大量的数据给服务器,并且数据包含在请求体中。
- form 表单:https://www.runoob.com/html/html-forms.html。
当然了,还有 PUT(推送数据到服务端)、HEAD(只返回相应头)、DELETE(删除服务端数据)、OPTIONS(查询 URL 指定资源)等,这些不常用,这里就不讲解了。
陪一张图:

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

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

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

- 注意:
'/ '不是 Linux 根目录,是 Linux 服务器的一个 web 目录(wwwroot),资源都放在这个目录下(如果路径不是/,服务器自动拼接首页)。
因此:
- 我们在浏览器段请求的时候,首页作为站点的入口,一个网站就是一颗多叉树,点击链接的时候,浏览器会形成新的访问地址,发起二次请求。
下面我们来验证下:
- 然后我们进行点击,他就会访问到我们 web 目录 (wwwroot) 的 home.html 内容发给浏览器进行转义,浏览器自动识别点击处封装的链接然后自动发送请求:
我们先输入网址进入网页:


然后比如点击了主页:

总结下:
- uri 就是对应服务器的某一 web 目录下的一个路径;当发起一定请求;服务端就会根据 uri(特定) 配合 request 的正文进行内容提取;然后构成响应正文最后发给浏览器进行转义!
认识 HTTP 版本
- 这里就简单理解成你是什么版本然后告诉服务端给你提供什么版本的服务。
- 比如你是微信低版本的客户端;然后给服务端发起请求:服务端识别到你的 http 的版本是低的就提供给你低版本服务(这里对于一些 app 应用也可以理解成是被限制的浏览器,其实也是发送 http 的网页请求)。
认识状态码描述
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),我们暂时只需要了解这些即可。
认识 HTTP 常见 Header
首先我们先认识下客户端收到答复是如何解析的:

如何提取相应正文:
- 响应文也可以是无内容的,如果是有内容那么一定会在 Content-length 加上长度再给用户的;因此用户就找空格然后看对应的 Content-length 大小进行提取响应正文进行转义即可。
陪一张 header 表:
| 字段名 | 含义 |
|---|---|
| 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 对应相关的了。
注意:
- 读文件的时候(有可能不都是文本,比如二进制的图片之类的) 进行二进制读取,一口气读完。
// 读取对应路径下的 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)) { // 使用 getline 读取的时候每次会将 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);
// 这里 string 不能强转 char* 因此可先调用 c_str
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;
}
- 重点来了:有个非常阴险的 bug:
就是每次浏览器开一个新页面就会先向对应的服务端请求 (/favicon.ico 一个文件) 里面是那个小图标。

我们必须对它进行忽略处理:

比如我么我们第一次连接到主页的时候:

此外还建议,读取的时候数组开大一点(因为这里我们是默认它可以把请求都读上来的,忽略了其他情况)

解释下原理:
浏览器会请求这个图标也就是会也就是 Accept 请求头会有对应的关于/favicon.ico 对应的类型;然后如果我们还是上面的逻辑找不到就 404 的话(之前就可以,但是现在我们报头会返回类型这样就导致类型不匹配了,因此会出现问题)那么请求如果是它,此时我们在答复的时候就必须返回对应的类型的响应正文以及对应的 Content-type 是 image/x-icon 这个类型,否则浏览器就会爆出类型不匹配从而出错,因此问题就在于这个请求头的 Accept 与答复的 Content-type 及响应正文之间的不匹配问题,故对于这个图标的请求我们采取忽略不处理,'不理这个请求图标的进程了!'
因此:
- 我们对服务端收到的 uri 为这个路径的请求直接忽略即可 (这里浏览器多个请求访问其实是多个多个不同进程去访问的,也就是 ip 同 port 不同,因此服务端直接对发它的进程忽略不回复;接着对其他进程的请求再做答复即可)。
根据此处小结一下:
- 对于客户端(浏览器)请求如果对应的报头比如 content-type 等;是空的那么服务端返回的如果不是空是可以匹配的(客户端默认)﹔但是如果客户端发送的正文已知一些报头的类型;但是服务端返回来的是不匹配的;客户端直接报错 (浏览器无法解释)。
下面我们根据我们制作的基于 http 的小网站的请求验证一下:

- 下面再说一下 Connection 报头:
- 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 一次,客户端和服务端就直接收发消息即可。

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


可以看到每次都要重新连接,故最后还是短连接!
- 访问 qq 新闻官网,使用 1.0 版本,就不支持长连接而且失败,因为 telnet 只支持 http 但是访问你的它是支持 https:

- 这里比如百度就支持了 http;而且发送的 1.1 默认就是长连接,双方达成协议:

总结下:
发送请求的时候,前提是客户端(浏览器)要支持长连接,然后发送 1.1,告诉服务端要进行长连接,最后协商后才是长连接,否则就默认短连接了 (这里注意互相支持的协议应该相同)。
基于我们模拟实现的简单的 http 服务器访问与应答原理分析:
- 下面看一下首页对应的 html:
首先我们访问对应的 ip+8080 默认进入我们的/:web 的首页也就是 index:


- 我们点击就自然会进行一下页面跳转;比如点击登录,此时浏览器就会识别到然后重新给服务端发送对应的 href 对应的请求,访问对应文件:

video 页面
- 下面比如说我们点击了 自动打款这个选项:浏览器就会自动找对应 web 首页的 1 文件;服务端解析后发现找不到就直接构建 404 文件返回给浏览器然后被它转义,于是我们就看到了:

不过,目前我们访问的资源全是以静态资源形式呈现的 (文件)。
小结一下:
给我们之前的 http 服务器简单添加了两个报头分别是 Content-type 和 Content-length;其次就是对于那个小图标特判一下,接著裁是理解不同 htm 文件里的的转关系:其实就是浏览器再次选一个进程向同一个服务端访问;有的是直接在当前页面展现出来,有的就是直接刷新当前页面进行显示(比如 a 标签),还有的是跳转新页面进行展示–>归根结底,还是浏览器重新构建请求发送给服务器而已!
telnet 与 wget 指令
telnet
使用 telent 前提是安装后,把自己对应服务器的端口开放,防火墙关闭!
首先,我们需要知道:
- 首先如果没有开放云服务器的端口的话拿公网的话,访向云服务器会被拒绝;因此之前 tcp/udp 模拟拿的比如是本地环回就是本地通信不涉及网络;如果是子网 ip 的话;是云服务器下发的 ip(云服务器所在局域对应的内部 ip),云服务器默认是接收的。
- 如果自己的云服务器开通端口后就允许外部的设备拿着对应的公网 ip+port(对应自己云服务器主机标志) 进行访问,由于存在风险;故需要对应的云服务器开放对应端口来支持访问才行。
- 因为我们开放了云服务器端口,因此被允许了,然后发送请求找到云服务器内执行的 main 程序,进行底层的 tcp 连接:成功后就可以进行发送请求,即基于 tcp 的通信了。
进行安装 telnet:
sudo apt install telnet
- 使用注意事项:使用的时候就是 telent ip port 然后 ctrl+] 然后再回车即输入访问的网址或者请求即可,q 为退出。

- 这里我们就用 telnet 进行连接我们的 http 服务器了。
- 进行访问首页:
client 界面:

server 界面:

- 下面我们试着访问下百度的网页:


wget
- 解释:获取对应的网址对应的云服务器内的 web 首页里对应文件内容 + 名称,下载到本地(爬虫) 一些网站有反爬机制明确禁止。
- 安装指令:
sudo apt-get update
sudo apt-get install wget
演示效果:
下面我们抓取 - 下 qq 新闻页面:

进行爬取:

爬取结果:

再探状态码:重定向之永久重定向 (301)+ 临时重定向 (302)
- 举个通俗易懂的例子:
比如学校南门开了个超市,但是超市临时搬到了北门,因此就会在原来南门的位置搞个告示,这样学生们就看到了直接去北门了–>(临时重定向)302+location! 但是如果超市由于北门生意好,就搬到北门不回来了,因此它就会在原来南门的地方贴个告示说永久到北门–>(永久重定向)301+location!
- 下面我们画个 client-server 图形象理解下:

- 这里就是客户 (浏览器) 给服务端发送访问 s1 的请求;但是原先的位置的内容不存在 (被服务端重定向了);因此当 client 访问 s1 资源的时候,服务端判断重定向返回对应的 s2 地址给 client(然后 client 拿到后就去访问),这样就完成了重定向!
这里永久重定向主要是针对搜索引擎的,因为它要保证拿到对应公司网址的是最新的,因此需要记录下来之前被重定向的资源的新地址!!!
- 比如这里我们汉语搜索 qq 新闻,浏览器搜索引擎要能保证上面的这点,及时抓取对应的网址:

下面那我们实现的 http 网站演示下效果:
- 这里只要我们搜索这个路径的资源就会被永久重定向:301
if(_route == "./wwwroot/redirect"){
setcode(301); // 永久重定向:第一次被浏览器记录保存,剩下的再访问就直接到 location 目的网址访问
setheader("Location", "https://example.com/new_location");
return true;
}
- 对于 301 的永久重定向也是一样的,只不过这个浏览器不会记录,但是永久的会记录,到时候直接去那个地方即可 (我们的属于短连接,每次请求完毕都会断开) 效果不明显。
效果:

被永久重定向了:

- telnet 一下也发现被返回的 location,然后浏览器拿着这个 location 的新地址去访问新的地方:

- 测试下 302:
if(!ans){
setcode(302); // 临时重定向,每次访问到指定网址就拿到对应的 location 里的网址进行访问,浏览器不进行保存!
setheader("Location", "http://123.249.104.207:8080/404.html");
}
- 我们访问的我们自己服务端没有的路径文件,就会被返回 302 重定向:

- 被临时重定向到 404 文件了:

telent 查看:

因此总结下:
重定向就是在 response 的 Location 处放上对应的网址,还有些其他操作等等,没有响应正文。
重识请求方法之 GET 与 POST

- GET:获取资源(图片,视频,音频,网页… 静态资源),这里也可以上传类似 post 功能但是,uri 是含参数的!
- POST:上传资源,比如登录的时候上传账号 + 密码;然后服务端对应根据它进行相应的服务–>动态的 (进行了交互)!
举个例子:


- 这里就是我们登录的首页,我们输入账号和密码就是上传数据就会和服务端进行交互,这里用的是 post 请求,服务端通过对应的正文拿到账号和密码,后执行 login 服务 (服务端自己定义的),比如访问对应账号密码下的资源。
1· 下面我们看一下利用 post 的请求与应答:
请求:

应答:

- 此时,浏览器会自动识别我们的输入然后进行转化成正文,构建请求发送,接着服务端收到后会执行 login 对应的服务然后返回正文。
下面看下我们的效果:
- 我们设置的这个服务是只要密码正确就会进行视频播放贡面,因此我们判断到是 post 请求后直接提取正文把密码拿到,然后进行对应 login 服务(RESTful 风格的网络接口!) 进行匹配构建应答即可!
进行跳转:

输入密码:

2·GET 请求与应答验证:
- 这里之前我们用的 get 都是静态的 (没有交互的),也就是直接访问对应路径下的文件,但是下面我们就要给它对应的 uri 加上参数!
这里修改成 get 那么浏览器识别输入后就会按照 get 请求构建请求:

- 这里也是同理我们从 uri 的参数中提取对应的账号密码传给 login 服务,让它匹配进行构建答复即可:
效果:

应答:

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

应答:

GET 与 POST 特点总结:
GET:
- 获得静态网页或者资源,加上参数可以获取动态资源。
- 提交参数以 uri 形式提交。
- GET 提交参数,不能过程,一般有长度限制。
- 参数会回显(比如账号密码)。
POST:
- 获得动态资源。
- 提交参数以正文形式提交。
- 正文传递,意味着长度可以很长。
- 不回显,比较私密。
但是,它俩都不是安全的,以明文形式在网络中传递,抓包就能获取,其中,https(有加密)>post 动态交互安全>get 动态交互!
fiddler 使用验证 GET 与 POST 的不安全性
fiddler:一个抓取构建好的报文的软件,fiddler 抓到的报文,是已经被浏览器构建完毕的 http 请求,就是将来要发送到网络中的。- 下面我们模拟下这个过程:

- 说自了就是这个 fiddler 带着浏览器的请求以及服务器对应的应答在网络中传输 (相当于浏览器和服务器之间通信的'信使')
它的存在不就暴露了某些信息吗 (尤其是 post 的比如登录功能等)
使用流程:
- 首先,我们在官网安装好 fiddler 软件,然后启动:
- 我们可以看到虽然 post 不会在网址栏回显但是这样一抓取不就暴露密码了吗,因此说 get 和 post 都是不安全的(对 http 而言)!
先进行清空,然后默认是可以抓取 http 的包的!


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


- 我们用用一个使用 https 协议发送请求的登录页面验证下:

显示抓包结果:

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

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



- 一般储存在浏览器的文件或者内存中,但是文件中的可能性更大!
- 但是这样有可能就会出现黑客盗取对应浏览器中的 cookie 信息,导致账号密码被盗!
- 因此下面 session 出现了。
点开就能看到对应信息:

小结下:
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 页面!
简单 HTTP 服务器功能测试效果
2.2 源码

三· 本篇小结
- 本篇学习了相关 http 协议的内容,明白了它的概念,结构等等,以及自己手搓实现了简单版的 http 服务器,当然前提还得是对 http 相关知识掌握牢固,其次就是考验代码能力了,博主在学习 http 专题时候历经一个星期多,从学习到编写对应代码,也陷入过对应的几个小时找 bug 环节,但是最终由于那份坚持还是完成了任务,此篇,博主通过自己整理的笔记再一次书写成博客耗时半天,重温了一遍知识就是很爽~,也希望对大家学习 http 有帮助!


