【计算机网络】HTTP 版本
如何查看http 版本?
请求头
http2.0(掘金):
http1.1(hao123):
http0.9
HTTP 0.9是第一个版本的HTTP协议,已过时。它的组成极其简单,只允许客户端发送GET这一种请求,且不支持请求头。HTTP0.9具有典型的无状态性,每个事务独立进行处理,事务结束时就释放这个连接。
http1.0
- 增加了 POST 请求
- 新增了请求头、http 状态码等
- 新增 Cookie(“假装”有了状态)
无状态:但是HTTP/HTTPS 协议仍是一个无状态协议,每个请求之间相互独立,服务器不会自动记录上一次请求的状态。
想要有状态需要通过某些方式解决:使用 Cookie、Session、Token 来维持会话状态。
连接性:严格来说 HTTP/HTTPS 不是无连接协议。
早期的 HTTP/1.0 默认使用 短连接(每次请求就断开,表现得像“无连接”)。从 HTTP/1.1 开始默认启用 持久连接(Keep-Alive),一个 TCP 连接可以复用多次请求,不再是“无连接”。
http1.1
新增 keep-alive 长连接
http1.1允许在请求时增加请求头 connection:keep-alive, 这样便允许后续的客户端在一段时间内复用之前的TCP 连接。通过⼀个 TCP 连接传输多个请求和响应,减少了连接建⽴和关闭的时间。避免了每次都要进行三次握手。
但是一个连接终归太慢了,但连接太多可能会造成 DDos 攻击。所以各家浏览器都会支持同一连接的最大连接请求数(每个域的最大同时请求数),比如 谷歌浏览器 最大连接数为 6 (最大并行请求数)。
新增 pipeline 管道
基于长连接的基础,管道化可以不等第一个请求响应继续发送后面的请求,但响应的顺序还是按照请求的顺序返回。但是各个请求之间存在依赖关系,一个请求出错,后面的请求都失败。
所以实际场景中很少有使用这个技术的,在网络层面解决不了这个问题。所以这时在开发层面就会出现一些解决方案:比如 1. 雪碧图;2. DataUrls 方案,图片以 base64 的编码形式写入 js / html 中,但是内容太多,同时因为浏览器的请求数量限制,所以网站会弄出多个域(域名分片),来增加请求数量。
增加了 PUT, DELETE, OPTIONS. PATCH 等新方法
缓存处理
新增响应头 cache-control、ETag, 用于实现客户端缓存。同时新增了一些缓存的字段 (If-Modified-Since,If-None-Match)。
支持分块传输
在上传/下截资源时,如果资源过大,将其分割为多个部分,分别上传/下载,如果遇到网绍故障,可以从已经上传/下载好的地方继续请求,不用从头开始,提高效率。在请求头中使用 Range 字段,可以分块(chunk)获取响应数据。
并发连接(谷歌是6个):对一个域名的请求允许分配多个长连接(缓解了长连接中的「HTTP 队头阻塞」问题)
强制要求 Host 头,让互联网主机托管称为可能
http2.0
二进制分帧
HTTP1.1 版本的头部信息是文本,数据部分可以是文本也可以是二进制。HTTP2.0 版本的头部和数据部分都是二进制,且统称为帧,因为不是 ascii 编码,是直接的二进制,所以解析更有效率。每帧有自己的标识序号,即便被随意打乱也能在另一端正确组装。
支持多路复用
废弃了HTTP1.1 中的管道,同一个TCP连接里面,客户端和服务器可以同时发送多个请求和多个响应,并且不用按照顺序来。正如上面二进制分帧所说,每帧有自己的标识序号(流标识符),即便被随意打乱也能在另一端正确组装。因此服务器不用按顺序来处理响应,也能得到正确的数据,所以避免了 HTTP 的“队头堵塞“的问题(并没有解决 TCP 队头阻塞问题)。此时可以突破浏览器的并发请求限制,而且精灵图、dataUrls、域名分片等等都将是没有必要的。
支持头部压缩
http2.0 通过字典的形式(比如首部:
{:method:GET},而且像是 cookie 这样的首部可以作为动态信息加入到动态表中),将头部中的常见信息替换为更少的字符,而且可以在二次请求中重复的首部直接复用,极大的减少了头部的数据量,从而实现更小的传输量。http/1.1 报文主主体压缩,报文首部不压缩。但是现在 HTTP/2 首部也压缩。
服务端主动推送:允许服务器主动向客户推送数据。
HTTP/2 的 Server Push 只是服务器在响应阶段提前推送静态资源,用于优化加载性能,不具备持续连接能力;而 SSE 和 WebSocket 是用于实时通信的机制,可以持续推送数据流。
注意:这里的推送和 sse / WebSocket 没有任何关系。
数据流:服务器可以以流 stream 流的形式向客户端返回内容。
http3.0
- 将底层TCP协议改为UDP,彻底解决 TCP 队头阻塞问题
- 应用层 HTTP 上的 http2 使用了二进制分帧,但是在传输层层面上 TCP 不能识别 http2 的帧信息,还是会造成队头阻塞的问题,如果有 tcp 数据段信息丢失,还需要重传。最好的解决方案是模仿 http2 那样做二进制分帧,但是 tcp 协议是基于操作系统内核实现的,很难进行彻底升级,因此直接改用了 UDP
- 缺点:兼容性还不行,不能大规模使用。
http 是基于TCP协议实现的。
TCP的主要作用是以正确的顺序将整个字节流从一个端点传输到另一个端点,但是当流中的某些数据包丢失时,TCP需要重新发送这些丢失的数据包,等到丢失的数据包到达对应端点时才能够被HTTP处理,这被称为TCP队头阻塞问题。
TCP 在启动时还会进行慢启动,因为一开始不知道网络实际情况,为了不造成网络拥堵,会进行拥塞控制。所以一开始只会发送小量数据段,后面再逐渐增加。新访问网页速度较慢。
HTTP3.0就是主要解决这个问题的。
基于这个原因,Google 基于UDP协议“创造”了QUIC协议,并且使用在了HTTP/3上。
QUIC的第一个特征就是快。
HTTP协议在传输层是使用了TCP进行报文传输,建立连接需要进行3次握手。QUIC的握手连接更快,因为它使用了UDP作为传输层协议(然后在 UDP 上层使用了 QUIC 这个“全新”的协议),这样能够减少三次握手的时间延迟。
本质上是 QUIC 整合了 TCP 的握手和 TLS 的握手,直接减少来回的开销。如果是恢复的会话,还可以不用握手,实现 0-RTT 。
- 使用UDP协议,不需要三次连接进行握手,而且也会缩短 TLS(HTTPS) 建立连接的时间(默认TLS安全)。QUIC 为了能够广泛部署才只能使用 UDP,其实内部大部分机制还是 TCP。
- 解决了 TCP 队头阻塞问题。可以不严谨的认为在之前的 TCP -> IP 的中间加了一层 UDP,而之前的 TCP+TSL 封装成为一个新的 QUIC 协议,建立在 UDP 之上。“没有什么是多一层不能解决的!”
- 实现动态可插拔,在应用层实现了拥塞控制算法,可以随时切换。
- 报文头和报文体分别进行认证和加密处理,保障安全性。
- 连接能够平滑迁移。
连接平滑迁移指的是,你的手机或者移动设备在4G信号下和WiFi等网络情况下切换,不会断线重连,用户甚至无任何感知,能够直接实现平滑的信号切换。
可以实现这样的原因是,应用层的帧信息传输到传输层最终转为 QUIC 数据包 ,QUIC 数据包会给这些数据加上一些信息,并且加密内容,其中一个重要的信息就是连接 ID,这样在网络发生改变的情况下,WiFi转为4G,IP 地址发生改变,但是客户端和服务端协商好的连接 ID 不变,连接 id 识别为同一个连接,避免再次握手,进一步加快整体传输速度。
对比总结
| 特性 | HTTP/1.0 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|---|
| 连接方式 | 短连接 | 长连接 | 多路复用 | QUIC |
| 并发 | ❌ | ❌(伪并发) | ✅ | ✅ |
| 队头阻塞 | ❌ | ✅ | ⚠️(TCP层) | ❌ |
| 传输格式 | 文本 | 文本 | 二进制 | 二进制 |
| 压缩 | ❌ | ❌ | ✅ | ✅ |
| 协议 | TCP | TCP | TCP | UDP(QUIC) |
