【网络协议】Http 核心知识点大全

Telnet 访问网站方式

  1. 运行 CMD
  2. telnet 网址 端口号。如:telnet www.example.com 80
  3. 输入请求行。如:GET /index.html HTTP/1.1

完成上面三步回车后即可看到服务器返回的原始数据。

Note

由于 Window 自带的 Telnet 在输入内容是看不到输入内容的,可以使用 putty 并选用 raw connection 的方式进行连接。具体的 PUTTY 访问 HTTP 连接的方式如下:

  1. 下载 PUTTY,并进行配置,如下图:
image
  1. 输入 HTTP 请求头信息,主要是包含请求行 + Host 主机名(HTTP 唯一必填的头字段),输入完成后,需要输入两个回车,第一个回车表示当前请求字段输入完毕,第二个回车表示所有请求头字段输入完毕后留出的空行。如下图:
image

1. HTTP 协议是什么

HTTP 是一个计算机世界里专门用来在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范。

协议的意思

  1. 协议必须要有两个或以上的参与者,也就是“协”
  2. 协议是对参与者的一种行为约束和规范,也就是“议”

2. HTTP 历史

2.1 第一个阶段:HTTP/0.9(20 世纪 90 年代初)

  1. 采用纯文本格式
  2. 由于最初设想里的文档都是只读的,所以,只允许通过 GET 操作拉取 HTML 数据
  3. 响应请求后立即关闭连接(短连接)

2.2 第二个阶段:HTTP/1.0(1996 年正式发布)

在 0.9 的基础上,做了下面改动:

  1. 增加了 HEAD、POST等新方法
  2. 增加了响应状态码,标记可能的错误原因
  3. 引入了协议版本号概念
  4. 引入了 HTTP Header 概念,让 Http 处理请求和响应更加容易
  5. 传输协议不再仅限于文本

只是一个“参考文档”。

2.3 第三个阶段:HTTP/1.1(1999 发布)

在 1.0 的基础上进行了小幅的修正,和 1.0 的一个最主要区别是:1.1 是一个“正式的标准”。

  1. 增加了 PUT、DELETE 等新的方法
  2. 增加了缓存管理和控制
  3. 明确了连接管理,允许持久连接
  4. 允许响应数据分块(chunked),利用数据大文件
  5. 强制要求 Host 头,让互联网主机托管成为可能

2.4 第四个阶段:HTTP/2.0(2015 年正式发布)

基于 GOOGLE 开发的 SPY 协议而来。主要特点为:

  1. 二进制协议,不再是纯文本
  2. 可发起多个请求,废弃了 1.1 里的管道
  3. 使用专用算法压缩头部,减少数据传输量
  4. 允许服务器主动向客户端推送数据
  5. 增强了安全性,要求加密通信

2.4.1 二进制协议和纯文本协议的区别

疑问

无论是文本协议还是二进制协议,数据从 A 电脑传到 B 电脑的过程,传输的数据都是二进制的 0 和 1 。 为什么还有纯文本协议和二进制协议呢?

纯文本协议:使用 ASCII 码进行编码的协议。好处:所见即所得,用最简单的工具就可以开发调试,非常方便。

二进制协议:直接传输 01 字节数据,不需要进行编码,但一般都有对定义数据的结构,比如:图片、视频、音频等二进制协议,在数据头部都有专门的协议标识,用来告诉接收方,我这是什么数据,需要用什么才能进行解释。如果没有用指定的工具查看的话,看到的将会是一堆乱码。

二进制协议相比纯文本的好处:

减少数据包的大小。比如:一个字符使用 ASCII 码(只支持英文字母和符号)占一个字节,如果要传递一个 655110 这个数据,需要使用 6 个字节,而如果直接使用二进制数据,只需要 2 个字节(16 位)就可以表示所有 Int 类型的数据。显然,二进制比纯文本更加节省空间。

2.5 第五个阶段:HTTP/3.0(2018 年进入标准化制定阶段)

基于 GOOGLE 的 QUIC 协议而来。

2.5.1 什么是 QUIC?

QUIC(Quick UDP Internet Connections)基于UDP的传输层协议,提供像TCP一样的可靠性。

2.5.2 什么是队头阻塞问题?

HTTP 收发数据是一个先进先出的“串行”队列,如果队首的请求由于处理的太慢,后面的数据就不得不等待,导致其它的请求承担了不应有的时间成本。

协议之外的解决方法

  1. 并发连接。一般是通过创建一个连接池,对连接进行复用的方式进行并发的请求,也就是只要连接池里的连接没有被使用完,每个连接都是并发执行的,不会出现由于串行导致的队头阻塞问题。
  2. 域名分片。也就是多个域名指向同一个服务器,突破 HTTP 和浏览器对并发数量的限制。

协议本身的解决方法

  1. HTTP 2.0 将 Header 和 Body 的消息“打散”为数据小片的二进制“帧”,用“HEADERS”帧放头部数据,“DATA”帧存放实体数据。同时,在应用层引入了“流”的概念,二进制“帧”可以在“流”上双向传输。同一个消息往返的帧会分配一个唯一的流 ID。多个消息往返会分配多个流 ID,也就是在同一连接中构建了多个虚拟的“数据流”,在同一个连接中同时乱序发送数据。这里要注意的是:同一个流 ID 的数据传递是有序的,只是不同流 ID 的数据是并行发送的。但由于传输层使用的是 TCP ,所以数据在经过传输层的时候,还是串行进行发送的,所以,瓶颈就从应用层转移到了传输层。下图是 HTTP 2 在应用层并发数据的情况。
