【Linux 26】应用层协议 - HTTP
文章目录
- 🌈 一、HTTP 协议介绍
- 🌈 二、URL 的概念及组成部分
- ⭐ 1. 协议方案名
- ⭐ 2. 登录信息
- ⭐ 3. 服务器地址
- ⭐ 4. 服务器端口号
- ⭐ 5. 带层次的文件路径
- ⭐ 6. 查询字符串
- ⭐ 7. 片段标识符
- 🌈 三、URL 的编码和解码
- ⭐ 1. URL 对特殊字符的编码转义
- ⭐ 2. 为什么要对特殊字符进行编码转义
- ⭐ 3. 在线编码 / 解码工具
- 🌈 四、HTTP 协议的格式
- ⭐ 1. HTTP 协议的请求格式
- 🌙 1.1 HTTP 请求协议格式
- 🌙 1.2 将 HTTP 请求报文的报头和有效载荷分离
- 🌙 1.3 获取浏览器的 HTTP 请求报文
- 🌙 1.4 请求行中 url 表示的是 web 根目录下的资源路径
- ⭐ 2. HTTP 协议的响应格式
- 🌙 2.1 HTTP 响应协议格式
- 🌙 2.2 将 HTTP 响应报文的报头和有效载荷分离
- 🌙 2.3 构建 HTTP 响应报文给浏览器
- 🌈 五、HTTP 常见请求方法
- ⭐ 1. GET 方法(重要)
- ⭐ 2. POST 方法(重要)
- ⭐ 3. PUT 方法 (少用)
- ⭐ 4. HEAD 方法
- ⭐ 5. DELETE 方法 (少用)
- ⭐ 6. OPTIONS 方法
- 🌈 六、HTTP 的状态码
- ⭐ 1. HTTP 状态码分类
- ⭐ 2. HTTP 常见状态码
- ⭐ 3. Redirection 重定向状态码
- 🌈 七、HTTP 的常见报头
- ⭐ 1. Host 报头
- ⭐ 2. User-Agent 报头
- ⭐ 3. Referer 报头
- ⭐ 4. Cookie 报头
- 🌙 4.1 Cookie 的概念
🌈 一、HTTP 协议介绍
- 超文本传输协议 HTTP (Hyper Text Transfer Protocol) 是一个简单的 请求 - 响应 协议。
- HTTP 协议通常运行在 TCP 之上,即HTTP 是基于 TCP 实现的应用层协议。
- HTTP 协议定义了客户端 (如浏览器) 与服务器之间如何通信,用以交换或传输超文本。
- 超文本:视频、音频、图片、图标、HTML 文件等都叫超文本。
- HTTP 协议是客户端与服务器之间通信的基础。客户端通过 HTTP 协议向服务器发送请求,服务器在收到请求后处理请求并返回响应。
- HTTP 是一个无连接、无状态的协议,即每次请求都需要建立新的连接,且服务器不会保存客户端的状态信息。
- HTTP 主要用于交换和传输超文本,它不需要记住客户端的状态信息。
- 诸如登录、注册这种需要维护用户状态信息的场景有 HTTP 参与,但 HTTP 本身并不会为了维护状态做更多工作。
🌈 二、URL 的概念及组成部分
- 平常所说的网址就 是统一资源定位符 URL (Uniform Resource Lacator) 是因特网的万维网服务程序上用于指定信息位置的表示方法。
URL 的构成部分
⭐ 1. 协议方案名
- 协议方案名 (协议名称)
http://
表示在请求时需要使用的协议,通常使用的是 HTTP 或 HTTPS 协议。- 由于对安全更加重视,因此当前的互连网使用的全都是 HTTPS 协议,但在学习上还是要先学习 HTTP。
- HTTPS 是以安全为目标的 HTTP 通道,用的主要还是 HTTP 协议,在 HTTP 的基础上通过传输加密和身份认证保证了传输过程的安全性。
常见的应用层协议
协议名简称 | 协议名全称 | 协议名中文 |
---|---|---|
DNS | Domain Name System | 域名系统 |
FTP | File Transfer Protocol | 文件传输协议 |
TELNET | Telnet | 远程终端协议 |
HTTP | Hyper Text Transfer Protocol | 超文本传输协议 |
HTTPS | Hyper Text Transfer Protocol over SecureSocket Layer | 安全数据传输协议 |
SMTP | Simple Mail Transfer Protocol | 电子邮件传输协议 |
POP3 | Post Office Protocol - Version 3 | 邮件读取协议 |
SNMP | Simple Network Management Protocol | 简单网络管理协议 |
TFTP | Trivial File Transfer Protocol | 简单文件传输协议 |
⭐ 2. 登录信息
- 登录认证信息
usr:pass
包括登录用户的用户名和密码。 - 现今,在大多数的 URL 网址中,已经省略了该部分,转而将登录信息通过其他方案交付给服务器。
- 例:由客户端界面上的输入用户名和用户密码、手机号和验证码、扫码等方式来替代。
⭐ 3. 服务器地址
- 服务器地址
www.example.jp
也叫做域名,常见的域名有 www.baidu.com、www.qq.com 等。 - 虽然在互联网中,用 IP 地址来标识公网内的唯一的一台主机。但 IP 地址本身不方便让用户记忆和理解,因此就采取域名的方式来代替 IP 地址的功能,方便用户通过域名访问到对应服务器。
- 会自动将域名转换成对应服务器的 IP 地址。
1. 查看域名所对应的 IP 地址
- 可以通过
ping 域名
的方式获取指定域名所对应的 IP 地址。- 例:通过 ping 命令,获取 www.baidu.com、www.qq.com 这两个域名所对应的 IP 地址。
2. 域名等价于 IP 地址
- 在计算机中,既可以使用域名访问服务器,也可以使用 IP 地址访问服务器。
- 但 URL 是为了方便用户而出现的,因此在 URL 中通过域名来映射对应服务器的 IP 地址。
⭐ 4. 服务器端口号
80
表示的是对应服务器中提供相关服务的进程的端口号。- HTTP 协议和套接字编程都是位于应用层,在进行套接字编程时需要给服务器绑定对应的 IP + port,那么 HTTP 这个应用层协议自然也需要有明确的端口号。
1. 常见协议所对应的端口号
常见协议名 | 对应的固定端口号 |
---|---|
HTTP | 80 |
HTTPS | 443 |
SSH | 22 |
2. URL 一般会省略服务器端口号
- 协议名称与端口号强关联,知名协议的端口号都是固定的。在使用某协议时,该协议实际就在为用户提供服务。
- 例:类似于现实中的 110、120 等电话与其提供的服务。端口号与协议名称 (提供的服务) 强关联,我说打 110 就知道是要报警,我说报警就知道要打 110。
- 由于常用服务与端口号之间的对应关系明确,因此在使用某种协议是不需要特别指出该协议所对应的端口号的。
- IP 地址和端口号缺一个都不可能访问到服务器。平时用的 URL 会省略服务器端口号,但在发起 HTTP 请求时,会自动拼接 80 这个端口号。
- 发起其他协议请求时也会自动拼接与该协议所对应的端口号。
⭐ 5. 带层次的文件路径
- 带层次的文件路径
/dir/index.htm
表示的是要访问的资源的所在路径,即该主机上唯一的文件资源。- 域名 + 路径 = 互联网中唯一的一个文件资源,这就是统一资源定位符 URL 的意思。
- 资源并不是凭空生成的,从网络上获取资源本质上就是从对应服务器中获取资源,并通过网络拿到自己手上。
- 后端服务器也是一台主机,用的基本上都是 Linux 系统。访问服务器获取到的各种资源在 Linux 看来都是文件资源,是文件资源就会有该文件的存储路径。访问服务器获取资源时就需要指明你要访问的是对应服务器主机中的哪个路径下的文件资源 (得先找到对应资源)。
- 获取的这个资源可以是网页、视频、音频、图像、文本等。
- 此外,文件路径的路径分隔符采用的是 Linux 下的
/
而不是 Windows 下的\
就更加证实了实际上很多服务都部署在 Linux 上。
举个例子
-
在打开浏览器输入百度的域名 (www.baidu.com) 后,浏览器就会获取到百度的首页信息。
- 本质上就是获取服务器主机中的一个关于百度首页的 html 文件。
-
在发起网页请求时,获取服务器主机中的一个关于百度首页的 html 文件,然后浏览器对这个 html 文件进行解释,最后就呈现出了对应的网页。
⭐ 6. 查询字符串
- 位于 ? 后面的查询字符串
uid=1
表示的是请求服务时额外提供的参数,这些参数通常以键值对的形式呈现,通过 & 分隔。
举个例子
- 在浏览器上搜索 HTTP,在 URL 的众多参数中,有一个 wd (word) 参数表示搜索时的搜索关键字,此处的 wd=HTTP 就表示当前的搜索关键字是 HTTP。
- 通过该例,说明能够通过 URL 进行用户数据传输。
⭐ 7. 片段标识符
- 位于 # 后面的片段标识符
ch1
是对资源的部分补充。
🌈 三、URL 的编码和解码
⭐ 1. URL 对特殊字符的编码转义
- URL 会将像
/?:
这样的字符当作特殊意义理解,URL 在呈现时,会对这些特殊字符进行转义。
URL 对特殊字符的转义规则
- 将需要转码的字符转为 16 进制,然后从右到左,取 4 位 (不足 4 位的直接处理),每 2 位当作 1 位,在前面加上 %,编码成 %XY 的格式。
- 例:在浏览器中搜索 C++ 时,由于字符 + 在 URL 中属于特殊字符,而字符 + 在转成 16 进制后的值位 0x2B,因此会将字符 + 编码成 %2B。
URL 的解码
- 编码就是将特殊字符根据对特殊字符的转义规则进行转义,解码就刚好反过来而已,就不细讲了。
⭐ 2. 为什么要对特殊字符进行编码转义
- 通过前面介绍的 URL 构成部分的查询字符串可知,URL 会通过 wd=关键词 的方式将搜索的关键词与自己拼接。
- 如果搜索的关键词包含这些特殊字符,不进行转义的话,这些特殊字符就可能会干扰到当前 URL,导致 URL 的链接出现问题。
⭐ 3. 在线编码 / 解码工具
- http://tool.chinaz.com/tools/urlencode.aspx
🌈 四、HTTP 协议的格式
⭐ 1. HTTP 协议的请求格式
🌙 1.1 HTTP 请求协议格式
- 请求行:请求方法 + URL + HTTP 版本,这里的 URL 表示的是请求的资源的所在路径。
- 请求报头:请求的属性,这些属性都以
key: value
的形式按行陈列。 - 空行:遇到空行就表示请求报头结束。
- 请求正文:一般为用户的相关信息或数据,允许为空字符串,如果请求正文存在,则在请求报头中添加一个
content-length
属性来标识请求正文的长度。
🌙 1.2 将 HTTP 请求报文的报头和有效载荷分离
- 当应用层收到一个 HTTP 请求时,需要将该 HTTP 请求的报头与有效载荷进行分离。
- 对于一个 HTTP 请求,报头 = 请求行 + 请求报头;有效载荷 = 请求正文。
- 可以根据 HTTP 请求中的 空行 来将报头和有效载荷分离。当服务器收到一个 HTTP 请求报文时,按行读取该报文,如果读取到空行则报头读取完毕。
- 从代码的角度看,回车 + 换行 = \n,如果连续读取到两个 \n,则说明已将报头读取完毕,剩下的都是有效载荷。
🌙 1.3 获取浏览器的 HTTP 请求报文
- 在网络协议栈中,应用层的下一层是传输层,HTTP 协议的底层通常采用的是传输层的 TCP 协议。因此可以使用 socket 套接字编写一个 TCP 服务器,然后启动浏览器访问这个服务器。
- 由于服务器采用的是 TCP 套接字来读取浏览器发来的 HTTP 请求,没有在服务端的应用层解析这个 HTTP 请求,此处就通过打印浏览器发送来的 HTTP 请求,来查看 HTTP 请求的基本构成。
编写简易的 TCP 服务器
- 该服务器只需要将从浏览器发来的 HTTP 请求打印到屏幕上即可。
#include <string>
#include <fstream>
#include <cstring>
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>int main()
{// 1.创建套接字int listen_sock = socket(AF_INET, SOCK_STREAM, 0);if (listen_sock < 0){std::cerr << "create socket error!" << std::endl;return 1;}// 2.绑定套接字struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(8888);local.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0){std::cerr << "bind socket error!" << std::endl;return 2;}// 3.监听套接字if (listen(listen_sock, 5) < 0){std::cerr << "listen socket error!" << std::endl;return 3;}// 4.启动服务器struct sockaddr peer;bzero(&peer, sizeof(peer));socklen_t len = sizeof(peer);// 5.接收请求int sock = accept(listen_sock, (struct sockaddr *)&peer, &len);if (0 == fork()){close(listen_sock);if (fork() > 0)exit(0);char request[1024];recv(sock, request, sizeof(request), 0); // 读取HTTP请求std::cout << "------------------------------" << std::endl;std::cout << request << std::endl;std::cout << "------------------------------" << std::endl;close(sock);exit(0);}close(sock);waitpid(-1, nullptr, 0);return 0;
}
🌙 1.4 请求行中 url 表示的是 web 根目录下的资源路径
- 在请求报文的请求行中的 url 表示的是想要访问的资源的所在路径。
- 而 url 当中的
/
不是云服务器上的根目录,而是 web 根目录。 - 这个 web 根目录可以指定为你机器上的任何一个目录,不一定就非得是 Linux 的根目录。
- http 会设置一个前缀路径表示 web 根目录,当接收到请求时,会将这个前缀路径和 url 所表示的资源路径连接起来。
- 在进行网页请求时,如果不指明请求的资源的所在路径,默认访问的是目标网站的首页,即 web 根目录下的 index.html 文件。
- 在进行网页请求时,如果用户指明了请求的资源所在路径,就会直接到 web 根目录下访问指定资源。
⭐ 2. HTTP 协议的响应格式
🌙 2.1 HTTP 响应协议格式
- 状态行:HTTP 版本 + 状态码 + 状态码描述。
- 响应报头:响应的属性,这些属性都以
key: value
的形式按行陈列。 - 空行:遇到空行就表示响应报头结束。
- 响应正文:这里面存着请求的资源,一般为 html、图片、音频等内容。如果响应正文存在,则在响应报头中添加一个
content-length
属性来标识响应正文的长度。
🌙 2.2 将 HTTP 响应报文的报头和有效载荷分离
- 对于一个 HTTP 响应报文来说,报头 = 状态行 + 响应报头;有效载荷 = 响应正文。
- 分离方式与 HTTP 请求相同,客户端按行读取获取的响应报头,如果读取到空行则说名报头读取完毕。
- 从代码层面上看,连续读取到两个 \n 就表示报头读取完毕。
🌙 2.3 构建 HTTP 响应报文给浏览器
#include <string>
#include <fstream>
#include <cstring>
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>int main()
{// 1.创建套接字int listen_sock = socket(AF_INET, SOCK_STREAM, 0);if (listen_sock < 0){std::cerr << "create socket error!" << std::endl;return 1;}// 2.绑定套接字struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(8888);local.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0){std::cerr << "bind socket error!" << std::endl;return 2;}// 3.监听套接字if (listen(listen_sock, 5) < 0){std::cerr << "listen socket error!" << std::endl;return 3;}// 4.启动服务器struct sockaddr peer;bzero(&peer, sizeof(peer));socklen_t len = sizeof(peer);// 5.接收请求int sock = accept(listen_sock, (struct sockaddr *)&peer, &len);if (0 == fork()){close(listen_sock);if (fork() > 0)exit(0);char request[1024];recv(sock, request, sizeof(request), 0); // 读取HTTP请求std::cout << "------------------------------" << std::endl;std::cout << request << std::endl;std::cout << "------------------------------" << std::endl;// 构建 HTTP 响应并发送回客户端std::string response = "HTTP/1.1 200\r\n"; // 状态行response += "contect-type:text/html\r\n"; // 响应报头response += "\r\n"; // 空行response += "<html><h1>hello world!</h1><html>";// 响应正文send(sock, response.c_str(), response.size(), 0);close(sock);exit(0);}close(sock);waitpid(-1, nullptr, 0);return 0;
}
🌈 五、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 |
LINK | 建立起和资源之间的的关系 | 1.0 |
UNLIKE | 断开连接关系 | 1.0 |
- 在 HTTP 的请求应用中,99% 用的都是 GET 和 POST 方法,而在这 99% 中,GET 方法又占到了 80%。
- 网络中的行为总共就两种:上传和下载。
⭐ 1. GET 方法(重要)
-
用途:向服务器发送 GET 请求,用于从服务器中获取数据 (下载)。
- GET 请求方法通过 URL 传参。客户端向服务端获取请求行中 URL 所指定的资源。
-
示例:
GET /index.html HTTP/1.1
,获取 index.html 文件的内容。 -
特性:指定要访问的资源会在经过服务器端的解析后再返回响应内容。
GET 方法通过 URL 向服务器传参
- 访问服务器时,使用 GET 请求方法,此时应该通过 URL 进行传参 (向服务器传参不就是向服务器发送数据嘛)。
- Postman 中的 Params 字段就相当于 URL 中的参数,在设置该字段的参数时,对应的 URL 也会变化。
- 问好 ? 左边才是要请求的资源 (虽然这里没写),问好 ? 右边的就是前面的查询字符串中讲的请求服务时额外提供的参数。
- 在登录一个网站时,输入的用户名和密码就可以当作额外的参数传给服务器。
⭐ 2. POST 方法(重要)
- 用途:向服务器发送 POST 请求,用于往服务器中传输数据 (上传)。
- POST 请求方法通过正文传参。将请求正文中的数据传输给服务器,通常用于提交表单数据。
- 示例:
POST /submit.cgi HTTP/1.1
,将 submit.cgi 中的数据发送给服务器。 - 特性:可以发送大量的数据给服务器,并且数据包含在请求正文中。
POST 方法通过请求正文向服务器传参
- 访问服务器时,使用 POST 请求方法,可以通过请求正文向服务器发送数据 (传参)。
- 在 Body 字段下进行参数设置,在设置时可以选择以 raw 方式进行原始传参 (输入的参数是什么实际就传递什么)。
⭐ 3. PUT 方法 (少用)
- 用途:向服务器传输文件,将请求报文中主体中的文件保存到请求行的 URL 所指定的位置。
- 很不幸,向服务器随意上传文件的行为是被 HTTP 禁止的。
- 示例:
PUT /example.html HTTP/1.1
,向服务器上传 example.html 文件。 - 特性:不太常用,但在如 RESTful API 等情况下,用于更新资源。
⭐ 4. HEAD 方法
- 用途:与 GET 方法类似,但不会返回响应报文主体部分,只会返回响应报头。
- 示例:
HEAD /index.html HTTP/1.1
- 特性:用于确认请求行中的 URL的有效性以及资源更新的日期时间等。
⭐ 5. DELETE 方法 (少用)
- 用途:删除服务器上的文件,和 PUT 方法的功能相反,将请求行中的 URL 所指定的文件删除。
- 很不幸,随意删除服务器上的文件的行为是被 HTTP 禁止的。
- 示例:
DELETE /example.html HTTP/1.1
,删除服务器上的 example.html 文件。 - 特性:删除请求行中的 URL 所指定的资源。
⭐ 6. OPTIONS 方法
- 用途:查询针对请求行中的 URL 所指定的资源支持的方法。
- 示例:
OPTIONS * HTTP/1.1
- 特性:返回被允许的方法,如 GET、POST 等。
🌈 六、HTTP 的状态码
- 服务器在处理完 HTTP 请求报文之后,会填写响应报文的状态行中的状态码,以及状态码所对应的状态码描述。
⭐ 1. HTTP 状态码分类
- 根据开头的数字将 HTTP 的状态码划分成 5 大类,HTTP 的状态码分类如下:
类别 | 原因短语 | |
---|---|---|
1XX | informational (信息性状态码) | 接收的请求正在处理 |
2XX | Success (成功状态码) | 请求正常处理完毕 |
3XX | Redirection (重定向状态码) | 需要进行附加操作以完成请求 |
4XX | Client Error (客户端错误状态码) | 服务器无法处理请求 |
5XX | Server Error (服务器错误状态码) | 服务器处理请求出错 |
⭐ 2. HTTP 常见状态码
- 以下是 HTTP 的常见状态码,其中最常见的状态码是:200 (OK)、404 (Not Found)、403 (Forbidden)、302 (Redirect)、504 (Bad Gateway)。
状态码 | 状态码含义 | 应用样例 |
---|---|---|
100 | Continue | 上传大文件时, 服务器告诉客户端可以继续上传 |
200 | OK | 访问网站首页, 服务器返回网页内容 |
201 | Created | 发布新文章, 服务器返回文章创建成功的信息 |
204 | No Content | 删除文章后, 服务器返回 “无内容” 表示操作成功 |
301 | Moved Permanently | 网站换域名后, 自动跳转到新域名; 搜索引擎更新网站链接时使用 |
302 | Found 或 See Other | 用户登录成功后,重定向到用户首页 |
304 | Not Modified | 浏览器缓存机制,对未修改的资源返回 304 状态码 |
400 | Bad Request | 填写表单时,格式不正确导致提交失败 |
401 | Unauthorized | 访问需要登录的页面时,未登录或认证失败 |
403 | Forbidden | 尝试访问无权查看的页面 |
404 | Not Found | 访问不存在的网页链接 |
500 | Internal Server Error | 服务器崩溃或数据库错误导致页面无法加载 |
502 | Bad Gateway | 使用代理服务器时,代理服务器无法从上有服务器获取有效响应 |
503 | Service Unavaliable | 服务器维护或过载,暂时无法处理请求 |
⭐ 3. Redirection 重定向状态码
重定向状态码 | 含义 | 是否为临时重定向 | 应用样例 |
---|---|---|---|
301 | Moved Permanently | 否 (永久重定向) | 网站更换域名后,自动跳转到新域名;在搜索引擎更新网站链接时使用 |
302 | Found 或 See Other | 是 (临时重定向) | 用户登录成功后,重定向到用户首页 |
307 | Temporary Redirect | 是 (临时重定向) | 临时重定向资源到新的位置 |
308 | Permanent Redirect | 否 (永久重定向) | 永久重定向资源到新的位置 |
- 重定向是通过各种方法将网络请求重新定个方向转到其他位置,此时的这个服务器提供的服务相当于是引路。
- 例:手机上的国产 APP 摇一摇广告,刚打开软件一不小心摇了一下手机就给我重定向到淘宝。
- 客户端向 A 服务器发送 http 请求后,A 服务器返回给客户端的响应报文中,响应正文不会携带任何内容。而是会在响应报文中添加一个 Location 报头,告知客户端应该去访问指定的网站,此时客户端就会向指定网站的服务器 B 发送 http 请求报文,由服务器 B 为客户端提供服务。
1. 重定向分类
-
永久重定向:第一次访问一个永久重定向的网站时,由浏览器进行重定向,后续再访问该网站时就不需要浏览器进行重定向,而是直接访问重定向后的网站。
- 例:访问 A 网站时,会重定向跳转到 B 网站,第一次由浏览器帮助跳转到 B 网站,之后再访问 A 网站就直接跳转到 B 网站。
-
临时重定向:在访问一个临时重定向的网站时,每次访问该网站都需要进行浏览器重定向跳转到目标网站。
- 例:访问 A 网站时,会重定向跳转到 B 网站,每一次重定向跳转都需要浏览器进行辅助。
2. 演示临时重定向
- 临时重定向需要添加
Location
报头信息,告知客户端接下来要去哪里访问 (指定要重定向到哪个网站)。 - 要做的工作:将 HTTP 响应报文中的状态码设为 307,并设置状态码所对应的状态信息;为响应报文添加 Location 报头,并指明要重定向到哪个网页。
- 此处以重定向到博主的 CSDN 首页为例。
#include <string>
#include <fstream>
#include <cstring>
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>int main()
{// 1.创建套接字int listen_sock = socket(AF_INET, SOCK_STREAM, 0);if (listen_sock < 0){std::cerr << "create socket error!" << std::endl;return 1;}// 2.绑定套接字struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(8888);local.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0){std::cerr << "bind socket error!" << std::endl;return 2;}// 3.监听套接字if (listen(listen_sock, 5) < 0){std::cerr << "listen socket error!" << std::endl;return 3;}// 4.启动服务器struct sockaddr peer;bzero(&peer, sizeof(peer));socklen_t len = sizeof(peer);// 接收请求int sock = accept(listen_sock, (struct sockaddr *)&peer, &len);if (0 == fork()){close(listen_sock);if (fork() > 0)exit(0);char request[1024];recv(sock, request, sizeof(request), 0); // 读取 HTTP 请求std::cout << "------------------------------" << std::endl;std::cout << request << std::endl;std::cout << "------------------------------" << std::endl;// 构建 HTTP 响应并发送回客户端std::string response = "HTTP/1.1 307 Temporary Redirect\r\n"; // 状态行response += "Location: https://blog.csdn.net/shangguanxiu?type=blog"; // 重定向响应报头response += "\r\n"; // 空行send(sock, response.c_str(), response.size(), 0); // 将响应报文发送回客户端close(sock);exit(0);}close(sock);waitpid(-1, nullptr, 0);return 0;
}
- 此时当我输入我服务器的 IP 地址以及用以访问 http 服务的端口号 (8888),就会重定向到我得 CSDN 个人主页。
🌈 七、HTTP 的常见报头
- HTTP 的常见报头
报头名 | 说明 |
---|---|
Content-Type | 资源的数据类型 (text / html 等) |
Content-Length | 正文的长度 (单位为字节) |
Host | 客户端告知服务端,所请求的资源在哪个主机的哪个端口 |
User-Agent | 声明用户的操作系统和浏览器的版本信息 |
Referer | 记录当前页面是从哪个页面跳转过来的 |
Location | 搭配 3XX 状态码使用,告知客户端接下来要去哪里访问 |
Cookie | 用于在客户端中存储少量信息,通常用于实现会话 (session) 功能 |
⭐ 1. Host 报头
- Host 报头中存储着客户端真正想要访问的服务所在的 IP 地址和端口号。
- 当使用浏览器访问某个服务器时,浏览器发送的 HTTP 请求 Host 中填的就是该服务器的 IP 和端口号。
- 由于有些服务器实际提供的是代理服务 (代替客户端向其他服务器发起请求),要访问的文件资源可能并不在当前服务器上。
- 因此 Host 中存着的才是真正的提供服务的服务端。
- 代理服务器要执行代理功能,首先得知道客户端要访问得资源在哪里,然后再将响应报文返回给客户端。
- 例:如果启动了
fiddler
这个软件,浏览器发起的 http 请求就会被这个软件抓包,该软件执行的就是代理服务。 - 该软件会代替客户端将请求报文发送给 Host 所指定的服务器,服务器就认为是该软件给它发送的请求,于是会将响应报文返回给该软件,最后该软件将响应报文发送给客户端。
- 例:如果启动了
⭐ 2. User-Agent 报头
- User-Agent 报头中存储着发送请求的客户端所对应的操作系统和浏览器的版本信息。
⭐ 3. Referer 报头
- Referer 记录着当前是从哪一个页面跳转过来的,方便回退到上一个页面。
⭐ 4. Cookie 报头
- 用于在客户端中存储少量信息,通常用于实现会话 (Session) 功能。
- HTTP 是一种无状态的协议,并不会保存客户端的信息。但在实际场景中,大部分网站都会记住你的登录信息,这就是通过 Cookie 实现的。
查看网站的 Cookie 数据
- 点击浏览器左上角的 🔒 即可查看对应网站的各种 Cookie 数据。
- 看到的这些 Cookie 数据都是由对应的服务器方写的,将某些包含登陆时所设置信息的 Cookie 数据删除的话就可能会导致需要重新进行登录认证。
🌙 4.1 Cookie 的概念
- Cookie 用于在客户端中存储少量的信息,由于 HTTP 是一种无状态的协议 (服务器不会保存客户端的信息),如果没有 Cookie,每次页面请求时都需要重新认证用户信息。
- 例:看 VIP 视频时,每个视频都是一个新的 HTTP 请求,如果每一集都要自己进行 VIP 认证,那用户早跑光了。
- HTTP 中有一个报头选项
Set-Cookie
,可以用来给浏览器设置 Cookie 值。 - 当第一次登录某个网站时,需要进行用户认证。此时如果服务器在经过数据对比之后判断为合法用户,就会进行 Set-Cookie 的设置,填充用户登录信息。
- 当认证通过并在服务端搞定了 Set-Cookie 设置后,服务器返回给浏览器的 HTTP 响应报文就会携带上 Cookie 报头及其对应的值。
- 浏览器在收到响应报文之后,会提取出 Set-Cookie 报头中的值,并将其保存在浏览器的 Cookie 文件中,此时就将认证信息保存在本地客户端的 Cookie 文件了。
- 往后 HTTP 发送的任何信息,都会携带上 Cookie 文件中的内容,不需要用户自己进行认证。
对应网站的各种 Cookie 数据。 - 看到的这些 Cookie 数据都是由对应的服务器方写的,将某些包含登陆时所设置信息的 Cookie 数据删除的话就可能会导致需要重新进行登录认证。