目录
[1. HTTP协议](#1. HTTP协议)
[1.1 认识URL](#1.1 认识URL)
[1.2 urlencode和urldecode](#1.2 urlencode和urldecode)
[1.3 HTTP协议请求与响应格式](#1.3 HTTP协议请求与响应格式)
[1.3.1 HTTP请求](#1.3.1 HTTP请求)
[1.3.2 http请求代码](#1.3.2 http请求代码)
[1.3.3 http响应](#1.3.3 http响应)
[1.4 HTTP的方法](#1.4 HTTP的方法)
[1.4.1 HTTP常见方法](#1.4.1 HTTP常见方法)
[1.4.1.1 GET方法(重点)](#1.4.1.1 GET方法(重点))
[1.4.1.2 POST方法(重点)](#1.4.1.2 POST方法(重点))
[1.4.1.3 PUT方法(不常用)](#1.4.1.3 PUT方法(不常用))
[1.4.1.4 HEAD方法](#1.4.1.4 HEAD方法)
[1.4.1.5 DELETE方法(不常用)](#1.4.1.5 DELETE方法(不常用))
[1.4.1.6 OPTIONS方法](#1.4.1.6 OPTIONS方法)
[1.5 HTTP的状态码](#1.5 HTTP的状态码)
[1.6 HTTP常见的Header](#1.6 HTTP常见的Header)
[1.6.1 关于connection报头](#1.6.1 关于connection报头)
[1.6.2 核心作用](#1.6.2 核心作用)
[1.6.3 持久连接(长连接)](#1.6.3 持久连接(长连接))
[1.6.4 语法格式](#1.6.4 语法格式)
[1.6.5 HTTP常见header的表格](#1.6.5 HTTP常见header的表格)
[1.7 最简单的HTTP服务器](#1.7 最简单的HTTP服务器)
[2. 笔记](#2. 笔记)
[2.1 URL](#2.1 URL)
[2.2 前置背景理解](#2.2 前置背景理解)
[2.3 http协议的宏观格式](#2.3 http协议的宏观格式)
[2.4 HttpRequest和HttpResponse](#2.4 HttpRequest和HttpResponse)
[2.5 重定向](#2.5 重定向)
[2.6 GET方法](#2.6 GET方法)
[2.7 POST方法](#2.7 POST方法)
[2.8 bind失败原因以及解决方法](#2.8 bind失败原因以及解决方法)
[2.8.1 bind失败原因](#2.8.1 bind失败原因)
[2.8.2 解决方法](#2.8.2 解决方法)
[2.9 Cookie](#2.9 Cookie)
[2.10 connection报头](#2.10 connection报头)
[2.11 findler工具原理](#2.11 findler工具原理)
[3. 代码&&板书](#3. 代码&&板书)
[3.1 板书](#3.1 板书)
[3.2 代码](#3.2 代码)
[HTTP 历史及版本核心技术与时代背景](#HTTP 历史及版本核心技术与时代背景)
1. HTTP协议
虽然我们说,应用层协议是我们程序猿自己定的.但实际上,已经有大佬们定义了一些现成的,又非常好用的应用层协议,供我们直接参考使用.HTTP(超文本传输协议)就是其中之一。
在互联网世界中,HTTP(HyperTextTransfer Protocol,超文本传输协议)是一个至关重要的协议。它定义了客户端(如浏览器)与服务器之间如何通信,以交换或传输超文本(如HTML文档)。
HTTP协议是客户端与服务器之间通信的基础。客户端通过HTTP协议向服务器发送请求,服务器收到请求后处理并返回响应。HTTP协议是一个无连接、无状态的协议,即每次请求都需要建立新的连接,且服务器不会保存客户端的状态信息。
1.1 认识URL
平时我们俗称的"网址"其实就是说的URL(统一资源定位符)U niform R esource L ocator

第一个就是我们所采用的协议,或者方案名,://就是固有格式,user:pass是登录信息,我们早期的url是把我们的登录信息放在://后面的,但现在的http已经不这么做了,现在已经见不到了,现在的登录信息已经被http隐藏起来了,www.exaple.jp是服务器的地址,:80现在没了,只要协议指明,端口号其实已经固定了,这个端口号可以省略了,浏览器会自己进行端口号的缺省填充,不管是谁要访问目标服务器必须要有端口号,这是少不了的,ur1没有端口号是因为看到的是没有端口号,但浏览器会根据协议名自动把端口号加上,这是必须的,给我们形成的url端口号往往是省略的,dir/index/htm是文件路径。
http也可以传参,后面带?意思是?左边是访问的资源,右边是我们要传递的参数,uid=1就是k,v值,#后面是片段标识符,一个网页中我们有片段标识符,比如说有图片,图片1、2、3,此时我们可以ur1不更新,而是片段标识符再更新,这个现在用的也很少了,这是较为完整的url
1.2 urlencode和urldecode
像/?:等这样的字符,已经被url当作特殊意义理解了,因为这些字符不能随意出现,比如,某个参数中需要带有这些特殊字符,就必须先对特殊字符进行转义。
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一个,前面加上%,编码成%XY格式。
例如:

"+"被转义成了"%2B"
urldecode就是urlencode的逆过程。
UrlEncode编码/UrlDecode解码 - 站长工具https://tool.chinaz.com/Tools/urlencode.aspx
C++语言的encode和decode:
1.3 HTTP协议请求与响应格式
1.3.1 HTTP请求
bash
[2025-07-11 01:45:47] [DEBUG] [146171] [TcpServer.hpp] [41] - get a new client,info is : 36.142.137.216:13632
[2025-07-11 01:45:47] [DEBUG] [146177] [HttpServer.hpp] [26] - HttPserver: get a new client: 4addr info:36.142.137.216:13632
GET / HTTP/1.1
Host: 112.126.76.148:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
- 首行:[方法]+[url]+[版本]
- Header:请求的属性,。冒号分割的键值对,每组属性之间使用\r\n分割,遇到空行表示Header部分结束
- Body:空行后面的内容都是Body,Body允许为空字符串,如果Body存在,则在Header中会有一个Content-Length属性来标识Body的长度。

1.3.2 http请求代码
1.3.3 http响应

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

1.4 HTTP的方法
|---------|-------------|-------------|
| 方法 | 说明 | 支持的HTTP协议版本 |
| GET | 获取资源 | 1.0、1.1 |
| POST | 传输实体主体 | 1.0、1.1 |
| PUT | 传输文件 | 1.0、1.1 |
| HEAD | 获得报文首部 | 1.0、1.1 |
| DELETE | 删除文件 | 1.0、1.1 |
| OPTIONS | 询问支持的方法 | 1.1 |
| TRACE | 追踪路径 | 1.1 |
| CONNECT | 要求用隧道协议链接代理 | 1.1 |
| LINE | 建立和资源之间的联系 | 1.1 |
| UNLINE | 断开连接关系 | 1.0 |
最常见的就是GET方法和POST方法。
1.4.1 HTTP常见方法
1.4.1.1 GET方法(重点)
用途:用于请求URL指定的资源。
示例:GET /index.html HTTP/1.1
特性:指定资源经服务器端解析后返回响应内容。
from表单:
cpp
// 要通过历史写的 http 服务器, 验证 GET 方法,这里需要了解一下 FORM 表单的问题
// 这里就要引入 web 根目录, 文件读取的基本操作了
/**
* @brief 读取指定路径的二进制文件内容
* @param path 文件路径
* @return 包含文件内容的字符串(读取失败返回空字符串)
*/
std::string GetFileContentHelper(const std::string &path)
{
// 1. 以二进制模式打开文件
std::ifstream in(path, std::ios::binary);
// 检查文件是否成功打开
if (!in.is_open())
return ""; // 打开失败返回空字符串
// 2. 定位到文件末尾获取文件大小
in.seekg(0, in.end); // 将读取指针移动到文件末尾
int filesize = in.tellg(); // 获取当前指针位置(即文件字节大小)
in.seekg(0, in.beg); // 将读取指针移回文件开头
std::string content; // 创建结果字符串
content.resize(filesize); // 调整字符串大小为文件字节数(分配内存空间)
// 3. 读取文件内容到字符串
// 注意:使用&content[0]直接操作字符串底层数组(C++11保证连续内存)
in.read((char *)content.data(), filesize); // 更推荐使用data()替代c_str()
// 4. 关闭文件流(通常RAII会自动关闭,显式关闭是良好实践)
in.close();
return content; // 返回包含二进制数据的字符串
}
1.4.1.2 POST方法(重点)
用途:用于传输实体的主题。通常用于提交表单数据。
示例:POST /submit.cgi HTTP/1.1
特性:可以发送大量的数据给服务器,并且数据包含在请求体中。
from表单:
1.4.1.3 PUT方法(不常用)
用途:用于传输文件,讲请求报文主体中的文件保存到请求URL指定的位置。
示例:PUT /examplt.html HTTP/1,1
特性:不太常用,但是在某些情况下,如RESTful API中,用于更新资源。
1.4.1.4 HEAD方法
用途:与GET方法类似,但不返回报文主题部分,仅返回响应头。
示例:HEAD /index.html HTTP/1.1
特性:用于确认URL的有效性及资源更新的日期时间等。
bash
// curl -i 显示
$ curl -i www.baidu.com
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: private, no-cache, no-store, proxy-revalidate, notransform
Connection: keep-alive
Content-Length: 2381
Content-Type: text/html
Date: Sun, 16 Jun 2024 08:38:04 GMT
Etag: "588604dc-94d"
Last-Modified: Mon, 23 Jan 2017 13:27:56 GMT
Pragma: no-cache
Server: bfe/1.0.8.18
Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
<!DOCTYPE html>
...
// 使用 head 方法, 只会返回响应头
$ curl --head www.baidu.com
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: private, no-cache, no-store, proxy-revalidate, notransform
Connection: keep-alive
Content-Length: 277
Content-Type: text/html
Date: Sun, 16 Jun 2024 08:43:38 GMT
Etag: "575e1f71-115"
Last-Modified: Mon, 13 Jun 2016 02:50:25 GMT
Pragma: no-cache
Server: bfe/1.0.8.18
1.4.1.5 DELETE方法(不常用)
用途:用于删除文件,是PUT的相反方法。
示例:DELETE .example.html HTTP/1.1
特性:按请求URL删除指定的资源。
1.4.1.6 OPTIONS方法
用途:用于查询针对请求URL指定的资源支持的方法。
示例:OPTIONS * HTTP/1.1
特性:返回允许的方法,如GET、POST等。
不支持的效果:
bash
// 搭建一个 nginx 用来测试
// sudo apt install nginx
// sudo nginx -- 开启
// ps ajx | grep nginx -- 查看
// sudo nginx -s stop -- 停止服务
$ sudo nginx -s stop
$ ps ajx | grep nginx
2944845 2945390 2945389 2944845 pts/1 2945389 S+ 1002 0:00
grep --color=auto nginx
$ sudo nginx
$ ps axj | grep nginx
1 2945393 2945393 2945393 ? -1 Ss 0 0:00
nginx: master process nginx
2945393 2945394 2945393 2945393 ? -1 S 33 0:00
nginx: worker process
2945393 2945395 2945393 2945393 ? -1 S 33 0:00
nginx: worker process
2944845 2945397 2945396 2944845 pts/1 2945396 S+ 1002 0:00
grep --color=auto nginx
// -X(大 x) 指明方法
$ curl -X OPTIONS -i http://127.0.0.1/
HTTP/1.1 405 Not Allowed
Server: nginx/1.18.0 (Ubuntu)
Date: Sun, 16 Jun 2024 08:48:22 GMT
Content-Type: text/html
Content-Length: 166
Connection: keep-alive
<html>
<head><title>405 Not Allowed</title></head>
<body>
<center><h1>405 Not Allowed</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
支持的效果:
bash
HTTP/1.1 200 OK
Allow: GET, HEAD, POST, OPTIONS
Content-Type: text/plain
Content-Length: 0
Server: nginx/1.18.0 (Ubuntu)
Date: Sun, 16 Jun 2024 09:04:44 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
// 注意: 这里没有响应体, 因为 Content-Length 为 0
1.5 HTTP的状态码
|-----|------------------------|---------------|
| | 类别 | 原因短语 |
| 1XX | Informational(信息性状态码) | 接收的请求正在处理 |
| 2XX | Success(成功状态码) | 请求正常处理完毕 |
| 3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
| 4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
| 5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
最常见的状态码,比如200(OK),404(Not Found),403(Forbidden),302(Redirect,重定向),504(Bad Gateway)。
|-----|-----------------------|-------------------------------|
| 状态码 | 含义 | 应用样例 |
| 100 | Continue | 上传大文件时,服务器告诉客户端可以上传 |
| 200 | OK | 访问网站首页,服务器返回网页内容 |
| 201 | Created | 发布新文章,服务器返回文章创建成功的信息 |
| 204 | No Content | 删除文章后,服务器返回"无内容"表示操作成功 |
| 301 | Moved Permanenrly | 网站换一命后,自动跳转到新域名,搜索引擎更新网站链接时使用 |
| 302 | Found或SeeOther | 用户登录成功后,重定向到用户首页 |
| 304 | Not Modified | 浏览器缓存机制,对未修改的资源返回304状态码 |
| 400 | Bad Request | 填写表单时,格式不正确导致提交失败 |
| 401 | Unauthorized | 访问需要登录的页面时,未登录或认证失败 |
| 403 | Forbidden | 尝试访问你没有权限查看的东西 |
| 404 | Not Found | 访问不存在的网页链接 |
| 500 | Internal Server Error | 服务器崩溃或数据库错误导致页面无法加载 |
| 502 | Bad Gateway | 使用代理服务器时,代理服务器无法从上游服务器获取有效响应 |
| 503 | Service Unavailable | 服务器维护或过载,暂时无法处理请求 |
以下是仅包含重定向相关状态码的表格:
|-----|--------------------|----------|--------------------------------|
| 状态码 | 含义 | 是否为临时重定向 | 应用样例 |
| 301 | Moved Permanently | 否(永久重定向) | 网站换域名后, 自动跳转到新域名;搜索引擎更新网站链接时使用 |
| 302 | Found 或 See Other | 是(临时重定向) | 用户登录成功后,重定向到用户首页 |
| 307 | Temporary Redirect | 是(临时重定向) | 临时重定向资源到新的位置(较少使用) |
| 308 | PermanentRedirect | 否(永久重定向) | 永久重定向资源到新的位置(较少使用) |
关于重定向的验证,以301为代表
HTTP状态码301(永久重定向)和302(临时重定向)都依赖Location选项。以下
是关于两者依赖Location选项的详细说明:
HTTP状态码301(永久重定向):
- 当服务器返回HTTP301状态码时,表示请求的资源已经被永久移动到新的位置。
- 在这种情况下,服务器会在响应中添加一个Location头部,用于指定资源的新位置。这个Location头部包含了新的URL地址,浏览器会自动重定向到该地址。
- 例如,在HTTP响应中,可能会看到类似于以下的头部信息:
cpp
HTTP/1.1 301 Moved Permanently\r\n
Location: https://www.new-url.com\r\n
HTTP状态码302(临时重定向):
- 当服务器返回HTTP302状态码时,表示请求的资源临时被移动到新的位置。
- 同样地,服务器也会在响应中添加一个Location头部来指定资源的新位置。浏览器会暂时使用新的URL进行后续的请求,但不会缓存这个重定向。
- 例如,在HTTP响应中,可能会看到类似于以下的头部信息:
cpp
HTTP/1.1 302 Found\r\n
Location: https://www.new-url.com\r\n
总结:无论是HTTP301还是HTTP302重定向,都需要依赖Location选项来指定资
源的新位置。这个Location选项是一个标准的HTTP响应头部,用于告诉浏览器应该
将请求重定向到哪个新的URL地址。
1.6 HTTP常见的Header
- Content-Type:数据类型(text/html等)
- Content-Length: Body的长度
- Host:客户端告知服务器,所请求的资源是在哪个主机的哪个端口上;
- User-Agent:声明用户的操作系统和浏览器版本信息;
- referer:当前页面是从哪个页面跳转过来的;
- Location:搭配3xx状态码使用,告诉客户端接下来要去哪里访问;
- Cookie:用于在客户端存储少量信息.通常用于实现会话(session)的功能;
1.6.1 关于connection报头
HTTP中的Connection字段是HTTP报文头的一部分,它主要用于控制和管理客户端与服务器之间的连接状态
1.6.2 核心作用
**管理持久连接:**Connection字允许客户端和服段还用于管理持久连接(也称为长连接)。持久连接务器在请求/响应完成后不立即关闭TCP连接,以便在同一个连接上发送多个请求和接收多个响应。
1.6.3 持久连接(长连接)
- **HTTP/1.1:**在HTTP/1.1协议中,默认使用持久连接。当客户端和服务器都不明确指定关闭连接时,连接将保持打开状态,以便后续的请求和响应可以复用同一个连接。
- **HTTP/1.0:**在HTTP/1.0协议中,默认连接是非持久的。如果希望在HTTP/1.0上实现持久连接,需要在请求头中显式设置Connection:keep-alive。
1.6.4 语法格式
- Connection:keep-alive:表示希望保持连接以复用TCP连接。
- Connection:close:表示请求/响应完成后,应该关闭TCP连接。
1.6.5 HTTP常见header的表格
|------------------|----------------------------|---------------------------------------------------------------------------------------------------------------------------------|
| 字段名 | 含义 | 样例 |
| Accept | 客户端可接受的响应内容类型 | Accept: text/html,application/xhtml+xml,app lication/xml;q=0.9,image/webp,image /apng,*/*;q=0.8 |
| Accept- Encoding | 客户端支持的数据压缩格式 | Accept-Encoding: gzip, deflate, br |
| Accept- Language | 客户端可接受的语言类型 | Accept-Language: zh- CN,zh;q=0.9,en;q=0.8 |
| Host | 请求的主机名和端口号 | Host: www.example.com:8080 |
| User-Agent | 客户端的软件环境信息 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 |
| Cookie | 客户端发送给服务器的 HTTPcookie 信息 | Cookie: session_id=abcdefg12345; user_id=123 |
| Referer | 请求的来源 URL | Referer: http://www.example.com/previous_pag e.html |
| Content-Type | 实体主体的媒体类型 | Content-Type: application/x-wwwform-urlencoded (对于表单提交) 或Content-Type: application/json (对于JSON 数据) |
| Content-Length | 实体主体的字节大小 | Content-Length: 150 |
| Authorization | 认证信息, 如用户名和密码 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== (Base64 编码后的用户名:密码) |
| Cache-Control | 缓存控制指令 | 请求时: Cache-Control: no-cache 或 Cache-Control: max-age=3600; 响应 时: Cache-Control: public, maxage=3600 |
| Connection | 请求完后是关闭还是保持连接 | Connection: keep-alive 或Connection: close |
| Date | 请求或响应的日期和时间 | Date: Wed, 21 Oct 2023 07:28:00 GMT |
| Location | 重定向的目标 URL(与 3xx 状 态码配合使用) | Location: http://www.example.com/new_location .html (与 302 状态码配合使用) |
| Server | 服务器类型 | Server: Apache/2.4.41 (Unix) |
| Last-Modified | 资源的最后修改时间 | Last-Modified: Wed, 21 Oct 2023 07:20:00 GMT |
| ETag | 资源的唯一标识符, 用于缓存 | ETag: "3f80f-1b6-5f4e2512a4100" |
| Expires | 响应过期的日期和时间 | Expires: Wed, 21 Oct 2023 08:28:00 GMT |
1.7 最简单的HTTP服务器
实现一个最简单的HTTP服务器,只在网页上输出"helloworld";只要我们按照HTTP协议的要求构造数据,就很容易能做到;
cpp
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void Usage()
{
printf("usage: ./server [ip] [port]\n");
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage();
return 1;
}
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
perror("socket");
return 1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port = htons(atoi(argv[2]));
int ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0)
{
perror("bind");
return 1;
}
ret = listen(fd, 10);
if (ret < 0)
{
perror("listen");
return 1;
}
for (;;)
{
struct sockaddr_in client_addr;
socklen_t len;
int client_fd = accept(fd, (struct sockaddr *)&client_addr,
&len);
if (client_fd < 0)
{
perror("accept");
continue;
}
char input_buf[1024 * 10] = {0}; // 用一个足够大的缓冲区直接把数据读完.
ssize_t read_size = read(client_fd, input_buf,
sizeof(input_buf) - 1);
if (read_size < 0)
{
return 1;
}
printf("[Request] %s", input_buf);
char buf[1024] = {0};
const char *hello = "<h1>hello world</h1>";
sprintf(buf, "HTTP/1.0 200 OK\nContent-Length:%lu\n\n%s",
strlen(hello), hello);
write(client_fd, buf, strlen(buf));
}
return 0;
}
编译, 启动服务. 在浏览器中输入 http://[ip]:[port], 就能看到显示的结果 "Hello World"


备注:
此处我们使用 9090 端口号启动了 HTTP 服务器. 虽然 HTTP 服务器一般使用 80 端口,
但这只是一个通用的习惯. 并不是说 HTTP 服务器就不能使用其他的端口号
使用 chrome 测试我们的服务器时, 可以看到服务器打出的请求中还有一个 GET /favicon.ico HTTP/1.1 这样的请求.同学们自行查找资料, 去理解 favicon.ico 的作用
实验
把返回的状态码改成 404, 403, 504 等, 看浏览器上分别会出现什么样的效果.
2. 笔记
2.1 URL

请求的资源,本质就是文件不就是Linux下的路径结构吗
2.2 前置背景理解
- 人上网不就是把我的数据给别人,或者别人的数据给我,这部就是I0吗?我们上网的所有行为,本质就是在做I0
- 图片、适配、文本等等称为资源
- 我得先确认我要的资源在哪一台服务器上(网络,IP),还得知道在什么路径下(系统,路径),这就叫做统一资源定位符URL,port帮助我们通信,实际上ip+port。
- url中/不一定是根目录,其实叫web根目录,两者不一定是同一个
url的意思就是我们想通过http或者https想使用这个协议,在gitee.com这台机器上,把/Axurea/c-plus-plus/tree/master/这个路径下的2024_10_18_stack_queue文件这个资源给我,服务器收到这个请求后就可能把这个路径下的文件打开,然后把文件资源给我推回来,推回来的时候是需要有端口号的,而我们ur1是没有体现端口号的。
不仅要告诉我什么协议,还得告诉我通过哪个端口号给你推回去。
成熟的应用层协议,往往和端口号是关联的!!!只要知道应用层协议名,它的端口号就是定死的,比如说http端口哈默认就是80,https端口号就是443,比如ssh、mysql,最典型的点口号往往都是1024以内的数字。
上面没有指明端口号是因为http和https只要指明协议,端口号也就固定了,所以在ur1当中端口号不用体现

第一个就是我们所采用的协议,或者方案名,://就是固有格式,user:pass是登录信息,我们早期的url是把我们的登录信息放在://后面的,但现在的http已经不这么做了,现在已经见不到了,现在的登录信息已经被http隐藏起来了,www.exaple.jp是服务器的地址,:80现在没了,只要协议指明,端口号其实已经固定了,这个端口号可以省略了,浏览器会自己进行端口号的缺省填充,不管是谁要访问目标服务器必须要有端口号,这是少不了的,ur1没有端口号是因为看到的是没有端口号,但浏览器会根据协议名自动把端口号加上,这是必须的,给我们形成的url端口号往往是省略的,dir/index/htm是文件路径。
http也可以传参,后面带?意思是?左边是访问的资源,右边是我们要传递的参数,uid=1就是k,v值,#后面是片段标识符,一个网页中我们有片段标识符,比如说有图片,图片1、2、3,此时我们可以ur1不更新,而是片段标识符再更新,这个现在用的也很少了,
这是较为完整的url。

如果有多组k,v,就用&符号级联起来。IO的明显体现。
像/?:等这样的字符,已经被ur1当作特殊意义理解了,因为这些字符不能随意出现,

下面我们这样搜索一下

我们发现我们的搜索关键字变成了%3A%2F%2F%3D%3F%2F%26,这个就是把我们刚刚搜索的关键词进行了编码,是为了避免这种字符和url中的特殊字符冲突,进而会导致url解析失败,所以这个过程就叫做urlencode。
服务器端收到了编码的ur1之后,服务器端要解析,服务器端搜索的时候还是想搜://=?/&,而不想搜%3A%2F%2F%3D%3F%2F%26所以我们还要把%3A%2F%2F%3D%3F%2F%26这种编码转化成://=?/&这个样子,这个过程就是ur1decode,叫做解码。
那么是怎么转的呢?
我们输入的每个字符都是ASCII码值,我们要将被转码的字符直接转为16进制,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式,对于汉字也是需要编码的,因为汉字本身不是以ASCII码值的编码方式,它是以UTF-8或者unicode编码方式,汉字也会被转成3~4个不定长的二进制,把二进制四个做一个转成十六进制,没两个做以为拼上%。
这个工作我们不需要自己做,网上可以找c++urlencodeurldecode代码。
2.3 http协议的宏观格式
http是应用层协议,是基于TCP协议的。
1、协议的格式是什么?
2、收发完整性---------tcp是面向字节流的。
第一行为请求行,请求和以\r\n结尾,第一个字段为请求方法(Get,Post),第二行为请求的url,中间以1个空格为单位,
url一般都是要请求的目标服务器的资源路径,再下来的就是请求http的协议的版本,最后都是以\r\n结尾。
第二部分为http的请求报头,但是它的请求报头也包括请求行,请求报头里面包含的是多行内容

如果他是一个类,是一个结构化的东西,它有自己的请求行,请求报头,空行或者正文,无非就是多行字符串,而字符串当中都有统一的标识符,叫做\r\n,以行为单位陈列的,所以我们所谓的http请求要做序列化其实是http协议自己做的,自己以\r\n,包括空行,作为特殊字符把一个个的请求包括空行之前的内容我们全部都合并成一个长字符串罢了,只不过字符串的分隔符是\r\n,所以我们可以把http请求整体作为一个长字符串给对方发过去。我们之前写的网络版本计算器就是这么写的,读取第一个字段就是按照\r\n区分正文的长度的,正文没有\r\n结尾,当我们把对应的请求合并之后,整个的前面的请求行,请求报头是不存在所谓的\r\n的,一直读,只要读到空行就知
道把前面的请求行和请求报头全读完了,而他们每一个都是按行为单位的,我们读到空行之前的就是http协议的报头部分,空行之后的就是用户的请求正文。所以我们是能做序列化的,http自己做了。
如何反序列化呢?http请求拿到了对应的字符串请求之后,我们是边读取边解析,解析有\r\n就往下走,没有就继续读取,所以就可以按行去读,直到读到空行,按行读的话每一个请求报头全知道,就能够直接做反序列化了。
我怎么知道正文部分有多长呢,正文部分没有\r\n结尾?
在http的请求报头当中,它的报头中的属性有一个字段,Content-Length:xxx\r\n,如果你今天携带了请求正文的话,在它的请求报头里有一个固定的这样的属性,代表的就是正文部分的长度,一旦http收到之后,因为有空行的存在,上层读的时候就按行,一定能把空行,包括空行在内的能全读完,而且以行为分隔符,就能把每个字段依次提取出来,一段提取出来,里面就有一个Content-Length,就得到了正文部分的长度,此时再读取正文部分就可以按长度进行读取了。
http能自己完成协议的序列化和反序列化,所以为什么http要自己做。
http是个独立协议,不想依赖与任何库,因为http再网络中比较偏底层的策略了,是我们对应的一套解决方案了,不想依赖任何库,它的序列化反序列化自己完成,而且做法非常简单,这就是http请求的宏观格式。http请求里面的内容全都是字符串,主体是以行作为分隔符来进行区分报头和有效载荷,这种策略既能保证写入时做序列化,读取的时候也能很容易的做反序列化。
2.4 HttpRequest和HttpResponse

应答的格式和请求的格式是极度类似的,因为只要是极度类似的,就意味着大家的序列化和反序列化的做法基本上可以做到完全一样,写一套方法就可以请求,可以处理,请求和响应,
状态码和状态码描述是什么?
当你再去请求的时候,一旦请求一个资源,最终会应答得到一个响应,只要请求必会应答,哪怕请求是错误的,也要应答,其中也有可能请求的资源根本就不存在,服务器出问题了等等,所以状态码表示的是你请求时的一些状态,404表示你所请求的资源不存在,状态码描述就叫NotFound,状态行里面的状态码状态描述就表示你本次请求的一些状态。
浏览器就是http的客户端,它用的就是http或者https,如果我们将来写一个服务器,这个服务器就按照上面的http宏观格式来构建对应的请求和应答的话,我们就可以把我们想要的信息直接呈现再浏览器上了,只要遵守协议,自己搞一套http服务,当浏览器请求时,只要给我的数据格式是这个样子的,浏览器就能做解释。
telnet连接响应:

浏览器的方式请求:


- HTTP大小写不敏感
- HTTP主流的版本是1.1
- 200表示成功

你在搜索的时候会把你的信息发给目标网站,http请求里面是包含User-Agent字段的,人家就能根据对应的相关属性字段来推出对应的东西,

在http请求当中,我们ip地址往后(端口号是我们显示的写出来了,不想写就需要指明协议了),从/往后的内容最后都会称为url的一部
分,这表明我们要请求的是一个什么资源,在往后跟的是HTTP1.1,再往后走就是HTTP的正常请求了。
在我们HTTP请求的时候,浏览器会自动的把我们/之后的内容全部作为HTTP请求的第二部分,叫做uri来统一构建HTTP请求,发送给我们
我们要请求的资源往往是uri所指明的路径来决定的,
- url当中开始的/叫做web根目录,
- 对于1个http来讲,必须得在代码当中构建一个httpserver自己的家目录,然后把我们要的网页信息放到家目录里面,

这个目录就是httpserver所对应的家目录
任何的网站或者是后端服务,如果想被别人进行访问,
默认请求的都是一个/,当只有一个/时,就决定了我们的站点就必须得有一个默认首页的东西,叫做index.htm或者index.html,
1.这个东西就是对应的默认首页,wwwroot不会暴露出来,所有我们不知道,web根目录名字是被隐藏起来的,我们看不到,所以这个名字我们可以随便去取。
2.index.htm或者index.html在大部分服务中都是约定俗成的,比如www.baidu.com拿到的是百度的首页,但是www.baidu.com/index.html拿到的也是百度的首页


http请求当中对应的url或者uri这一部分表明用户想通过这个http请求想获得你服务器上面的什么资源,而/表示请求默认首页,如果是index.html那么就应该从wwwroot这个目录开始,
如果请求的是根目录,我们就应该把wwwroot/index.html这个文件的内容返回
用户访问的资源都在首页中,所以我们默认就要拼上wwwroot/或者自定义的web根目录的路径,
HTTP中的version:
我们在请求和应答这里都看到了HTTP版本,那这两个版本是什么意思呢?
HTTP请求的版本指的是浏览器采用的HTTP协议的版本,应答所对应的HTTP版本代表的是服务器所用的HTTP版本,HTTP作为一个成熟的协议,双放在进行请求和应答交换的时候,也要交换一下双方的版本信息,因为双方客户点和服务器的版本有可能不一致,

这是百度的404错误页面,也就是说就算客户端访问的资源不存在,我们也要给返回一个404的页面

页面跳转本质就是向HTTP服务发起HTTP请求
HTTP叫做超文本传输协议吗,它的本质就是帮我们把我们的一个特点目录下的内容给我们进行返回。


2.5 重定向


Referer字段:

重定向:



重定向时一定要带全路径
临时重定向和永久重定向效果是完全一样的。
2.6 GET方法


2.7 POST方法

POST方法传参是通过正文传递参数的
GET方法和POST方法的区别:
- GET方法通常获取网页内容,POST通常用来上传数据
- GET方法也可以传参,通过URL传参,POST传参通过body传参
如果是登录的话特别不建议用GET方法,因为GET方法会把我们的用户名和密码直接回显到URL中,GET方法传参的话可以传一些不怎么私密的数据。
URL是一个链接,但这个链接长度上不能太长,所以传递的参数的个数一定是有上限的,而正文部分传参的话参数可以变的非常的长
GET方法和POST方法都能上传数据,POST方法传数据可以传更多数据,因为是用body传递数据的,POST方法传递参数是通过正文的,所以用户看不到,作为普通用户我们不知道http请求的正文部分的,因为被浏览器藏起来了,但是绝对不能认为POST方法比GET方法安全,只能说
POST方法比GET方法传参更私密一些。

表单中怎么没有method呢?
如果from表单中没有method属性,在点击提交的时候,默认用的就是GET方法,

所以我们在搜东西的时候,把提交的参数就回显在了ur1上面,百度搜索提参默认用的就是GET方法。
2.8 bind失败原因以及解决方法

2.8.1 bind失败原因
服务器有时候能起来,有时候会出现bind失败,主要原因就是在断开的时候有可能浏览器还在连着服务器,一旦服务器断开了,服务器作为主动断开的一方,在TCP协议那会进行四次挥手,而挥手的时候在内核当中服务端会处于一个timewait的状态,这个持续的时间一般都是60到120秒。
2.8.2 解决方法

要设置一下套接字的一些相关选项

2.9 Cookie
Cookie是网站为了"记住你"而存放在你浏览器里的小文本文件。它们让登录保持有效、记住你的偏好、保存购物车内容,但也可能被用于跟踪你的浏览活动以投放广告。现代隐私法规要求网站征得你的同意才能使用某些类型的Cookie,并且浏览器提供了工具让你管理它们。
比如说你第一次上b站,b站不认识你,你登陆了了一些,b站就会在你的浏览器中写入相关的信息,等你过了很长时间去访问b站的时候,b站还认识你,这是因为你访问b站的时候在请求信息中带了上次b站服务器向浏览器中写的信息了。
Cookie在浏览器中有两种存在形式,1.内存级 2.文件级
内存级的话你浏览器只要不关,你访问目标网站就一直认识呢,但是你关闭了浏览器,下次打开目标网站就要重新登录了文件级的话就算你关了浏览器,下次访问目标网站也是认识你的

这就是b站给我写的Cookie信息,当我把这两个删干净之后b站也就不认识我了。

这个功能就是基于Cookie的会话保持功能。
http协议是无连接、无状态的协议,有的人会说http是无连接的不对,因为http要先建立连接,是基于TCP的,其实TCP是面向连接的,HTTP不是,HTTP请求和应答的时候和UDP是一样的,直接就发起Request和Response。
HTTP是无连接的,也就是不需要处理连接问题,由TCP来做。
HTTP是无状态的,现在你请求一个网站的首页,过后你在请求,HTTP不知道你刚才请求过,HTTP也不管,你要请求就请求,也不会记录历史的请求。
正是因为http是无状态的,你访问b站或者是有会员的网站,你每次切换页面都要登录,那么你会抓狂的,正是因为有Cookie技术,把用户的私密信息写道Cookie中,然后通过http提交给服务器,然后服务器端就指导这个Cookie信息了,就会在后端进行身份认证了,这个我们就称为Cookie,从第二次往后再也不需要用户进行任何的登录相关的信息,
如果有黑客把你的Cookie文件盗走,放在自己的电脑上,学着你访问目标网址,此时会出现什么问题?
这个服务端就把这个黑客当成你了,比如说qq,qq客户端底层是https,qq也有记住密码,保存在特定的目录下,被盗走的话别人也能登录你的qq。
我们现在的计算机中用会话保持,只用一个Cookie是严重不够的,

就算这样,我们也避免不了Cookie信息被盗取,但是这种方法更安全,
- 不会造成用户信息的泄漏了
以前这个管理工作是由客户端管理的,现在是由服务端统一管理, - 服务端就可以设计各种策略,来进行防止黑客进行恶意操作,比如ip变更快,上一秒在上海,下一秒缅甸,这个时候session_id全都会失效的,服务端可以做ip地址的检查,一瞬间让session_id失效。
所以session+Cookie才是我们当前HTTP会话保持的一个对应的关键功能。
2.10 connection报头
建立一个连接,就帮助用户获取一个资源,不管是网页还是图片,这种短链接方案获取一张网页要构建四次链接,那么如果一张网页有40个资源呢?如果有400个资源呢?那么我们的服务器光获取首页,客户端就要发400个请求,那么我们的服务器压力太大了,每一次都要基于TCP建立链接,所以HTTP在1.0之前只支持短链接,那个时候网络资源比较少,而现如今大部分浏览器用的是1.1,而在1.1中新增了一个字段叫connection,connection是HTTP请求报头和响应报头的一部分,主要控制客户端和服务器的连接状态。
如果connection携带了字段keep-akive,意味着当前HTTP客户端给服务器说我的客户端支持长连接,客户端连接服务器把对应的首页响应回来的时候,服务器先不要断开这个连接,因为客户端还有可能基于这个连接继续向服务端发起HTTP请求。所以,我们一旦支持connection,双方就可以在一条TCP连接上把我们本次HTTP请求需要的所有资源通过一条连接全部发送回给客户端,这种技术叫做HTTP长连接,keep-alive表示长连接,close表示短连接
这个工作我们做过吗?做过,当时我们在写网络版本计算器的时候。服务端读取请求就是一个死循环。直到你关闭链接,一直给客户端建立连接,建立连接之后,一直给可以允许客户端给我发送我们的数据计算的请求。那么当时我们就已经用起来了,那个叫做长链接。而HTTP已经一条链接发起请求,作为我们对应的服务端,完全能处理。我就可以把你的请求按行读,按行读,直到读到空行,读到空行之后,提取Content-Legth,如果没有或者为0,代表没有正文即一个请求读完。如果有正文,有Content-Legth,我就根据Content-Legth指定的长度继续从接入缓冲区里继续读取,比如100字节。那么此时我就保证能把一个完整的http请求读完,那么我就可以构建HttpResponse,然后做反序列化了。同时,下一次再循环读的时候,那么不就是你给我发来的第二个请求的格式吗?我得照猫画虎做刚才的工作。所以支持长链接,它的核心在于你能不能让你的服务处理TCP读取的字节流问题。而我们之前已经做过了。
2.11 findler工具原理

浏览器的所有请求叫交给findler,所以findler就能专门抓取http请求了,findler是抓门抓取http请求的

http本身是不安全的,因为它没有加密。

在我们的http和传输层之间加了一层TLS/SSL,我们的请求构建好,把请求的正文部分交给TLS/SSL,形成一个加密之后的请求,一旦这个请求是加密的,将来就直接把我们的加密数据交到对方,对方收到一定是加密层先收到,然后再解密,再交到http层,其实https还是http,之不过它再交到下层时使用的是加密和解密的技术。
3. 代码&&板书
3.1 板书
3.2 代码
附录:
HTTP 历史及版本核心技术与时代背景
HTTP(Hypertext Transfer Protocol, 超文本传输协议) 作为互联网中浏览器和服务器间通信的基石, 经历了从简单到复杂、 从单一到多样的发展过程。 以下将按照时间顺序, 介绍 HTTP 的主要版本、 核心技术及其对应的时代背景。
HTTP/0.9
核心技术:
- 仅支持 GET 请求方法。
- 仅支持纯文本传输, 主要是 HTML 格式。
- 无请求和响应头信息。
时代背景:
- 1991 年, HTTP/0.9 版本作为 HTTP 协议的最初版本, 用于传输基本的超文本HTML 内容。
- 当时的互联网还处于起步阶段, 网页内容相对简单, 主要以文本为主。
HTTP/1.0
核心技术:
- 引入 POST 和 HEAD 请求方法。
- 请求和响应头信息, 支持多种数据格式(MIME) 。
- 支持缓存(cache) 。
- 状态码(status code) 、 多字符集支持等。
时代背景:
- 1996 年, 随着互联网的快速发展, 网页内容逐渐丰富, HTTP/1.0 版本应运而生。
- 为了满足日益增长的网络应用需求, HTTP/1.0 增加了更多的功能和灵活性。
- 然而, HTTP/1.0 的工作方式是每次 TCP 连接只能发送一个请求, 性能上存在一定局限。
HTTP/1.1
核心技术:
- 引入持久连接(persistent connection) , 支持管道化(pipelining) 。
- 允许在单个 TCP 连接上进行多个请求和响应, 提高了性能。
- 引入分块传输编码(chunked transfer encoding) 。
- 支持 Host 头, 允许在一个 IP 地址上部署多个 Web 站点。
时代背景:
- 1999 年, 随着网页加载的外部资源越来越多, HTTP/1.0 的性能问题愈发突出。
- HTTP/1.1 通过引入持久连接和管道化等技术, 有效提高了数据传输效率。
- 同时, 互联网应用开始呈现出多元化、 复杂化的趋势, HTTP/1.1 的出现满足了这些需求。
HTTP/2.0
核心技术:
- 多路复用(multiplexing) , 一个 TCP 连接允许多个 HTTP 请求。
- 二进制帧格式(binary framing) , 优化数据传输。
- 头部压缩(header compression) , 减少传输开销。
- 服务器推送(server push) , 提前发送资源到客户端。
时代背景:
- 2015 年, 随着移动互联网的兴起和云计算技术的发展, 网络应用对性能的要求越来越高。
- HTTP/2.0 通过多路复用、 二进制帧格式等技术, 显著提高了数据传输效率和网络性能。
- 同时, HTTP/2.0 还支持加密传输(HTTPS) , 提高了数据传输的安全性。
HTTP/3.0
核心技术:
- 使用 QUIC 协议替代 TCP 协议, 基于 UDP 构建的多路复用传输协议。
- 减少了 TCP 三次握手及 TLS 握手时间, 提高了连接建立速度。
- 解决了 TCP 中的线头阻塞问题, 提高了数据传输效率。
时代背景:
- 2022 年, 随着 5G、 物联网等技术的快速发展, 网络应用对实时性、 可靠性的要求越来越高。
- HTTP/3.0 通过使用 QUIC 协议, 提高了连接建立速度和数据传输效率, 满足了这些需求。
- 同时, HTTP/3.0 还支持加密传输(HTTPS) , 保证了数据传输的安全性。