image
  1. HTTP 3.0 让 HTTP 运行在了 QUIC 上,而不是 TCP 上。完美解决了“队头阻塞”的问题。QUIC 在 UDP 上把 TCP 的那一套连接管理、拥塞窗口、流量控制等直接在 QUIC 上进行了重新更加优化的实现。

3. OSI 和 TCP/IP 协议栈

3.1 OSI 和 TCP/IP 的关系

TCP/IP 网络分层模型发明于 1970 年,在当年还有很多其它的网络分层模型,但由于 TCP/IP 已被广泛应用,且其扩展性也较强,于是 ISO(国际标准化组织)基于 TCP/IP 制定了统一的网络分层模型。

OSI 全称是“开放式系统互联通信参考模型”,由于在 OSI 推出之前,TCP/IP 已经被广泛应用,ISO 标准化组织也很清楚,将其推倒重来也是不太现实的,所以,OSI 在发布的时候,就表明自己只是一个参考模型,没有强制约束力。

3.2 OSI 和 TCP/IP 的网络层次的对应关系

image

从上图中可以看出,网络层和传输层两都一致,而在网络层之下,TCP/IP 协议栈只定义了链路层(MAC),而 OSI 模型更加全面,将物理层也包含了进来。

同样的,在传输层之上,TCP 将其全部划分为应用层,而在 OSI 模型上将其细分为会话层、表示层和应用层。其中,会话层主要是用来维护网络中的状态,即保持会话和同步;表示层主要是把数据转换为合适的,可以理解的语法和语义;应用层就是面向具体的应用传输数据。

但由于 OSI 将传输层之上的分层过细,而 TCP/IP 实际应用时的会话管理、编码转换和压缩等和具体应用经常联系得很紧密,很难分开,结果就是 OSI 上三层很难在具体应用的过程中实施。

3.3 一次 HTTP 数据请求对应 TCP/IP 协议栈的处理

image

从上图可以看出,HTTP 请求所传输的数据在应用层被打上了一个 HTTP Header 标签后传递到传输层;传输层收到后,在应用层 Message 的基础上,打上了一个 TCP Header 标签后又传递给了网际层;网络层收到传输层发过来的 Segment 的基础上,以打上了一个 IP Header 标签后又传递给了数据链路层;数据链路层收到网际层发过来的 Package 后,在其基础上打上了 Ethernet Header 标签后,将封装好的 frame 数据通过物理线缆传递出去。

这个时候,会有一个设备收到这个广播(数据链路层都是通过广播来通信的),这里假设路由器收到了这个 frame 数据包。它会脱掉数据包中的 frame 头部,并解析获取到目的 MAC 地址,如果目的 MAC 地址不是自己,就直接丢弃;如果是自己的,就会把报文送到网络层进行解析,此时,路由器脱掉并解析数据包的 IP 头部信息,并检查目的 IP 地址是否是自己,如果不是,就查询本地路由表,找到目标 IP 地址对应自己的出口端口,再给报文封装上目的 IP 和源 IP 地址,并通过路由表中的下一跳 IP 地址查 ARP 表,获取到下一跳 IP 地址对应的 MAC 地址,并将报文打上 Frame 头,其中,目的 MAC 地址为下一跳 IP 地址对应的 MAC 地址,源地址为当前路由器出口对应的 MAC 地址。

Frame 数据由下一跳 IP 地址所对应的设备收到后,此时假设是目的主机。目的主机收到 Frame 数据后,会脱掉并解析帧头部,发现目的 MAC 地址是自己,于是,将 IP 报文将由网络层来处理。网络层解析到目的 IP 地址后,发现目标 IP 地址是自己,则将 TCP 的 Segment 段数据交由传输层处理。传输层同样的脱掉 TCP 头部数据,并将数据交由应用层处理。应用层脱掉并解析 HTTP 报头,打到对应的端口号,并通过端口号将数据转发给对应的应用进行处理。

到此,一个完整的 HTTP 请求过程就完成了。主要就是在当前设备上依次封装对数据包进行封装(也就是打上对应层的标签),并通过重复解析和再封装数据链路层和网际层的数据头部信息(也就是不断的在数据链路层和网络层之间进行数据的传输)。直到数据包到了目标 IP 地址所匹配的主机后,再将带有 TCP 头的报文交由传输层处理后,再将带有 HTTP 头的报文将由应用层处理,最后通过目标端口将真实数据传递给目标应用处理。

4. HTTP 请求-响应

4.1 一次 HTTP 请求响应过程

image

整个过程可以根据图上的序号来进行说明:

1:客户端使用端口号 5053 与服务器发送 SYN 请求,试图与服务器建立 TCP 连接

2:服务器发送 SYN + ACK 包,对客户端发送过来的 SYN 请求进行响应。其中,SYN 中 Req 值为服务器生成的 0,而 ACK 值是对客户端 SYN 的响应,其值为客户端 SYN 值 + 1,表示“我已经收到你的请求了,并且已经同意和你(客户端)建立连接,你愿意和我建立连接么?”

