引言
看到这篇文章时,你正在使用它。
没错,它就是 HTTP 协议,运行在计算机网络体系结构的应用层。
“协议(Protocol) “ 在计算机网络中,我们经常看到或听到这个词。什么是协议?协议的本质是什么?
当我们人进行交流的时候,我们要遵循一定的语言规范,中国人之间交流用中文,美国人之间交流用美式英语,英国人之间交流用英式英语 … …. 而 协议 就是计算机网络世界的语言规范,它定义了端系统之间通讯的规范和标准,端系统间共同实现这套通信的标准,方可进行通信。我们熟知的其他协议还有 IP 协议、TCP 协议、UDP 协议等等。
HTTP 简介
HTTP(HyperText Transfer Protocol) 即 超文本传输协议,是一种用于客户端与服务器之间交换信息的协议,主要用于浏览器与Web服务器之间的通信。它定义了如何请求、传输和响应网页内容,是现代互联网应用中最为常见和重要的协议之一。
HTTP 起源
追溯 HTTP 的历史,首先要了解 万维网 (World Wide Web) 的诞生。
在 1989 年,蒂姆·伯纳斯-李 (Tim Berners-Lee) 在 CERN (The European Organization for Nuclear Research) 工作的时候,写了关于在因特网上构建超文本系统 (hypertext system) 的提议。该提议最初被称为 Mesh,后在其被实现的 1990 期间,改名为 World Wide Web 万维网,构建在已有的 TCP/IP 协议之上。该提议包含 4个构建模块:
- 使用文本格式化语言 HyperText Markup Language (HTML) 来表示超文本文档(hypertext document)。
- 使用一个简单的协议 HyperText Transfer Protocol (HTTP) 来传输超文本文档。
- 第一个 Web 浏览器名为 WorldWideWeb 作为客户端,用来显示和编辑 超文本文档。
- 使用一个服务器以让客户端获取超文本文档,httpd 的早期版本。
这四个构建模块于 1990 年末完成,最早一批 Web 服务器于 1991 年初运行在 CERN 之外。蒂姆·伯纳斯-李 于 1991年8月6日,在公共 alt.hypertext 新闻组上发帖。这被现在认为是 World Wide Web 作为公共项目的官方起源。
早期的 HTTP 协议非常简单,后来被称为 HTTP/0.9,也被称为单行协议。
HTTP 版本
HTTP 自诞生至今(2024.12.3),历经了 HTTP/0.9 、HTTP/1.0、HTTP/1.1、HTTP/2.0、HTTP/3.0 共 5 个版本。从最初只是传输简单的文本,到如今支持丰富多媒体数据的传输(包括图片、音频、视频、JSON 等各种格式),HTTP 协议不仅满足了基础的网页浏览需求,也为现代互联网应用的快速发展奠定了基础。
HTTP/0.9 - HTTP 的最初版本
最初的 HTTP 版本(1991 年首次发布)是没有版本号的,后来随着 HTTP 的发展,出现了其他版本。为了区别其他版本,最初的这个版本后来被称为 HTTP/0.9 。
特点:
- 仅有一个 GET 方法。请求报文仅有一行:GET 方法加上请求的资源的路径。
1
GET /page.html
- 仅支持文本传输(HTML)。响应报文仅有请求的数据:
1
2
3<html>
Hello world!
</html> - 无状态协议。每个请求都是独立的,服务器并不保存任何状态,通信过程中没有复杂的管理或控制机制。
HTTP/1.0 - 引入请求头和响应头
HTTP/1.0 于 1996 年发布,改进了 HTTP/0.9 的许多不足之处,增加了请求头和响应头的概念,使得协议能够处理更复杂的 Web 请求。1.0 是第一个得到广泛使用的 HTTP 版本。HTTP/1.0 添加了版本号、各种 HTTP 首部、一些额外的方法,以及对多媒体资源对象的处理。
在这一时期,典型的 HTTP 请求和响应是这样子的:
1 | GET /my-page.html HTTP/1.0 |
上面的请求和响应会在同一个 TCP 连接上进行,请求-响应完毕后,TCP 连接就会关闭。然后在 HTML 页面中,包含了一张图片,则又建立一个新的 TCP 连接来请求图片:
1 | GET /my-image.gif HTTP/1.0 |
HTTP/1.0 定义在 [RFC 1945] 中。
HTTP/1.1 - 引入持久连接与管道化
在 HTTP/1.0 和 HTTP/1.1 发布之间,许多 Web 客户端和服务器在实现 HTTP 协议时加入了各自的特性,导致了协议实现上的混乱。为了统一和标准化 HTTP 协议,HTTP/1.1 于 1997 年初发布,仅在 HTTP/1.0 发布几个月后提出,并被收录在 RFC 2068 中。HTTP/1.1 是第一个正式标准化的 HTTP 协议版本,并在随后的十几年中不断进行更新和完善。
1999 年,RFC 2616 发布,对 RFC 2068 进行了更新和改进,进一步规范了协议内容。RFC 2616 一度成为 HTTP/1.1 的权威规范,并成为全球 Web 开发和通信的基础。然而,随着 Web 技术的演进,RFC 2616 也逐渐暴露出一些不足之处,特别是在细节和可扩展性方面。为了更好地适应现代互联网的需求,2014 年,IETF(Internet Engineering Task Force)发布了系列 RFC,分别是 RFC 7230、RFC 7231、RFC 7232、RFC 7233、RFC 7234 和 RFC 7235,这六个 RFC 对 RFC 2616 进行了拆分和重构,详细地对 HTTP/1.1 协议进行了更新、解释和细化。这些文档分别对不同的协议部分进行了重新定义,以便更清晰、更现代地解决 HTTP 协议在实际应用中的问题。
持久连接:在 HTTP/1.0 中我们可以看到,每一个 HTTP 请求-响应都在同一个 TCP 连接之上,每次请求完毕后都会关闭 TCP 连接,在同一服务器后继到来的 HTTP 请求要重新建立 TCP 连接。熟悉 TCP 的朋友应该都知道 TCP 的连接建立包含了 TCP 三次握手,需要一个 RTT(Round Trip Time) 时延,TCP 连接的关闭 4 次挥手需要两个 RTT 时延。TCP 连接在建立之初,传输数据有个慢启动的过程,为了防止网络拥塞,试探性地逐步地提高发送速率。频繁地建立关闭 TCP 连接,总时延消耗大,用户体验差,也降低了服务器的性能。所以在 HTTP/1.1 中引入了持久化连接,它在 HTTP Headers 中加入了 Connection: keep-alive
字段,即发起该请求后,客户端要求保持 TCP 连接的打开,以进行后继的 HTTP 请求,服务器能够识别并支持 keep-alive
,则应当在响应报文的首部也要加上 Connection: keep-alive
字段,以告知客户端:”我支持持久连接”。这样客户端和服务器之间的多个 HTTP 请求-响应就可以在同一 TCP 连接上进行,减少了 TCP 连接建立和关闭的开销。一个 TCP 连接如果在一段时间内,没有后继的 HTTP 请求,就会关闭,这个 TCP 连接保活的时间是不固定的。
HTTP/1.0 请求图解:
管道化:HTTP/1.1 允许客户端在等待响应的同时发送多个请求,进一步提高了资源请求的并发性。管道化的性能优势在实践中受到了队头阻塞(Head-of-Line Blocking)问题的限制,即如果前面的 HTTP 请求阻塞了,后面所有的 HTTP 请求也会跟着阻塞。
值得注意的是,HTTP/1.1 仍然以文本的形式传输数据。图片、音频、视频等以二进制的形式编码放在响应报文的消息体中进行传输。
SSL/TLS
SSL/TLS - Secure Sockets Layer / Transport Layer Security (安全套接字层 / 传输层安全),该协议运行在 TCP、UDP 之上,HTTP 协议(SMTP、FTP)之下,严格来说 SSL/TLS 协议运行在传输层,它为上层的协议实现安全加密传输服务。
HTTP/1.1 是一个明文的、无加密的协议,而 HTTPS 是通过在 HTTP/1.1 上增加 SSL/TLS 加密机制来实现的。
握手过程中的 SSL/TLS 会先建立一个加密的通道(即通过 TCP 连接的加密握手),然后 HTTP/1.1 的请求和响应在这个加密通道上进行传输。
HTTPS & SSL/TLS 工作流程:
- 客户端(浏览器)向服务器发起连接请求:客户端请求使用 HTTPS 协议(即通过端口 443),这会触发 SSL/TLS 握手过程。
- SSL/TLS 握手:客户端和服务器进行一系列的握手操作,以验证对方身份,并协商加密算法和密钥。在此过程中,服务器会提供一个 SSL/TLS 证书,客户端通过证书验证服务器的身份。
- SSL/TLS 握手流程:
- 客户端Hello(Client Hello)
- 服务器Hello(Server Hello)
- 服务器证书和身份验证
- 客户端密钥交换(Client Key Exchange)
- 密钥生成和会话密钥交换
- 客户端Finished(客户端完成)
- 服务器Finished(服务器完成)
- 握手完成
- SSL/TLS 握手过程中使用非对称加密算法(公钥加密算法)交换密钥,后面的通信数据传输使用对称加密算法(因为对称加密算法更加高效)。非对称加密算法用于保证对称加密密钥的保密性。
- SSL/TLS 握手流程:
- 建立加密连接:握手完成后,客户端和服务器之间建立一个加密的连接,从而可以安全地传输 HTTP 请求和响应数据。
- HTTP/1.1 数据传输:经过 SSL/TLS 加密的 HTTP 数据(请求和响应)在安全的通道上交换。
HTTP/1.1/SSL/TLS 图解:
HTTP/2.0 - 多路复用与头部压缩
HTTP/2.0 于 2015 年发布,基于 Google 提出的 SPDY 协议,并解决了 HTTP/1.x 中的一些性能问题,特别是对于多并发请求的优化。
HTTP/2 支持在一个连接上并行传输多个请求和响应,避免了 HTTP/1.x 中因请求顺序而引起的队头阻塞问题。这样可以大大提高并发请求的性能,特别是在加载页面时需要同时请求多个资源的情况下。
HTTP/2 引入了头部压缩技术,将请求和响应的头部数据进行压缩,减少了冗余信息的传输,从而优化了带宽的使用。
HTTP/2 使用二进制格式而非文本格式,协议更容易解析和处理,减少了通信过程中的错误率和解析开销。
HTTP/2 允许服务器主动向客户端推送资源,而无需客户端显式请求,这对于加速页面加载(尤其是在预知客户端需求时)有很大帮助。
HTTP/2 强制要求实现 SSL/TLS 加密。
HTTP/1.0 系列与 HTTP/2 图解对比:
HTTP/2 的引入解决了 HTTP/1.x 的很多瓶颈,尤其是在性能和并发处理方面。然而,它仍然依赖于 TCP 协议,这意味着如果网络延迟较高,或者出现丢包情况时,TCP 的重传机制可能会影响性能。
HTTP/2.0/SSL/TLS 图解:
HTTP/3.0 - 使用 QUIC 协议
HTTP/3 于 2020 年正式标准化,它基于 Google 提出的 QUIC 协议,并采用了 UDP 协议作为传输层协议。
QUIC (Quick UDP Internet Connections) 快速 UDP 因特网连接协议,运行在 传输层,基于 UDP(用户数据报协议)开发的一个新型传输协议,旨在提供比 TCP 更低的连接建立延迟和更高效的多路复用。
UDP 协议不提供可靠的数据传输,也不像 TCP 那样需要握手建立连接,没有慢启动的过程。UDP 协议非常简单,直接在 IP 数据报上包裹了一下,加上了源端口号、目的端口号、长度、检验和,就进行传输了。
UDP 协议和 TCP 协议的实现通常是在操作系统层面,由操作系统内核中的网络栈(network stack)提供的。操作系统中的网络栈处理从应用层到物理层的所有网络通信过程,保证数据能够顺利从一个设备传输到另一个设备。这意味着,应用程序与 TCP/UDP 进行通信时,不需要直接操作硬件或担心底层的网络细节。它们通过操作系统提供的 API(如 socket 编程接口)来发起网络请求,而操作系统网络栈负责实际的协议实现。
HTTP 协议建立在 TCP 之上,几乎将 TCP 协议的性能发挥到了极致,以至于继续在 TCP 上实现突破遇到了性能的瓶颈(TCP 三次握手,四次挥手,拥塞控制 - 慢启动,队头阻塞问题)。而 TCP 协议的实现在操作系统层面上,想要在操作系统层面更新或者添加新的协议是一个漫长的过程,这要涉及到硬件层面。操作系统层面的更新无法满足当今快速发展的网络应用需求,HTTP 作为一个应用层协议,再想寻求性能的突破只能在 UDP 上实现。随着网络硬件的更新换代,带宽的提升,在现代高速网络环境中,TCP 的某些传统机制未必能够充分发挥网络硬件的优势,从而暴露出性能瓶颈。
在 UDP 上也可以实现可靠的数据传输和拥塞控制。TCP 的可靠数据传输和拥塞控制实现于操作系统层面,QUIC 的可靠数据传输和拥塞控制在自己的协议栈中实现,它把很多传统由操作系统内核完成的功能,如拥塞控制、可靠传输等,都集成到协议本身。
QUIC 协议严格要求加密来建立连接。此外,加密应用于流经连接的所有数据,而不仅仅是HTTP有效载荷,这可以防止整个类别的安全问题。在QUIC中,建立持久连接、协商加密协议,甚至发送第一批数据都被合并到一个请求/响应周期中,从而大大降低了连接延迟。如果客户端在本地缓存了加密参数,则可以通过简化的握手(0-RTT)重新建立与已知主机的连接。
为了解决传输层队首阻塞问题,通过 QUIC 连接传输的数据被分成多个流。流是持久 QUIC 连接内短暂、独立的“子连接”。每个流处理自己的错误更正和交付保证,但使用连接全局压缩和加密属性。每个客户端发起的 HTTP 请求都在单独的流上运行,因此丢失数据包不会影响其他流/请求的数据传输。
UDP 是一种无状态协议(持久连接只是其上的抽象),这使得 QUIC 能够支持很大程度上忽略数据包传送复杂性的功能。例如,客户端在连接过程中更改其 IP 地址(如智能手机从移动网络跳转到家庭 wifi)理论上不应该中断连接,因为该协议允许在不同的 IP 地址之间迁移而无需重新连接。但这也带来了安全挑战,恶意攻击者可以重新发送之前捕获的数据包,这些数据包将被服务器解释为有效且来自受害者。许多 Web 服务器(例如提供静态内容的服务器)不会受到此类攻击的危害。防御措施:QUIC 通过引入 抗重放攻击机制 来应对这一挑战。为了防止攻击者利用之前捕获的 0-RTT 数据包进行重放,QUIC 引入了 Token 机制,确保只有合法的客户端才能重用 0-RTT 数据。并且,0-RTT 数据不会影响到后续的安全性,即使被重放,攻击者也只能重复一些无关紧要的请求,不会泄露敏感数据。
在浏览器中查看请求的 HTTP 版本:
按 F12 键进入网页调试,可以看到 HTTP 请求的版本。
目前(2024.12.3),仍然有很多服务器运行着 HTTP/1.1 ,可以通过反向代理的形式在 HTTP/1.1 上实现 HTTP/2.0。HTTP/2.0 基本上国内外都普及开来,HTTP/3.0 部分国外的 CDN (Content Delivery Network 内容分发网络)上已实现,国内应该也有部分实现。HTTP/3.0 还有很长的路要走。
END