Http 协议简析
Http/0.9 是于 1991 年提出,用来网络之间传递 HTML 超文本的内容,所以被称为超文本传输协议
Http/0.9 完整的请求流程
1.Http 是基于 TCP 协议的,客户端要先根据 IP 地址、端口和服务器建立 TCP 连接,而建立连接的过程就是 TCP 协议协议三次握手的过程。
2.建立好连接后,会发送一个 GET 请求行的信息,如GET/index.html 来获取 index.html
3.服务器接受到请求后,读取对应的 HTML 文件,并将数据以 ASCII 字符流返回给客户端。
4.HTML 文档传输完成后,断开连接。

TCP 协议三次握手流程
所谓的三次握手(Three-way HandShake),是指建立一个 TCP 连接时,需要客户端和服务器总共发3个包。
三次握手的目的是连接服务器指定端口,建立 TCP 连接,并同步连接双方的序列号和确认号,交换 TCP 窗口大小信息。
1.第一次握手(SYN=1,seq=x)
客户端发送一个 TCP 的 SYN 标志为 1 的包,指明客户端打算连接的服务器的端口,以及初始序号 X,保存在包头的序列号(Sequence Number)字段里
2.第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1)
服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即 X+1。
3.第三次握手(ACK=1,ACKnum=y+1)
客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1。
当服务器端接收到第3次握手的包后,TCP握手结束。开始传输数据。

TCP 协议四次挥手流程
TCP 的连接的断开需要发送四个包,因此称为四次挥手(Four-way handshake),也叫做改进的三次握手。客户端或服务器均可主动发起挥手动作。
1.第一次挥手(FIN=1,seq=x)
假设客户端想要关闭连接,客户端发送一个 FIN 标志为 1 的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。
发送完毕后,客户端进入 FIN_WAIT_1 状态。
2.第二次挥手(ACK=1,ACKnum=x+1)
服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。
发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。
3.第三次挥手(FIN=1,seq=y)
服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。
发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK
4.第四次挥手(ACK=1,ACKnum=y+1)
客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。
服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态
Http/1.0
1.新增请求头字段
1 | Accept 文件类型 |
2.新增响应头
1 | Content-Encoding 服务器返回文件的压缩类型 |
3.响应行状态码 Status Code
status code: 200 表示请求成功
4.新增缓存机制,用来缓存已经下载过的资源,减轻了服务端压力
Http/1.1
Http/1.0 发送多个同域名请求。每次请求都需要重新建立 TCP 连接和断开连接的操作,这无疑增加了网络开销,同时也延迟了页面显示
在 Http/1.1 请求头中增加了 Connection 字段,用于提供 TCP 的持久连接。
1 | Connection: keep-alive |
它默认是开启持久连接的,即对于同一个域名,浏览器默认支持 6 个 TCP 持久连接。建立一次 TCP 连接,可以发送多次 HTTP 请求。
Http/1.1 通过引入 Chunk transfer 机制来支持动态内容:服务器会将数据分割成若干个任意大小的数据块,每个数据块发送时会附上上个数据块的长度,最后使用一个零长度的块作为发送数据完成的标志。
Http/1.1 还引入了客户端 Cookie 机制和安全机制
Http/2
Http/1.1 尽管作出很多优化,但对带宽的利用率并不理想。
带宽是指每秒最大能发送或者接收的字节数。我们把每秒能发送的最大字节数称为上行带宽,每秒能够接收的最大字节数称为下行带宽。
Http/1.1 很难将带宽用满。主要由3个问题导致:
1.TCP 慢启动
一旦一个 TCP 连接建立之后,就进入了发送数据状态,刚开始 TCP 协议会采用一个非常慢的速度去发送数据,然后慢慢加快发送数据的速度,直到发送数据的速度达到一个理想状态,我们把这个过程称为慢启动。
2.同时开启多条 TCP 连接,这些连接会竞争固定的带宽。
3.HTTP/1.1 队头阻塞的问题
HTTP/1.1 中使用持久连接时,虽然能公用一个 TCP 管道,但是在一个管道中同一时刻只能处理一个请求,在当前的请求没有结束之前,其他的请求只能处于阻塞状态。这意味着我们不能随意在一个管道中发送请求和接收内容。
为了解决 Http/1.1 中的问题,Http/2 采用多路复用的机制。
Http/2 多路复用
浏览器针对同一域名的资源,只建立一个 TCP 连接通道,所有的针对这个域名的请求全部都在这个通道中完成。
数据的传输不再使用文本格式,而是会将它们分割为更小的流和帧,并对他们采用二进制格式的编码。在一个 TCP 连接通道中,支持任意数量的双向数据流,这些数据流是并行、乱序的且它们之间互不干扰。而数据流中传输的数据是二进制帧,它是 Http/2 中数据传输的最小单位,一个流中的帧是按照顺序传输的,且是并行的,所以无需按顺序等待。