3:客户端收到服务器的 SYN + ACK 包后,发送 ACK 确认包给服务器,表示“我愿意和你建立连接”。其中,SYN 的值是在第一次发送 SYN 请求时会值的基础上自 +1 得到的,而 ACK 的值是在服务器 SYN 包中 Req 值的基础上 +1,表示对服务器请求的一种响应。

7:经过 TCP 三次握手之后,客户端与服务器已经建立了 TCP 连接。此时,客户端在 TCP 连接的基础上发送 HTTP GET 请求。其中请求行中的 / 表示要获取服务器根目录下的默认文件。

8:服务器收到客户端的 HTTP 请求后,会先在 TCP 层先进行传输层的响应,表示我已经收到了客户端发来的请求。同时,请求被继续向上传递给了应用层的 80 端口,丢给了 HTTP 服务进行处理。

9:服务器对客户端发过来的请求进行处理后,将默认首页通过 Header + Body 的方式对客户端的请求进行了响应。

10:客户端收到服务器的响应后,先在 TCP 传输层对服务器进行了响应,表示“我已经收到了你的响应”;同时,将响应数据继续向上传递给了应用层的 5053 端口,丢给了 HTTP 客户端进行处理。

13:在 TCP 连接空闲了一段时间后,超过了 keep alive 所配置的超时时间后,客户端通过发送 FIN 发起了断开 TCP 的请求,此时客户端进入 FIN_WAIT1 状态,等待服务器应答。

14:服务器收到客户端发送过来的 FIN 断开请求后,立即通过 ACK 响应报文告诉客户端,你的请求我已经收到了,此时服务器处于 CLOSE_WAIT 状态。

17:服务器发送 FIN 报文告诉客户端自己也要断开连接了,此时,服务器处于 LAST_ACK 状态,等待客户端的应答。

18:客户端收到服务器发过来的 FIN 断开连接请求报文后,立即通过 ACK 应答报文告诉客户端,你的断开请求我也已经收到了。此时,客户端处于 TIME_WAIT 状态,并等待一会儿来确保服务器收到 ACK 应答报文后才进入 CLOSED 状态。而服务器收到客户端发过来的 ACK 应答报文后,就立即关闭了连接,处于 CLOSED 状态。

整个通信的过程如下图所示:

image

4.1.1 为什么 TCP 连接是三次握手?

这里主要考虑的是客户端和服务器的发送能力和接收能力。客户端需要知道服务器同时具有发送数据的能力和接收并处理数据的能力;同样的服务器也需要知道客户端同时具有发送数据的能力和接收并处理数据的能力。

第一个 SYN 请求由客户端发出,服务器收到客户端的请求后,就知道了客户端具有发送数据的能力;

第二个 SYN + ACK 响应由服务器发出,客户端在收到服务器的响应后,就知道了服务器具有发送数据的能力;同时,ACK 是对客户端发送请求的处理和响应,也就是说,客户端收到 ACK 包后,就知道了服务器具有处理数据的能力了。到这里服务器的两个能力客户端都已经知道了,而此时服务器仍然只知道客户端具有发送数据的能力。

第三个 ACK 包由客户端发出,服务器收到客户端的响应后,就知道了客户端具有了接收并处理数据的能力。至此,服务器也知道了客户端同时具有了发送数据和接收并处理响应的能力。

4.1.2 什么是半连接队列/全连接队列?

服务器第一次收到客户端的 SYN 之后,进行处理后,并发送 SYN + ACK 请求响应包到客户端,此时服务器就会处于 SYNC_RCVD 状态,此时双方还没有完全建立其连接,这个时候的 Socket 请求连接会被放在一个队列里缓存起来,这种队列就被称为半连接队列。

TCP 重传处理

服务器发送完 SYN + ACK 包后,如果未收到客户端传来的 ACK 包,服务器会进行首次重传,等待一段时间后,会进行二次重传..... 直到重传次数超过系统规定的最大重传次数,系统将该连接从半连接队列中移除,重传的等待时间,一般是指数增长的,比如:1s,2s,4s......

全连接队列指的就是已经完全三次握手,建立连接的就会放在全连接队列中。默认情况下,全连接队列满了以后,服务器会忽略客户端的 ACK,随后会重传 SYN + ACK,通过这种重传的方式,等待队列有多余的空间存储这个 Socket 连接为止。当然,也可以进行配置,改成当全连接队列满后,由服务器端发送 SYN_RST 给客户端,直接释放资源。

4.1.3 三次握手会携带数据么?

当客户端发送 ACK 报文时,是可以携带数据的,因为 ACK 报文发送出去之后,就已经建立了 TCP 连接,而在此时携带数据,可以一定程度上增长传输的效率。

为什么其它阶段不能携带数据呢?

如果在客户端发送 SYN 包的时候就携带数据,这个数据就需要被缓存到服务器内存中,而这样做就会有一个安全隐患:恶意程序只发送 SYN 包,而不发送 ACK 包,且在每个 SYN 包中携带大量数据,这种情况下,就有可能会耗尽服务器的资源,而使其无法响应正常的请求。

