# 前端HTTP发展历程

# 1. HTTP/0.9

HTTP/0.9 是于 1991 年提出的,主要用于学术交流,需求很简单——用来在网络之间传递 HTML 超文本的内容,所以被称为超文本传输协议。整体来看,它的实现也很简单,采用了基于请求响应的模式,从客户端发出请求,服务器返回数据。

  • 只有一个请求行,并没有 HTTP 请求头和请求体,也就是说只有GET请求,不能回应其他请求类型

  • 服务器也没有返回头信息,协议规定,服务器只能回应HTML格式的字符串,不能回应别的格式

  • 返回的文件内容是以 ASCII 字符流来传输的,因为都是 HTML 格式的文件,所以使用 ASCII 字节码 来传输是最合适的

  • 没有状态码或错误代码:一旦出现问题,一个特殊的包含问题描述信息的HTML文件将被发回,供人们查看

  • 服务器发送完毕,就关闭TCP连接

# 2. HTTP/1.0

# 2.1 HTTP/1.0 新增功能

由于 HTTP/0.9 协议的应用十分有限,浏览器和服务器迅速扩展内容使其用途更广。1996 年 5 月,HTTP/1.0 版本发布,内容大大增加。 相对于 HTTP/0.9 大致增加了如下几点:

  • 引入了HTTP头的概念,无论是对于请求还是响应,允许传输元数据,使协议变得非常灵活,更具扩展性

  • 引入了 POST 命令和 HEAD 命令,丰富了浏览器与服务器的互动手段

  • 在新HTTP头的帮助下,具备了传输除纯文本HTML文件以外其他类型文档的能力(感谢Content-Type头)

  • 协议版本信息现在会随着每个请求发送(HTTP/1.0被追加到了GET行)

  • 状态码(status code)会在响应开始时发送,使浏览器能了解请求执行成功或失败,并相应调整行为(如更新或使用本地缓存)

  • 通过Expries/Last-Modified字段对资源进行缓存,轻服务器的压力

  • 多字符集支持、多部分发送(multi-part type)、权限(authorization)、内容编码(content encoding)

GET /myPage.html HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)

200 OK
Date: Tue, 15 Nov 1994 08:12:31 GMT
Server: CERN/3.0 libwww/2.17
Content-Type: text/html
<HTML>
一个包含图片的页面
  <IMG SRC="/myImage.gif">
</HTML>
1
2
3
4
5
6
7
8
9
10
11

# 2.2 HTTP/1.0缺点

  • 每个 TCP 连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接

  • TCP 连接的新建成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow start)

# 3. HTTP/1.1

# 3.1 HTTP/1.1 新增功能

HTTP/1.0 多种不同的实现方式在实际运用中显得有些混乱,自1995年开始,即HTTP/1.0文档发布的下一年,就开始修订HTTP的第一个标准化版本。在1997年初,HTTP1.1 标准发布,就在HTTP/1.0 发布的几个月后。

相对于 HTTP/1.0 版本 HTTP/1.1 做了一些优化大致如下:

  • 长连接: HTTP 1.1 支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个 TCP 连接上可以传送多个 HTTP 请求和响应,减少了建立和关闭连接的消耗和延迟,在 HTTP1.1 中默认开启 Connection: keep-alive,一定程度上弥补了 HTTP1.0 每次请求都要创建连接的缺点

  • 缓存处理: 在 HTTP1.0 中主要使用 header 里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1 则引入了更多的缓存控制策略例如Etag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略

  • 带宽优化及网络连接的使用:HTTP1.0 中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1 则在请求头引入了range 头域,它允许只请求资源的某个部分,即返回码是206(Partial Content)

  • 错误通知的管理:在 HTTP1.1 中新增了24 个错误状态响应码,如 409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除

  • 引入内容协商机制:包括语言,编码,类型等,并允许客户端和服务器之间约定以最合适的内容进行交换

  • Host 头处理:在 HTTP1.0 中认为每台服务器都绑定一个唯一的 IP 地址,因此,请求消息中的 URL 并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个 IP 地址。HTTP1.1 的请求消息和响应消息都应支持 Host 头域,且请求消息中如果没有 Host 头域会报告一个错误(400 Bad Request)。个人理解就是一个服务器(唯一IP)有多个域名(huoyuhao.com、liam.com),为了区别出请求是那个域名发起的,所以在请求头中添加Host字段

  • 客户端 Cookie 机制和安全机制:HTTP协议是无状态的。Cookie是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器

  • 新增了许多动词方法:PUT、PATCH、HEAD、 OPTIONS、DELETE