Http/2 因为只使用一个 TCP 连接,所以减少了由于 TCP 慢启动而消耗的时间,另外也由于只有单条 TCP 连接,所以不存在不同的 TCP 争夺网络带宽的问题
客户端发送的请求经过二进制分帧层后,不再是一个个完整的 HTTP 请求报文,而是一堆乱序的帧(即不同流的帧是乱的,但是同一条流的帧数顺序传输的),所以就不会按顺序传输,也就不存在等待,从而解决了 HTTP 对头阻塞问题。
Http/2 其他特性
1.可以设置请求的优先级
支持设置请求的优先级,这样服务器收到高优先级的请求后,会优先处理。
2.服务器推送
在 HTTP/2 中服务器解析到一个 HTML 页面后,服务器知道浏览器需要这个页面上引用到的资源,比如 CSS 和 JS,那么服务器就会主动的把这些资源一并推送给浏览器,减少客户端的等待时间。
3.头部压缩
HTTP/2 使用 HPACK 压缩算法对请求头和响应头进行压缩。
Http 缓存
浏览器缓存指的就是浏览器对之前请求过的文件进行缓存,以便在下一次访问时重复使用,从而节省带宽、提升访问速度、降低服务器压力。
HTTP 缓存机制就是利用HTTP响应头将所请求的资源在浏览器中进行缓存,缓存方式主要分为两种:强缓存、协商缓存。
Http 强缓存
强缓存是指在缓存期间不会向服务器发起请求,只有过期后才会想服务器发起请求。
1.Expires
Expires 是 http1.0 中的字段,是一个绝对时间,即服务器时间。浏览器检查当前时间,如果还没到失效时间就直接使用缓存文件。
1 | <meta http-equiv="Expires" content="0" /> |
2.Cache-Control
由于 Expires 存在服务器时间与客户端时间不一致的问题,所以 http1.1 中新增了 Cache-Control 字段。
通过设置 max-age 来保存一个相对时间,表示其在该相对时间内容有效。
1 | <meta http-equiv="Cache-Control" content="no-cache" /> |
content 可以设置为:
1 | private: 默认值,表示客户端可以缓存,中间代理、CDN等不能缓存此响应 |
当同时设置 Expires 和 Cache-Control 时,浏览器会优先使用 Cache-Control
Http 协商缓存
协商缓存都会向服务器发送请求,判断缓存数据是否过期,过期的话会返回新的内容,没有过期则使用本地的缓存数据。

1.Last-Modify
Last-modified 是 http1.0 中的字段,是第一次请求资源时,服务器返回的字段,表示最后一次更新的时间。下一次浏览器请求资源时就发送 if-modified-since 字段。服务器用本地 Last-modified时间与 if-modified-since 时间比较,如果不一致则认为缓存已过期并返回新资源给浏览器;如果时间一致则发送 304 状态码,让浏览器继续使用缓存。
2.Etag
Etag是 http1.1 中新增的字段,是资源的实体标识(哈希字符串),当资源内容更新时,Etag 会改变。服务器会判断 Etag 是否发生变化,如果变化则返回新资源,否则返回304。