什么是 SYN 攻击?

服务器发送完 SYN + ACK 包后,就会将当前 Socket 连接放到半连接队列中进行缓存。攻击者常常在短时间内伪造大量不存在的 IP 地址,并不断的向服务器 SYN 包,Server 则回复确认包,并等待客户端的 ACK 包,由于资源不存在,服务器会不断的重试,直到超时,才将其从半连接队列中移除。而由于客户端在短时间内发送了大量的 SYN 包,导致正常的 SYN 请求因为半连接队列满了而被丢弃,从而拒绝了正常客户端的请求。SYN 攻击就是一种典型的 Dos/DDos 拒绝服务攻击。

常用防御 SYN 攻击的方法:

  1. 缩短 SYN 超时时间
  2. 增加最大半连接数
  3. 过滤网关防护
  4. SYN cookies 技术

4.1.4 为什么握手是三次,而挥手需要四次?

TCP 半关闭

TCP 半关闭指的是 TCP 提供了连接的一端在结束它的发送数据的能力后,还能接收来自另外一端数据的能力。

TCP 四次挥手

TCP 三次握手只能由客户端发起不同,四次握手可以由客户端或服务器中的任意一端发起。

四次握手的过程:

image
  1. A 端发起 FIN 报文,此时 A 端处于 FIN_WAIT1 状态,此状态后,该端就停止再发送数据,意思是 A 端关闭了发送数据的能力,并等待 B 端确认
  2. B 端收到 FIN 之后,会发送 ACK 报文,表示 B 端已经收到了 A 端想要断开连接的请求。此时 B 端处于 CLOSE_WAIT 状态。此时,TCP 处于半关闭状态,等待 B 端发送 FIN 连接释放报文段
  3. 如果 B 端也想断开连接,和 A 端一样,发送 FIN 报文,此时,B 端处于 LAST_ACK 的状态,表示服务器也停止了向客户端发送数据,意思是 B 端关闭了发送数据的能力,并等待 A 端确认
  4. A 关收到 B 端发过来的 FIN 报文后,一样发送一个 ACK 报文作为应答,此时,客户端处于 TIME_WAIT 状态,而不会立即关闭,而是会等一会儿来确保 B 端收到 ACK 报文之后才进入 CLOSED 状态。而 B 端在收到 A 端发送的 ACK 应答后,就立即关闭连接,处于 CLOSED 状态

为什么是四次挥手,而不是三次

当 A 端发送关闭请求的 FIN 报文时,B 端可能还有数据需要传输而不想马上关闭连接,所以就发送了一个 ACK 应答报文,表示我已经收到了你想要关闭连接的请求。此时,进入半关闭状态,即 A 端已经不会再发送数据,而 B 可能还需要发送数据。

当 B 端发送完数据后,已经没有更多数据要发送的情况下,也会发送一个 FIN 报文给 A 端,表示我也没有数据要发送了,咱们关闭连接吧。A 端在收到 FIN 报文后,就立即发送了 ACK 应答报文。B 端在收到 ACK 应答报文后,就立即关闭了连接,而 A 端在等待一段时间后,认为 B 端已经收到了 ACK 应答报文的情况下,也关闭了连接。至此,TCP 连接彻底断开了。

为什么四次挥手时,第四次发送了 ACK 包后的一端会进入 TIME_WAIT 状态,而不是直接进入 CLOSED 状态

由于 TCP 协议是一种面向连接的传输方式,必须保证数据能够正确到达目标机器。如果最后一个发给服务器 ACK 包,由于网络原因,服务器未收到的话,此时,服务器会再次重传上一个 FIN 包给客户端。如果这时客户端完全关闭了连接,那么服务器永远都收不到 ACK 包了,所以,客户端在发送 ACK 包后,要等待片刻,确认对方收到 ACK 包(也就是不会再收到服务器重传的 FIN 包)后才能进入 CLOSED 状态。

那客户端需要等待多久了?

数据包在网络中是有生存时间的,超过这个时间还未到达目标主机就会被丢弃,并通知源主机。这个时间被称为 报文最大生存时间(MSL,Maximum Segment Lifetime)TIME_WAIT 通常要等待 2MSL 才进入 CLOSED 状态。这 2MSL 包括 ACK 包到达服务器需要的 1MSL 时间和服务器重传 FIN 包也需要的 MSL 时间。 如果 2MSL 后还未收到服务器重传的 FIN 包,就说明服务器已经收到了 ACK 包。

疑问

在抓包的过程中,HTTP 请求响应完成后,客户端在 2S 左右的时间内,就断开了和服务器的 TCP 连接?

在抓包过程中,四次挥手中的 FIN 发送方中为什么都有带一个 ACK 的确认应答?

5. HTTP 报文结构

5.1 HTTP 协议的基本组成

image

请求行

image

example:GET / HTTP/1.1

请求行的作用:描述客户端想要怎样操作服务器上的资源。

状态行

image

example:HTTP/1.1 200 OK

状态行的作用:描述服务器的响应状态。

请求头唯一必填字段

Host: www.example.com

Host 请求字段的作用:告诉服务器这个请求应该由哪个主机处理,当一台计算机(主要是多个域名对应同一个 IP 的情况)上托管了多个虚拟主机的时候,服务器就需要使用 Host 字段来进行“路由”。