# 3.2 请求头Range

Range是在 HTTP/1.1里新增的一个请求头字段域。我们使用的迅雷等支持多线程下载以及断点下载的核心也是基于此重要特性

如果用户的请求中含有range ,则服务器的相应代码为206

206 - Partial Content 客户发送了一个带有Range头的GET请求,服务器完成了它

请求头中:
Range: bytes=0-801 // 一般请求下载整个文件是bytes=0- 或不用这个头
响应头中:
Content-Range: bytes 0-800/801 // 801:文件总大小
1
2
3
4

# 3.3 HTTP/1.1缺点

  • 虽然 1.1 版允许复用 TCP 连接,但是同一个 TCP 连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。要是前面的回应特别慢,后面就会有许多请求排队等着。这称为"队头堵塞"(Head-of-line blocking)。为了防止这种问题,现代浏览器会针对单个域名开启6个连接,通过各个连接分别发送请求。它实现了某种程度上的并行,但是每个连接仍会受到队头阻塞的影响。另外,这也没有高效利用有限的设备资源

  • HTTP1.x 在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,这在一定程度上无法保证数据的安全性

  • HTTP1.x 在使用时,header 里携带的内容过大,在一定程度上增加了传输的成本,并且每次请求header基本不怎么变化,尤其在移动端增加用户流量

  • 虽然 HTTP1.x 支持了 keep-alive,来弥补多次创建连接产生的延迟,但是keep-alive使用多了同样会给服务端带来大量的性能压力,并且对于单个文件被不断请求的服务(例如图片存放网站),keep-alive可能会极大的影响性能,因为它在文件被请求之后还保持了不必要的连接很长时间

# 4. HTTP/2.0

# 4.1 二进制协议

HTTP/1.1 版的头信息肯定是文本(ASCII编码),数据体可以是文本,也可以是二进制。HTTP/2 则是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为"帧"(frame):头信息帧和数据帧

二进制协议的一个好处是,可以定义额外的帧。HTTP/2 定义了近十种帧,为将来的高级应用打好了基础。如果使用文本实现这种功能,解析数据将会变得非常麻烦,二进制解析则方便得多。

# 4.2 头信息压缩

HTTP 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,比如Cookie和User Agent,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。

HTTP/2 对这一点做了优化,引入了头信息压缩机制(header compression)。一方面,头信息使用gzip或compress压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。

HTTP2头部压缩示意图

头部压缩需要在支持 HTTP/2 的浏览器和服务端之间

  • 维护一份相同的静态字典(Static Table),包含常见的头部名称,以及特别常见的头部名称与值的组合

  • 维护一份相同的动态字典(Dynamic Table),可以动态地添加内容

  • 支持基于静态哈夫曼码表的哈夫曼编码(Huffman Coding)

# 4.3 服务器推送

Server Push 即服务端能通过 push 的方式将客户端需要的内容预先推送过去,也叫“cache push”。 服务器可以对一个客户端请求发送多个响应。服务器向客户端推送资源无需客户端明确地请求,服务端可以提前给客户端推送必要的资源,这样可以减少请求延迟时间,例如服务端可以主动把 JS 和 CSS 文件推送给客户端,而不是等到 HTML 解析到资源时发送请求

所有推送的资源都遵守同源策略。 服务器必须遵循请求- 响应的循环,只能借着对请求的响应推送资源

# 4.4 多路复用

多路复用代替原来的序列和阻塞机制。所有就是请求的都是通过一个 TCP 连接并发完成。因为在多路复用之前所有的传输是基于基础文本的,在多路复用中是基于二进制数据帧的传输、消息、流,所以可以做到乱序的传输。多路复用对同一域名下所有请求都是基于流,所以不存在同域并行的阻塞。

# 4.4.1 帧(frame)

HTTP/2 中数据传输的最小单位,因此帧不仅要细分表达 HTTP/1.x 中的各个部份,也优化了 HTTP/1.x 表达得不好的地方,同时还增加了 HTTP/1.x 表达不了的方式。

