# HTTP1
# HTTP/0.9
HTTP/0.9 与 1991 年提出,主要用来在网络之间传输 HTML 文件,采用请求响应模式,从客户端发出请求,服务端返回数据。
特点:
- 只有一个请求行,没有HTTP 请求头和请求体,因为只有一个请求行就能完整表达客户端的需求了。
- 服务器没有返回头信息,因为服务端不需要告诉客户端太多信息,只需要返回数据就可以了。
- 返回的文件内容是以 ASCII 字符流来传输的,因为都是 HTML 格式的⽂件,所以使⽤ ASCII 字节码 来传输是最合适的。
# HTTP/1.0
支持多种类型的文件下载是 HTTP/1.0 的核心需求
为实现多种类型的文件下载,HTTP/1.0 引⼊了请求头和响应头,它们都是以为 Key-Value 形式保存的,在 HTTP 发送请求时,会带上请求头信息,服务器返回数据时,会先返回响应头信息。请求头和响应头新增了很多有用信息:
# 请求头和响应头
请求头:
accept: text/html
,表⽰期望服务器返回 html 类型的⽂件,accept-encoding: gzip, deflate, br
,表⽰期望服务器可以采⽤ gzip、deflate 或者 br 其 中的⼀种压缩⽅式,accept-Charset: ISO-8859-1,utf-8
,表⽰期望返回的⽂件编码是 UTF-8 或者 ISO-8859-1,accept-language: zh-CN,zh
,表⽰期望⻚⾯的优先语⾔是中⽂。User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36
,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。
响应头:
content-encoding: br
,表⽰服务器采⽤了 br 的压缩⽅法,content-type: text/html; charset=UTF-8
,表⽰服务器返回的是 html ⽂件,并且该⽂件的编码类 型是 UTF-8。
# 状态码
有的请求服务器可能⽆法处理,或者处理出错,这时候就需要告诉浏览器服务器最终处理该请求的情况,这就引⼊了状态码。状态码是通过响应⾏的⽅式来通知浏览器的。
# cache 缓存
为了减轻服务器的压⼒,在 HTTP/1.0 中提供了 Cache 机制,⽤来缓存已经下载过的数据。
# HTTP/1.1
# 持久链接
- 在 HTTP/1.0,每次请求都要重新链接一次,会经历建立 TCP 链接,传输 HTTP 数据,断开 TCP 链接。性能上会有限制。
- 在 HTTP/1.1 中增加了持久链接的方法,它的特点是在一个 TCP 链接上可以传输多个 HTTP 请求,只要浏览器或者服务器没有断开 TCP 链接,那么该 TCP 会一直保持。
- 持久链接在 HTTP/1.1 是默认开启的,如果不想采用持久链接,可以在请求头设置
Connection: close
。 - ⽬前浏览器中对于同⼀个域名,默认允许同时建⽴ 6 个 TCP 持久连接。
# 队头阻塞与 HTTP 管线化
- 持久连接虽然能减少 TCP 的建⽴和断开次数,但是它需要等待前⾯的请求返回之后,才能进⾏下⼀次请求。如果 TCP 通道中的某个请求因为某些原因没有及时返回,那么就会阻塞后⾯的所有请求,这就是著名的队头阻塞的问题。
- HTTP/1.1 中试图通过管线化的技术来解决队头阻塞的问题。HTTP/1.1 中的管线化是指将多个 HTTP 请求整批提交给服务器的技术,虽然可以整批发送请求,不过服务器依然需要根据请求顺序来回复浏览器的请求。
- FireFox、Chrome 都做过管线化的试验,但是由于各种原因,它们最终都放弃了管线化技术。
# 对虚拟主机的支持
在 HTTP/1.0 中,每个域名绑定了⼀个唯⼀的 IP 地址,因此⼀个服务器只能⽀持⼀个域名。但是随着虚拟主机技术的发展,需要实现在⼀台物理主机上绑定多个虚拟主机,每个虚拟主机都有⾃⼰的单独的域名,这些单独的域名都公⽤同⼀个 IP 地址。
因此,HTTP/1.1 的请求头中增加了 Host 字段,⽤来表⽰当前的域名地址,这样服务器就可以根据不同的 Host 值做不同的处理。
# 对动态生成的内容的支持
在设计 HTTP/1.0 时,需要在响应头中设置完整的数据⼤⼩,如 Content-Length: 901
,这样浏览器就可以根据设置的数据⼤⼩来接收数据。不过随着服务器端的技术发展,很多⻚⾯的内容都是动态⽣成的,因此在传输数据之前并不知道最终的数据⼤⼩,这就导致了浏览器不知道何时会接收完所有的⽂件数据。
HTTP/1.1 通过引⼊Chunk transfer 机制来解决这个问题,服务器会将数据分割成若⼲个任意⼤⼩的数据块,每个数据块发送时会附上上个数据块的⻓度,最后使⽤⼀个零⻓度的块作为发送数据完成的标志。这样就提供了对动态内容的⽀持。
# 客户端 Cookie、安全机制
Cookie 主要用于保存 HTTP 会话状态,比如记录用户登陆状态:
- 用户登陆成功后,响应头通过
Set-cookie
返回用户登陆信息,比如:Set-Cookie: UID=3431uad
; - 浏览器读取响应头,,如果有
Set-cookie
字段,浏览器就会把这个字段信息保存到本地。⽐如把UID=3431uad
保持到本地。 - 当⽤户再次访问时,浏览器会发起 HTTP 请求,但在发起请求之前,浏览器会读取之前保存的 Cookie 数据,并把数据写进请求头⾥的 Cookie 字段⾥(
Cookie: UID=3431uad;
),然后浏览器再将请求头发送给服务器。 - 服务器在收到 HTTP 请求头数据之后,就会查找请求头⾥⾯的“Cookie”字段信息,当查找到包 含
UID=3431uad
的信息时,服务器查询后台,并判断该⽤户是已登录状态,然后⽣成含有该⽤户信息的⻚⾯数据,并把⽣成的数据发送给浏览器。 - 浏览器在接收到该含有当前⽤户的⻚⾯数据后,就可以正确展⽰⽤户登录的状态信息了。
# HTTP/1.0 的问题
# 1.HTTP/1.0 对带宽对利用率不理想
带宽是指每秒最大能发送或接收的字节数。每秒能发送的最⼤字节数叫上⾏带宽,每秒能够接收的最⼤字节数叫下⾏带宽。
HTTP/1.1 很难将带宽⽤满,比如 100M 带宽,实际的下载速度能达到 12.5M/S,⽽采⽤ HTTP/1.1 时,也许在加载⻚⾯资源时最⼤只能使⽤到 2.5M/S,很难将 12.5M 全部⽤满。主要有 3 个原因:
TCP 的慢启动
TCP 链接建立后,刚开始会用比较慢的速度发送数据,然后慢慢加快到一个理想速度,这个过程叫慢启动。慢启动是 TCP 为了减少网络拥塞的一种策略。而对于页面来说,关键资源一般不会很大,如 HTML、js、css,慢启动导致耗时增加,推迟了首次渲染页面的时长了。同时开启多条 TCP 链接,会竞争固定的带宽
同时存在多条 TCP 链接时,当带宽不足时,各个 TCP 连接就需要动态减慢接收数据的速度。而有些 TCP 链接下载的是一些关键资源(HTML,css,js),而有些是非关键资源(图片,视频,普通文件),TCP 之间无法协商哪些资源优先下载,因此就有可能影响关键资源的下载速度。HTTP/1.1 队头堵塞问题
持久连接虽然能减少 TCP 的建⽴和断开次数,但是它需要等待前⾯的请求返回之后,才能进⾏下⼀次请求。如果 TCP 通道中的某个请求因为某些原因没有及时返回,那么就会阻塞后⾯的所有请求,这就是著名的队头阻塞的问题。发生堵塞后,后续的请求都需要等待,在等待过程中带宽和 cpu 都白白浪费了。堵塞让数据请求不能并行,无法提前接收到其他请求都数据,不利于浏览器优化。