5.2 常用请求方法

  • GET:获取资源,读取或者下载数据
  • HEAD:获取资源的元信息,也就是只有响应头,而没有实体 body。比如:检查文件是否存在;检查文件是否有新版本等操作
  • POST:向资源提交信息,相当于写入或上传数据,数据放在报文的实体 body 里,相比于 * PUT,POST 常常有“新建”的含义
  • PUT:类似 POST,相比于 POST,PUT 常常有“修改”的含义
  • DELETE:删除资源
  • CONNECT:建立特殊的连接隧道,要求服务器为客户端和另一台远程服务器建立一条特殊的连接隧道,此时,WEB 服务器充当“代理”角色
  • OPTIONS:列出对资源实行的方法
    TRACE:追踪请求/响应的传输路径

安全与幂等

在 HTTP 里的安全指的是:请求方法不会对服务器上的资源造成实质的修改。

从上面的定义可以看出:

  1. 只有 GET 和 HEAD 是安全的,无论操作多少次,服务器上的资源都不会发生实质的改变。
  2. POST/PUT/DELETE 是不安全的,这三个方法会增加、修改或删除服务器上的资源。

在 HTTP 里的幂等指的是:多次执行相同的操作,结果也是一样的。即多次“幂”后也相等。

从定义可以看出:

  1. GET 和 HEAD 也是幂等的,DELETE 多次删除同一个资源,结果也是“资源不存在”,所以也是幂等的。
  2. 如果将 POST 严格定义为“新增或提交数据”,多次提交数据就会创建多个资源,所以不是幂等的。而如果将 PUT 严格定义为“替换或更新数据”,多次更新数据,其数据的状态也还是第一次更新的状态,所以也是幂等的。

5.3 常见状态码

  • 1xx:提示信息,表示目前是协议处理的中间状态,还需要后续的操作
  • 2xx:成功,表示报文已经收到并被正确处理
  • 3xx:重定向,资源位置发生变动,需要客户端重新发送请求
  • 4xx:客户端错误,请求报文错误,服务器无法处理
  • 5xx:服务器错误,服务器在处理请求时内部发生了错误

1xx 常见响应码

101:表示要在 HTTP 协议的基础上改成其它协议继续通信,如:WebSocket,而如果服务器也同意的情况下,就会返回状态码 101,之后的数据传输就不用 HTTP 了。

2xx 常见响应码

  • 200 OK:表示服务器处理成功并返回
  • 204 NOT Content:表示服务器处理成功并返回,但是响应数据中不包含 body 数据(与 200 的区别)
  • 206 Partial Content:HTTP 分块下载或断点续传的基础,客户端发送 Content-Range 请求、要求获取资源的部分数据时,服务器会通过 206 来进行响应,表示服务器成功处理其范围请求

3xx 常见响应码

  • 301 Moved Permanently:永久重定向。表示当前访问的资源已经不存在了,需要使用新的 URI 来进行访问
  • 302 Moved Temporary:临时重定向。表示访问的资源还在,但需要暂时用另一个 URI 访问
  • 304 Not Modified:缓存重定向。一般在请求中使用 If-Modified-Since 字段后,用来表示资源未被修改,让客户端直接去缓存中获取数据

301 和 302 的区别

  1. 对于搜索引擎来说,如果是 302 的话,搜索引擎会抓取新的内容而保留旧的地址,因为搜索引擎认为新的网址是暂时的;而如果是 301 的话,搜索引擎在抓取新的内容的同时,也将旧的网址替换为新的网址
  2. 对于客户端缓存来说,如果是 302 的话,客户端是不会缓存数据的,每次请求都访问原地址;而如果是 301 的话,客户端会进行缓存数据,在缓存没有过期的情况下,客户端会使用缓存的数据,而不会再去请求原地址

4xx 常见响应码

  • 400 Bad Request:表示请求报文有错误,但不清楚具体是什么错误
  • 403 Forbidden:表示服务器禁止客户端请求该资源
  • 404 Not Found:表示服务器没有找到客户端想要访问的资源

5xx 常见响应码

  • 500 Internal Server Error:表示服务器发生了错误,但不清楚具体是什么错误
  • 501 Not Implemented:表示客户端请求的功能还不支持
  • 502 Bad Gateway:通常表示服务器作为网关或代理服务器时返回的错误码,表示自己正常工作,而访问后端服务器时发生了错误
  • 503 Service Unavailable:表示服务器当前很忙,暂时无法响应请求

6. HTTP 特点

  1. 灵活可扩展,用空格分隔单词,用换行分隔字段
  2. 可靠传输。由 TCP 协议来保持其可靠传输
  3. 应用层协议。HTTP 几乎可以传递一切东西,满足各种需求,称得上是一个“万能”的协议
  4. 请求-应答通信模式。请求方主动发起连接和请求,应答方只有在收到请求后才能被动的答复,如果没有请求,不会有任何答复
  5. 无状态。在通信过程中,两端都不保存任何数据,每次收发报文都是互相独立的、没有任何的联系