每一帧都包含几个字段,有length、type、flags、stream identifier、frame playload等,其中type 代表帧的类型,在 HTTP/2 的标准中定义了 10 种不同的类型,。

在 HTTP 2.0 中,它把数据报的两大部分分成了 header frame 和 data frame。也就是头部帧和数据体帧。帧的传输最终在流中进行,流中的帧,头部(header)帧 和 data 帧可以分为多个片段帧,例如data帧即是可以 data = data_1 + data_2 + ... + data_n。

# 4.4.2 流(stream)

流: 存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一的整数 ID。 HTTP/2 长连接中的数据包是不按请求-响应顺序发送的,一个完整的请求或响应(称一个数据流 stream,每个数据流都有一个独一无二的编号)可能会分成非连续多次发送。它具有如下几个特点:

  • 双向性:同一个流内,可同时发送和接受数据

  • 有序性:流中被传输的数据就是二进制帧 。帧在流上的被发送与被接收都是按照顺序进行的

  • 并行性:流中的 二进制帧 都是被并行传输的,无需按顺序等待

  • 流的创建:流可以被客户端或服务器单方面建立, 使用或共享

  • 流的关闭:流也可以被任意一方关闭

  • HEADERS 帧在 DATA 帧前面

  • 流的 ID 都是奇数,说明是由客户端发起的,这是标准规定的,那么服务端发起的就是偶数了

HTTP2流数据传输

# 4.5 队头阻塞

# 4.5.1TCP队头阻塞

TCP队头阻塞:队头阻塞(head-of-line blocking)发生在一个TCP分节丢失,导致其后续分节不按序到达接收端的时候。该后续分节将被接收端一直保持直到丢失的第一个分节被发送端重传并到达接收端为止。该后续分节的延迟递送确保接收应用进程能够按照发送端的发送顺序接收数据。这种为了达到完全有序而引入的延迟机制非常有用,但也有不利之处。

假设在单个TCP连接上发送语义独立的消息,比如说服务器可能发送3幅不同的图像供Web浏览器显示。为了营造这几幅图像在用户屏幕上并行显示的效果,服务器先发送第一幅图像的一个断片,再发送第二幅图像的一个断片,然后再发送第三幅图像的一个断片;服务器重复这个过程,直到这3幅图像全部成功地发送到浏览器为止。要是第一幅图像的某个断片内容的TCP分节丢失了,客户端将保持已到达的不按序的所有数据,直到丢失的分节重传成功。这样不仅延缓了第一幅图像数据的递送,也延缓了第二幅和第三幅图像数据的递送。

# 4.5.2 HTTP队头阻塞

队头阻塞”与短连接和长连接无关,而是由 HTTP 基本的“请求 - 应答”机制所导致的。因为 HTTP 规定报文必须是“一发一收”,这就形成了一个先进先出的“串行”队列。

http1.0的队首阻塞 (非管道化下):完全串行执行,请求->响应->请求->响应...,后一个请求必须在前一个响应之后发送。如果前一个请求卡着了,那么队列中后续的http就会阻塞。http1.0的队首组塞发生在客户端。

http1.1的队首阻塞 (管道化下):请求可以并行发出,但是响应必须串行返回。后一个响应必须在前一个响应之后。原因是,没有序号标明顺序,只能串行接收。也就是说,先接收到的请求需要先响应回来。这样造成的问题是,如果最先收到的请求的处理时间长的话,响应生成也慢,就会阻塞已经生成了的响应的发送。也会造成队首阻塞,响应的阻塞。

# 4.5.3 HTTP队头阻塞 的解决方法

  • 并发TCP连接:浏览器一个域名采用6-8个TCP连接,并发HTTP请求
  • 域名分片:多个域名,可以建立更多的TCP连接,从而提高HTTP请求的并发
  • 利用HTTP2的多路复用解决:http2使用一个域名单一TCP连接发送请求,请求包被二进制分帧,不同请求可以互相穿插,避免了http层面的请求队头阻塞

即使使用HTTP2,如果HTTP2底层使用的是TCP协议,仍可能出现TCP队头阻塞

