HTTP协议发展史
2024-08 1
一个数据包的旅程
- 请求方要发送的数据包,在应用层加上HTTP头组成新的数据包交给传输层。
- 传输层会在数据包前面附加上 TCP 头,组成新的 TCP 数据包,再将新的 TCP 数据包交给网络层;
- 网络层再将 IP 头附加到数据包上,组成新的 IP 数据包,并交给底层;
- 数据包被传输到目的地的网络层,在这里拆开 IP 头信息,并将拆开来的数据部分交给传输层;
- 在传输层,数据包中的 TCP 头会被拆开,并根据 TCP 中所提供的端口号,把数据部分交给上层的应用程序;
- 最终,含有“Web应用”信息的数据包就旅行到了目的地主机的上层应用程序这里。
超文本传输协议 HTTP/0.9
HTTP/0.9 是于 1991 年提出的,主要作用是在网络之间传递 HTML 超文本的内容,所以被称为超文本传输协议。其传输流程如下:
- 因为 HTTP 都是基于 TCP 协议的,所以客户端先要根据 IP 地址、端口和服务器建立 TCP 连接,而建立连接的过程就是 TCP 协议三次握手的过程。
- 建立好连接之后,会发送一个 GET 请求行的信息,如GET /index.html用来获取 index.html。
- 服务器接收请求信息之后,读取对应的 HTML 文件,并将数据以 ASCII 字符流返回给客户端。
- HTML 文档传输完成后,断开连接。
总的来说,当时的需求很简单,就是用来传输体积很小的 HTML 文件,所以 HTTP/0.9 的实现有以下三个特点:
- 第一个是只有一个请求行,并没有 HTTP 请求头和请求体,因为只需要一个请求行就可以完整表达客户端的需求了。
- 第二个是服务器也没有返回头信息,这是因为服务器端并不需要告诉客户端太多信息,只需要返回数据就可以了。
- 第三个是返回的文件内容是以 ASCII 字符流来传输的,因为都是 HTML 格式的文件,所以使用 ASCII 字节码来传输是最合适的。
被浏览器推动的 HTTP/1.0
随着万维网的高速发展,而 HTTP/0.9 已经不能适用新兴网络的需求。
首先在浏览器中展示的不单是 HTML 文件了,还包括了 JavaScript、CSS、图片、音频、视频等不同类型的文件。因此支持多种类型的文件下载是 HTTP/1.0 的一个核心诉求,而且文件格式不仅仅局限于 ASCII 编码,还有很多其他类型编码的文件。
因此HTTP/1.0 引入了请求头和响应头,它们都是以为 Key-Value 形式保存的,在 HTTP 发送请求时,会带上请求头信息,服务器返回数据时,会先返回响应头信息。
引入请求头和响应头之后,HTTP/1.0的各种需求便可以借此满足了:
- 通过请求头的
accept
和响应头content-type
字段,浏览器和服务器便完成了数据类型的传递。 - 通过请求头的
accept-encoding
和响应头的content-encoding
字段,浏览器和服务器便完成了压缩类型的传递。 - 通过请求头的
accept-language
和响应头的Content-Language
字段,浏览器和服务器便完成了语言版本的传递。 - 通过请求头的
accept-Charset
和响应头的content-type
字段,浏览器和服务器便完成了编码形式的传递。(因为utf-8
的广泛使用,accept-Charset
已不再使用) - 通过相应行的状态码字段,浏览器知道了服务器处理该请求的情况。
- 通过响应头的
Expires
字段为浏览器提供缓存机制。 - 通过请求头的用户代理
User-Agent
字段为服务器提供了用户客户端信息。
如下面这个例子,便是浏览器的请求头,它告诉服务器:期待服务器返回什么类型的文件、采取什么形式的压缩、提供什么语言的文件以及文件的具体编码。
accept: text/html
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh
accept-Charset: ISO-8859-1,utf-8
服务器接收到请求头后,会告诉浏览器它所支持的格式,比如服务器不支持 gzip,只支持 br 压缩,那么它会通过响应头中的 content-encoding 字段告诉浏览器最终的压缩类型,最终浏览器需要根据响应头的信息来处理数据。
content-encoding: br
content-type: text/html; charset=UTF-8
缝缝补补的 HTTP/1.1
随着技术的继续发展,很快 HTTP/1.0 也不能满足需求了,所以 HTTP/1.1 又在 HTTP/1.0 的基础之上做了大量的更新。
-
**改进持久连接:**HTTP/1.0每次通信发送请求后都需要收到 ACK 消息,以确认对方已接收到数据。如果每次请求都要在收到上次请求的 ACK 消息后再请求,那么效率无疑很低。
为此,HTTP/1.1 中增加了持久连接的方法,它的特点是在一个 TCP 连接上可以传输多个 HTTP 请求,只要浏览器或者服务器没有明确断开连接,那么该 TCP 连接会一直保持。
在这个背景下,下面就来谈 HTTP/1.1 的队头阻塞。下图中,一个 TCP 连接同时传输 10 个请求,其中第 1、2、3 个请求已被客户端接收,但第 4 个请求丢失,那么后面第 5 - 10 个请求都被阻塞,需要等第 4 个请求处理完毕才能被处理,这样就浪费了带宽资源。
因此,HTTP 一般又允许每个主机建立 6 个 TCP 连接,这样可以更加充分地利用带宽资源,但每个连接中队头阻塞的问题还是存在。
持久连接在 HTTP/1.1 中是Connection:keep-alive
默认开启的,可在请求头加上Connection: close
关闭。
-
支持管道传输:TCP请求有队头阻塞问题。HTTP/1.1尝试将多个 HTTP 请求整批提交给服务器的技术,虽然可以整批发送请求,不过服务器依然需要根据请求顺序来回复浏览器的请求。
FireFox、Chrome 都做过管线化的试验,但是由于各种原因,它们最终都放弃了管线化技术。
-
支持虚拟主机:HTTP/1.1 的请求头中增加了 Host 字段,表示当前的域名地址。多个域名地址可公用一个IP地址。服务器中可配置根据不同域名处理不同的请求。
-
客户端 Cookie、安全机制:HTTP/1.1 还引入了客户端 Cookie 机制和安全机制。
-
引入新的缓存策略:cache-control和协商缓存。
总结如下:
- 增加了持久连接;
- 浏览器为每个域名最多同时维护 6 个 TCP 持久连接;
- 使用 CDN 的实现域名分片机制。
- 引入新的缓存策略:cache-control和协商缓存。
推广中的HTTP/2.0
HTTP/1.0尚存在不少问题,其中最致命的是TCP的慢启动以及队头阻塞问题,针对这些问题,HTTP/2 协议规范于 2015 年 5 月正式发布,并做了如下优化:
-
多路复用:HTTP/2.0采用一个域名只建立一个TCP连链接、每个请求都被拆分成多个片段进行传输。并且重要的请求可以通过标记优先级让服务器优先处理。
-
服务器推送:服务器在接受到
html
文件的请求后,可以配置同时返回特定的css
和js
文件,从而减少这两个文件的请求时间。 -
头部压缩:HTTP/2 对请求头和响应头进行了压缩。
缺陷:
- HTTP/2.0虽然引入了多路复用的机制,但是仍不能完美解决队头阻塞问题。如果请求的某个片段失败后,后续的请求仍然被阻止,这是TCP协议的特性,无法改变。有测试数据表明,当系统达到了 2% 的丢包率时,HTTP/1.1 的传输效率反而比 HTTP/2 表现得更好。
- TCP每次通信都需要握手,三次握手带来了每次通信都会有
1.5 RTT
(Round Trip Time)的延迟。如果还要进行TLS连接,那么还会消耗1~2个RTT
的延迟。因此在传输数据前,可能需要花费3~4个RTT
的时间。
未来的HTTP/3.0
鉴于TCP
协议的根本性缺陷以及中间设备僵化等问题,HTTP/3.0
摒弃了TCP
协议,采用基于UDP
协议的QUIC
(Quick UDP Internet Connections)协议,该协议和TCP
协议的对比如下:
-
实现了类似 TCP 的流量控制、传输可靠性的功能:提供了数据包重传、拥塞控制以及其他一些 TCP 中存在的特性。
-
集成了 TLS 加密功能:目前 QUIC 使用的是 TLS1.3,相较于早期版本 TLS1.3 有更多的优点,其中最重要的一点是减少了握手所花费的 RTT 个数。
-
实现了 HTTP/2 中的多路复用功能。和 TCP 不同,QUIC 实现了在同一物理连接上可以有多个独立的逻辑数据流(如下图)。实现了数据流的单独传输,就解决了 TCP 中队头阻塞的问题。
缺陷
- 服务器和浏览器端都没有对 HTTP/3 提供比较完整的支持。
- 部署 HTTP/3 也存在着非常大的问题。因为系统内核对 UDP 的优化远远没有达到 TCP 的优化程度,这也是阻碍 QUIC 的一个重要原因。
- 中间设备僵化的问题。这些设备对 UDP 的优化程度远远低于 TCP,据统计使用 QUIC 协议时,大约有 3%~7% 的丢包率。
参考链接:
- 无目录
- HTTP