HTTP 无状态和 UDP 无状态的区别

  • HTTP 是有连接无状态,顺序发包顺序收包,按照报文顺序管理报文
  • UDP 是无连接无状态,顺序发包乱序收包,数据包发出去后,就不管了

HTTP 的优点

  1. 简单、灵活、易于扩展
  2. 应用广泛
  3. 无状态。不需要记录额外的状态信息,减轻了服务器的负担;同时,由于没有差异也导致很容易组建集群,通过负载均衡将请求转发到任意一台服务器,不会因为状态的不一致而导致处理出错
  4. 明文传输。方便调试和分析

HTTP 的缺点

  1. 无状态。无法支持多个步骤的“事务”操作,每次请求都要携带身份验证信息,增加了不必要的数据传输量。可以通过 Cookie 技术实现有状态
  2. 明文传输。毫无隐私可言,容易被窥视。如:免费 WIFI 陷阱
  3. 不支持数据完整性校验。数据在传输过程中,容易被篡改而无法验证真伪
  4. 性能问题。可能出现队头阻塞的问题

7. HTTP 实体数据

7.1 识别 HTTP 传输数据类型的方法

HTTP 中主要通过 MIME 头字段来标识传输数据的类型,同时通过 Encoding type 来标识使用的哪种数据使用的哪种压缩方法。

MIME

MIME 格式:type/subtype

常见的几种 MIME 类型:

  1. text/html,超文本文档
  2. image/图片格式
  3. audio/video,音视频数据
  4. application/类型,(如:application/json,application/pdf...)数据不固定,可能是文本或二进制,需要由上层应用程序来解释

Encode type

  1. GZIP
  2. Deflate
  3. br:一种专门为 HTTP 优化的新压缩算法

传输内容类型协商方法

Accept:客户端使用 Accept 头告诉服务端希望接收什么样的数据
Content:服务器通过 Content 告诉客户端实际传输的是什么类型的数据

7.2 HTTP 传输大文件的方法

  1. 数据压缩,使用 GZIP 等压缩方式对 body 数据进行压缩
  2. 分块传输,由服务器将大文件分解成多个小块分批发送给浏览器,浏览器收到后再组装还原
  3. 范围请求,由客户端发起,表示只想请求大文件的某一部分的数据(如:看电影时拖动进度条)。服务器通过使用 Accept-Ranges: bytes 告诉客户端自己是支持范围请求的。请求头使用 Range: bytes=x-y(x 和 y 分别表示范围的开头和结尾,单位为 byte)
  4. 多段数据,实际上也是一种范围请求,只是在请求头里面同时添加了多个范围,如:Range: bytes=x-y,a-b,c-d。其响应头中对应的 MIME 类型为:multipart/byteranges,其报文结构也与单个的范围请求有所区别。

HTTP 如何分块传输

服务器使用响应头字段 Transfer-Encoding: chunked 来标识数据是分块传输的。

Transfer-Encodding: chunked 和 Content-length 的关系

  1. 这两个字段都是响应头字段
  2. 两个响应字段是互斥的,不能同时出现

分块结构

  1. 每个分块由长度头和数据块两部分组成
  2. 长度头或数据块都是以 CRLF(回车换行作为结尾)
  3. 最后用一个长度为 0 的块表示结束,即:0\r\n\r\n,其中,第一个 \r\n 表示长度头的结尾,第二个 \r\n 表示数据块的结尾,由于数据块中没有数据,所以,两个 \r\n 就连在一起了
image

=== chunked 实验 16-1 使用 Putty 访问

image

Range 的格式

开头或结尾中的任意一个都可以省略,但不可以同时省略。如:

  • "0-":表示从文档起点到终点,如果文档有 200 个字节,相当于 0-199;
  • "10-":表示从 第 10 个字节到文档末尾;
  • "-1":表示文档的倒数第一个字节,也就是最后一个字节到文档结尾;
  • "-10":表示从文档末尾倒数 10 个字节到文档结尾;

服务器 Range 响应

服务器使用 Content-Range: bytes x-y/lengthRange 请求进行响应。这里的 x 和 y 表示服务器当前响应的范围区间,length 表示数据的总长度。

服务器响应 Range 异常

如果超出范围,则返回状态码 416,意思是“你请求的范围有误”

===== Range 范围请求实验 16-2

image

多段数据响应的报文结构

image

===== 多段数据范围请求实验 16-2

image

8. HTTP 连接管理

8.1 短连接

一次请求-响应后就会断开连接。

短连接存在问题

因为 TCP 建立连接和关闭连接是非常“昂贵”的操作,建立连接要有“三次握手”,需要耗费 1 个 RTT(Round-Trip Time,往返延时);关闭连接是“四次挥手”,4 个数据需要 2 个 RTT。

8.2 长连接

HTTP/1.1 中长连接默认是开启的,

服务器主动关闭长连接的策略

  1. 使用 “keepalive-timeout” 指令,设置连接超时时间,如果超过这个时间都没有任何数据收发就关闭连接
  2. 使用 “keepalive-requests” 指令,设置长连接上可以发送的最大请求次数,如果超过最大请求次数则会断开长连接

8.3 队头阻塞

HTTP 的请求-应答通信模型,决定了请求是一发一收的串行通信方式,所有包只有入队的顺序,排在前面的会先被处理,没有优先级的概念。如果队首由于处理的太慢耽误了时间,就会使得其它请求不得不跟着一起等待。