HTTP/2 使用了多路复用,一般来说同一域名下只需要使用一个 TCP 连接。但当这个连接中出现了丢包的情况,那就会导致 HTTP/2 的表现情况反倒不如 HTTP/1 了。因为在出现丢包的情况下,整个 TCP 都要开始等待重传,也就导致了后面的所有数据都被阻塞了。但是对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。

HTTP2对头阻塞讲解 (opens new window)

# 5. 连接管理

# 5.1 串行连接

HTTP/0.9 和早期的 HTTP/1.0 协议对 HTTP 请求处理是串行化的。

每次http事务(http事务由一次完整的http请求加上http响应构成)都要建立一个新的TCP连接,而且每个新的http事务都要串行执行。

HTTP串行连接

# 5.2 持久连接(长连接)

HTTP/1.1允许HTTP设备在事务处理结束后将TCP连接保持在打开状态,以便未来重用。在事务处理完成之后仍然保持打开状态的TCP连接称之为持久连接,持久连接会在不同事务之间保持打开状态直到客户端或服务器决定将其关闭

HTTP持久连接(长连接)

# 5.3 并行连接

HTTP允许客户端(如浏览器)打开多条连接,并行执行多个HTTP事务,加快加载速度。当然这里有一点要说明,并行连接并不是一定更快,如果并行的进程数太多会消耗很多内存资源,此外,如果每个用户的客户端打开100个连接,则100个用户同时访问时会有10000个连接需要服务器处理,加重服务器的负担。

HTTP并行连接

浏览器连接数以及其他限制

# 5.4 管道化连接

持久连接让我们可以重用连接来完成多次请求,但它必须满足 FIFO 的队列顺序,必须保证前一个请求成功到达服务器、处理成功并且收到服务器返回的首个字节,才可以发起队列中下一个请求。HTTP 管道允许客户端在同一个 TCP 通道内连续发起多个请求,而不必等待响应,消除了往返延迟时间差。但现实情况由于 HTTP/1.x 协议的限制,不允许数据在一个链路上交错到达(IO 多路复用)。设想一种情况,客户端服务器端同时发送一个 HTML 和多个 CSS 请求,服务器并行处理所有请求,当所有的 CSS 请求处理完成并加入到缓冲队列,却发现 HTML 请求处理遇到问题而无限被挂起,严重时甚至造成缓冲区溢出,这种情况就叫做队首阻塞。因此,这个方案在 HTTP/1.x 协议中并没有被采纳。

HTTP管道化连接

# 6 HTTP请求响应头头内容

# 6.1 请求报文的首部内容由以下数据组成

  • 请求行 —— 包含用于请求的方法、请求 URI 和 HTTP 版本

  • 首部字段 —— 包含表示请求的各种条件和属性的各类首部(通用首部、请求首部、实体首部以及RFC里未定义的首部如 Cookie 等)

  • 请求体 —— 包含表示请求的提交数据内容

HTTP请求头

HTTP请求头

# 6.2 响应报文的首部内容由以下数据组成

  • 状态行 —— 包含表明响应结果的状态码、原因短语和 HTTP 版本

  • 首部字段 —— 包含表示请求的各种条件和属性的各类首部。(通用首部、响应首部、实体首部以及RFC里未定义的首部如 Cookie 等)

HTTP响应头

HTTP响应头

# 6.3 一个 TCP 连接中 HTTP 请求发送可以一起发送么

再来看看第三个问题:一个 TCP 连接中 HTTP 请求发送可以一起发送么(比如一起发三个请求,再三个响应一起接收)?

HTTP/1.1 存在一个问题:单个 TCP 连接在同一时刻只能处理一个请求。

意思是说:两个请求的生命周期不能重叠,任意两个 HTTP 请求从开始到结束的时间在同一个 TCP 连接里不能重叠。

虽然 HTTP/1.1 规范中规定了管道化连接(Pipelining)来试图解决这个问题,但是这个功能在浏览器中默认是关闭的

# 7.参考文章

网络编程懒人入门(七):深入浅出,全面理解HTTP协议 (opens new window)

HTTP的发展 (opens new window)

Http系列(-) Http发展历史 (opens new window)

Http 系列(二) Http2 中的多路复用 (opens new window)

HTTP 协议中的并发限制及队首阻塞问题 (opens new window)

Last Updated: 9/16/2025, 8:23:04 PM