8.3 HTTP 性能优化

  1. 并发连接,同时对一个域名发起多个长连接。
  2. 域名分片,将访问同一个域名的请求分摊到不同 IP 地址的服务器上。

9. HTTP 重定向

9.1 重定向的响应头字段

Location: /index.html

对应的值即可以指定绝对 URI,还可以指定相对 URI。

如果使用绝对 URI 的话,需要带上 http:// 头,否则会被当作相对路径处理。

10. HTTP Cookie 机制

10.1 Cookie 的作用

让 HTTP 这种无状态的协议通过 Cookie 机制保存相应的状态。通过在客户端缓存一些状态信息,并在客户端请求时,携带这些状态信息来实现 HTTP 有状态的目的。

10.2 Cookie 的工作过程

  1. 当用户通过浏览器第一次访问服务器时,服务器会为该浏览器创建一个独特的身份标识,格式为 key=value。并将其放到 set-cookie 字段里返回给浏览器。
  2. 浏览器收到报文后,将 set-cookie 的值保存了起来,并在下次请求的时候,主动将这个 Cookie 放入到请求头中一并发送给服务器。
  3. 通常情况下,服务器会通过多个 set-cookie 头字段来返回多条 Cookie 信息给浏览器。而浏览器在向服务器发送请求时,只需要通过 ; 将多个 key-value 的键值隔开并放到同一个 Cookie 请求头字段即可。

Cookie 是由浏览器来维护的,所以,其作用范围在各种浏览器内部。使用其它浏览器则需要重新保存 Cookie。

10.3 Cookie 的常用属性

  • Expires:过期时间,是一个绝对时间点,可以理解为截止日期
  • Max-Age:相对时间点,单位是“秒”。浏览器使用最后一次收到报文的时间点 + Max-Age,就等于失效的绝对时间。
  • Exprices 和 Max-Age 同时被设置时,会优先采用 Max-Age 的相对时间点。
  • Domain 和 path 用来指令 Cookie 所属于的域名和路径,也就是指令 Cookie 的作用域,只会在对应的域名和路径都匹配的时候,浏览器才会携带该 Cookie。作用域可以针对每个 set-cookie 来进行单独配置。如果没有指定作用域的情况下,当前域名下的所有请求中都携带这个 Cookie 值。
  • HttpOnly:告诉浏览器,该 Cookie 只能通过 HTTP 协议传输。
  • sameSite:设置 Cookie 是否允许随着跳转链接跨站发送
  • Secure:表示这个 Cookie 仅能用 HTTP 协议加密传输,但 Cookie 保存本身不是加密的,仍然是以明文的形式存储。

10.4 Cookie 的典型应用

  1. 身份认证
  2. 广告跟踪,通过使用第三方 Cookie 来实现,而非主站 Cookie。如果广告商覆盖范围广,你访问的很多网站都集成了该广告商的广告后,你走到哪里都会认出你,然后,给你推送广告。

====== Cookie 失效时间和作用域 19-2

11. HTTP 缓存

11.1 HTTP 缓存工作流程

  1. 浏览器发现缓存无数据,于是发送请求,向服务器获取资源
  2. 服务器响应请求,返回资源,同时标记资源的有效期
  3. 浏览器缓存资源,等待下次重用

11.2 Cache-Control 缓存字段

服务器使用 Cache-Control: max-age=50 字段来对资源进行缓存控制,值为 key-value 键值对格式。

max-age

max-age 是 HTTP 缓存控制常用的属性,常见的值有:

  1. no-store:不允许缓存。例如:秒杀页面
  2. no-cache:不是不能缓存,而是可以缓存,只是使用之前要去服务器验证一下是否已经过期,是否有最新版本
  3. must-revaliable:表示如果缓存没有过期则可以继续使用,但过期了如果还想用就需要去服务器验证

客户端同样可以使用 Cache-Control 字段来控制是否使用缓存。

刷新按钮

当点击浏览器“刷新按钮”时,浏览器会在请求头中添加 Cache-Control: max-age=0 字段,由于 max-age=0 说明浏览器不会使用缓存,而是向服务器发请求,服务器看到 max-age=0,所以会重新生成最新的报文返回给浏览器。

Ctrl+F5 强制刷新

当使用 Ctrl + F5 强制刷新时,浏览器会在请求头中添加 Cache-Control: no-control 头字段,处理效果基本上和刷新按钮一样。

浏览器默认不使用缓存

当服务器通过 Cache-Control: max-age=30 字段返回缓存时,浏览器默认不会使用缓存,而是使用 Cache-Control: max-age=0 请求头字段去请求服务器获取最新的数据。那缓存如何还会被使用呢?

  1. 当浏览器点击前进、后退或页面跳转操作时,浏览器才会去访问缓存里的数据(也就是 from disk cache)

===== Cache-Control 实验 20-1

11.3 条件请求 If-Modified-Since/Last-Modified 或 If-None-Match/ETag

ETag:表示资源的唯一标识,刚资源被修改后,值会跟着改变。
Last-Modified:资源最后修改时间。

If-Modified-Since 和 Last-Modified 的工作流程

  1. 浏览器发送请求,服务器响应请求,并在响应头里添加了 Last-Modified: 修改时间 字段来标识当前资源的最后修改时间(如果是动态生成的网页,由于每次都是新生成的,所以,不会访问缓存),浏览器缓存 Last-Modified 时间
  2. 浏览器二次对同一资源发起请求,并在请求头中添加 If-Modified-Since: Last-Modified 字段给服务器
  3. 服务器收到请求后,和对应资源的最后修改时间作比较,如果相等,则返回 304,让浏览器去本地获取数据;如果不相等,就会把最新的资源文件及其 Last-Modified 返回给浏览器

If-None-match/ETag

  1. 浏览器发送请求,服务器响应请求,并在响应头里添加了 ETag 字段来标识当前资源的唯一标识,浏览器缓存 ETag 标识
  2. 浏览器二次对同一资源发起请求,并在请求头中添加了 If-None-Match: ETag 头字段发送给服务器
  3. 服务器收到请求后,与本地资源的 ETag 值进行比较,如果相等,则返回 304,让浏览器去本地获取数据;如果不相等,就会把最新的资源文件及其 ETag 返回给浏览器

12. HTTP 代理

12.1 什么是代理服务

代理服务指:服务器本身不生产内容,而是处于中间位置转发上下游的请求和响应,具有双重身份。面向下游用户时,表现为服务器,代表源服务器响应客户端请求;面向上游的源服务器时,又表现为客户端,代表客户端发送请求。

12.2 代理服务器的作用

  1. 负载均衡。代理服务器可以通过将请求转发到不同的服务器(不同的 IP 地址)来实现负载均衡。常用的负载均衡方法有轮询、一致性哈希算法等。
  2. 健康检查。通过“心跳”等方式监控后端服务器,并在发现故障时及时将其踢出群,保证服务的高可用。
  3. 安全防护。保护被代理的后端服务器,限制 IP 地址或流量,抵御网络攻击和过载。
  4. 加密卸载。对外使用 SSL/TSL 加密通信认证,而在安全内网不加密,消除服务器的解密成本。
  5. 数据过滤。通过策略对上下行数据进行过滤。
  6. 内容缓存。暂存、复用服务器响应。

12.3 代理相关头字段

Via

在请求头或响应头中都会出现。每经过一个代理服务器,代理服务器都会在请求或响应头里将自身信息(如:主机名或域名)追加到 via 字段的末尾。

image

作用:为了让客户端或源服务器感知到代理服务器的存在,但不知道对方的真实信息(如:服务器或客户端 IP)。

但一般来说,服务器的 IP 的需要保密的,是不会让客户端知道的。但是通常情况下,服务器需要知道客户端的 IP 地址,方便访问控制、用户画像、统计分析等。

X-Forwarded-For 和 X-Real-IP

HTTP 协议里并没有定义代理服务器转发过程中添加 IP 地址的字段。但是 X-Forwarded-ForX-Real-IP 已经成为了事实上的标准。

X-Forwarded-For: 在客户端请求过程中,每经过一个结点就会通过在该字段上追加当前结点的 IP(包括客户端)。所以,最左边的就是客户端的 IP。

X-Real-IP:该字段只是用于记录客户端的 IP 地址,经过的代理服务器 IP 并不会被追加。是 X-Forwarded-For 的简化版本。

====== 代理服务器信息标记实验 21-1。

代理服务器转发请求过程

image
  1. 客户端通过 55061 端口与代理服务器通过三次握手来建立 TCP 连接。
  2. 建立 TCP 连接后,客户端向服务器发送 GET 请求。
  3. 代理服务器不产生内容,所以通过 55063 端口通过 TCP 三次握手建立连接。
  4. 代理服务器连接源服务器成功后,发送了一个 HTTP/1.0 的 GET 请求。
  5. 源服务器响应了代理服务器,并且由于代理服务器使用的是 HTTP/1.0,所以响应后就立即通过四次挥手断开了与代理服务器的连接。
  6. 代理服务器收到源服务器响应后,将其转发给了客户端,完成一次代理服务。

X-Forward-For 给代理服务器带来的问题

因为通过 X-Forwarded-For 操作代理信息必须要解析 HTTP 报文头,这对于代理来说成本比较高,原本只需要简单地转发消息就好,而现在却必须要费力解析数据再修改数据,会降低代理的转发性能。

12.4 代理协议

代理协议也不是 HTTP 定义的,而是由知名代理软件 HAProxy 所定义的,也是一个“事实标准”。

PROXY TCP4 1.1.1.1 2.2.2.2 55555 80\r\n
GET / HTTP/1.1\r\n
Host: www.xxx.com\r\n
\r\n

代理协议的定义是在 HTTP 的请求行前添加一个以 PROXY 开头的信息行。

好处

代理服务器收到报文后,只需要解析第一行就可以拿到客户端地址,不需要再去理会后面的 HTTP 数据。

13. 缓存代理

13.1 什么是缓存代理

缓存代理就是支持缓存控制的代理服务。HTTP 服务器的缓存功能主要由代理服务器来实现。

作用

可以让请求不必走完整个后续处理流程,“就近”获得响应结果。

你可能感兴趣的:(【网络协议】Http 核心知识点大全)