学习笔记之 What is HTTP?系列4:HTTP/3 从头到尾——核心概念

程序员

快速总结 ↬ 经过近五年的开发,新的 HTTP/3 协议已接近最终形态。早期的迭代已经作为实验性功能提供,但您可以预期 HTTP/3 的可用性和使用将在 2021 年适当增加。那么 HTTP/3 到底是什么?为什么在 HTTP/2 之后这么快就需要它?您可以或应该如何使用它?尤其是,它如何提高 Web 性能?让我们来了解一下。


您可能已经阅读了一些博客文章或听过有关此主题的会议演讲,并认为您知道答案。你可能听说过这样的话:“当丢包时,HTTP/3 比 HTTP/2 快得多”,或者“HTTP/3 连接的延迟更短,建立时间更短”,可能还有“HTTP/3可以更快地发送数据,并且可以并行发送更多资源”。

这些陈述和文章通常会跳过一些关键的技术细节,缺乏细微差别,而且通常只是部分正确。通常,它们使得 HTTP/3 看起来好像是一场性能革命,而实际上它是一种更温和(但仍然有用!)的演变。这很危险,因为新协议在实践中可能无法满足这些高期望。我担心这会导致许多人最终感到失望,并且让新手被大量的盲目地长期存在的错误信息所迷惑。

我害怕这一点,因为我们已经看到 HTTP/2 发生了完全相同的情况。它曾被宣传为一场惊人的性能革命,具有令人兴奋的新功能,例如服务器推送、并行流和优先级。我们将能够停止捆绑资源,停止在多个服务器上分片我们的资源,并大大精简页面加载过程。只需轻轻一按开关,网站就会神奇地变快 50%!

五年后,我们知道服务器推送在实践中并没有真正发挥作用,流和优先级的实现通常很糟糕,因此,(减少?)资源 捆绑甚至分片在某些情况下仍然是很好的做法。

同样,对协议行为的其他机制的微调,例如预加载提示,通常包含隐藏的 深度错误,使得它们难以被正确使用。

因此,我认为防止此类错误信息和这些不切实际的期望传播到 HTTP/3 是非常重要的。

在本系列文章中,我将讨论新协议,尤其是它的性能特性,并且会更加细致入微。我将表明,虽然 HTTP/3 确实有一些有前途的新概念,但遗憾的是,它们对大多数网页和用户的影响可能相对有限(但对一小部分人来说可能至关重要)。HTTP/3 的设置和(正确)使用也非常具有挑战性,因此在配置新协议时要小心。

  • 第 1 部分:HTTP/3 历史和核心概念
    本文一般针对 HTTP/3 和协议的新手,主要讨论基础知识。
  • 第 2 部分:HTTP/3 性能特性
    这一部分更深入,也更有技术含量。已经了解基础知识的人可以从这里开始。
  • 第 3 部分:实用的 HTTP/3 部署选项
    本系列的第三篇文章解释了自己部署和测试 HTTP/3 所涉及的挑战。它详细说明了如何以及是否应该更改您的网页和资源。

注意本系列主要面向对协议不一定有深入了解但希望了解更多信息的 Web 开发人员。但它确实包含足够的技术细节和许多指向外部资源的链接,以使更高级的读者也感兴趣。

Why Do We Need HTTP/3?

我经常遇到的一个问题是,“为什么在 2015 年才标准化的 HTTP/2 之后这么快就需要 HTTP/3?” 这确实很奇怪,直到您意识到我们一开始并不真正需要新版本的 HTTP,而是需要对底层的传输控制协议(TCP) 进行升级。

TCP 是提供关键服务的主要协议,例如向 HTTP 等其他协议提供可靠性和按顺序交付等关键服务。这也是我们可以继续与许多并发用户一起使用 Internet 的原因之一,因为它巧妙地将每个用户的带宽使用限制在他们的公平份额内。

你知道吗?

使用 HTTP(S) 时,您实际上是在同时使用包括 HTTP 在内的多种协议。这个“堆栈”中的每个协议都有自己的特性和职责(见下图)。例如,HTTP 处理 URL 和数据解释,传输层安全性 (TLS) 通过加密来确保安全性,TCP 通过重新传输丢失的数据包来实现可靠的数据传输,而互联网协议 (IP) 通过很多中间件将数据包从一个端点路由到另一个端点。

这种相互叠加的协议“分层”是为了方便重用它们的特性。高层协议(如 HTTP)不必重新实现复杂的功能(如加密),因为低层协议(如 TLS)已经为它们完成了这项工作。再举一个例子,Internet 上的大多数应用程序在内部使用 TCP 来确保它们的所有数据都被完整传输。因此,TCP 是 Internet 上使用和部署的最广泛的协议之一。

程序员

几十年来,TCP 一直是网络的基石,但它在 2000 年代后期开始显得老迈。它的预期替代品是一种名为 QUIC 的新传输协议,它在几个关键方面与 TCP 有很大不同,直接在其上运行 HTTP/2 将非常困难。因此,HTTP/3 本身是对 HTTP/2 的一个相对较小的改编,以使其与新的 QUIC 协议兼容,该协议包含了人们兴奋的大部分新功能。

之所以需要 QUIC,是因为 TCP 自 Internet 早期就已经存在,但并不是真正以效率最大化来构建的。例如,TCP 需要一个“握手”来建立一个新的连接。这样做是为了确保客户端和服务器都存在并且他们愿意并且能够交换数据。然而,它也需要一个完整的网络往返才能完成,然后才能对连接进行任何其他操作。如果客户端和服务器在地理上相距遥远,则每次往返时间 (RTT) 可能会花费超过 100 毫秒,从而导致明显的延迟。

又比如,TCP 将其传输的所有数据视为单个“文件”或字节流,即使我们实际上是使用它来同时传输多个文件(例如,当下载由很多资源组成的网页时)。实际上,这意味着如果包含单个文件数据的 TCP 数据包丢失,那么所有其他文件也会延迟,直到这些数据包被恢复。

这称为线头 (HoL) 阻塞。虽然这些低效率在实践中是可以控制的(否则,我们不会使用 TCP 超过 30 年),但它们确实会以明显的方式影响更高级别的协议,例如 HTTP。

随着时间的推移,我们尝试改进和升级 TCP 以改善其中的一些问题,甚至引入新的性能特性。例如,TCP Fast Open 通过允许高层协议从一开始就发送数据,从而消除了握手开销。另一项工作称为 MultiPath TCP。这里的想法是您的手机通常同时具有 Wi-Fi 和 (4G) 蜂窝连接,那么为什么不同时使用它们来提高吞吐量和健壮性呢?

实现这些 TCP 扩展并不很难。然而,在互联网规模上实际部署它们极具挑战性。由于 TCP 如此流行,几乎每个连接的设备都有自己的板载协议实现。如果这些实现太旧、缺少更新或有问题,那么这些扩展实际上将无法使用。换句话说,所有实现都需要已知(这些TCP)扩展才能使其有用。

如果我们只讨论最终用户设备(例如您的计算机或 Web 服务器),这不会有太大问题,因为这些可以相对容易地手动更新。但是,许多其他设备(比如包括防火墙、负载均衡器、路由器、缓存服务器、代理等)位于客户端和服务器之间,它们也有自己的 TCP 板载代码。

这些中间件通常更难更新,有时它们接受的内容更严格。例如,如果设备是防火墙,它可能被配置为阻止所有包含(未知)扩展的流。实际上,事实证明,大量的活动中间件对 TCP 做出了不再适配新扩展的某些假设。

因此,可能需要数年甚至十多年的时间才能更新足够的(中间件)TCP实现以真正的大规模使用扩展。可以说,TCP 已经几乎不可能演进了。

因此,很明显我们需要一个 TCP 协议的替代协议,而不是直接升级它来解决这些问题。然而,由于 TCP 功能及其各种实现的绝对复杂性,从头开始创建新的但更好的东西将是一项艰巨的任务。因此,在 2010 年代初,这项工作被推迟了。

毕竟,不仅 TCP 存在问题,HTTP/1.1 也存在问题。我们选择拆分工作并首先“修复” HTTP/1.1,从而形成现在的 HTTP/2。完成后,替换 TCP的工作就可以开始了,即现在的 QUIC。最初,我们希望能够直接在 QUIC 之上运行 HTTP/2,但在实践中这会使实现效率太低(主要是由于功能重复)。

相反,HTTP/2 在几个关键地方进行了调整,使其与 QUIC 兼容。这个经过调整的版本最终被命名为 HTTP/3(而不是 HTTP/2-over-QUIC),主要是出于营销原因和清晰度。因此,HTTP/1.1 和 HTTP/2 之间的差异比 HTTP/2 和 HTTP/3 之间的差异要大得多。

TAKEAWAY

这里的关键点是,我们真正需要的不是 HTTP/3,而是“TCP/2”,我们在(追寻TCP/2)的过程中“免费”获得了 HTTP/3。我们对 HTTP/3 感到兴奋的主要特性(更快的连接设置、更少的 HoL 阻塞、连接迁移等)实际上都来自 QUIC。

What Is QUIC?

你可能想知道为什么这很重要?谁在乎这些功能是在 HTTP/3 还是 QUIC 中?我觉得这很重要,因为 QUIC 是一种通用传输协议,它与 TCP 非常相似,除了 HTTP 和网页加载之外,还可以并且将用于许多用例。例如,DNS、SSH、SMB、RTP 等都可以在 QUIC 上运行。因此,让我们更深入地了解一下 QUIC,因为我读过的关于 HTTP/3 的大多数误解都来自这里。

您可能听说过的一件事是 QUIC 在另一个协议之上运行,称为用户数据电报协议(UDP)。这是真的,但不是出于许多人声称的(性能)原因。理想情况下,QUIC 应该是一个完全独立的新传输协议,直接在我上面分享的图片中显示的协议栈中的 IP 之上运行。

但是,这样做会导致我们遇到在尝试发展 TCP 时的相同问题:首先必须更新 Internet 上的所有设备才能识别和允许 QUIC。幸运的是,我们可以在 Internet 上另一种广泛支持的传输层协议:UDP 之上构建 QUIC。

你知道吗?
UDP 是最简单的传输协议。除了所谓的端口号(例如,HTTP 使用端口 80,HTTPS 使用 443,DNS 使用端口 53)之外,它实际上并没有提供任何功能。它不会通过握手建立连接,也不可靠:如果 UDP 数据包丢失,它不会自动重传。因此,UDP 的“尽力而为”方法意味着它的性能是您所能得到的最好的:无需等待握手,也没有 HoL 阻塞。在实践中,UDP 主要用于以高速率更新的实时流,因此几乎不会受到丢包的影响,反正丢失的数据很快就会过时(例如实时视频会议和游戏)。它对于需要低显著延迟的情况也很有用;例如,DNS域名查找真的应该一次往返就能完成。

许多消息来源声称,HTTP/3 构建在 UDP 之上是因为性能原因。他们说 HTTP/3 更快,因为就像 UDP 一样,它不建立连接,也不等待数据包重传。这些说法是错误的。正如我们上面所说,UDP 被 QUIC 使用,主要是因为HTTP/3希望更容易部署,因为 UDP 已经为互联网上的(几乎)所有设备所知并实现.

那么,在 UDP 之上,QUIC 基本上重新实现了几乎所有的使 TCP 成为如此强大和流行(但速度较慢)协议的特性。QUIC 是绝对可靠的,它使用对接收到的数据包的回执重传来确保丢失的数据包仍然到达。QUIC 也仍然建立连接并且具有高度复杂的握手

最后,QUIC 还使用所谓的流量控制拥塞控制机制来防止发送方使网络或接收方超载,但这些机制也使 TCP 比原始 UDP 更慢。关键是 QUIC 以比 TCP 更智能、更高效的方式实现了这些功能。它结合了 TCP 数十年的部署经验和最佳实践以及一些核心新特性。我们将在本文后面更深入地讨论这些功能。

TAKEAWAY

这里的关键点是没有免费的午餐。HTTP/3 并没有只是因为我们将 TCP 换成了 UDP,而神奇地比 HTTP/2 快。相反,我们重新构想并实现了更高级的 TCP 版本,并将其称为 QUIC。因为我们想让 QUIC 更容易部署,所以我们将它运行在 UDP 之上。

The Big Changes

那么,QUIC 究竟是如何改进 TCP 的呢?有什么不同?QUIC 中有几个新的具体特性和机会(o-RTT 数据、连接迁移、对丢包和慢速网络的更强弹性),我们将在本系列的下一部分中详细讨论。然而,所有这些新事物基本上归结为四个主要变化:

  • QUIC 与 TLS 深度集成。
  • QUIC 支持多个独立的字节流。
  • QUIC 使用连接 ID。
  • QUIC 使用帧。
    让我们仔细看看这些要点。

THERE IS NO QUIC WITHOUT TLS

如前所述,TLS(传输层安全协议)负责保护和加密通过 Internet 发送的数据。当您使用 HTTPS 时,您的纯文本 HTTP 数据首先由 TLS 加密,然后由 TCP 传输。

你知道吗?
幸运的是,TLS 的技术细节在这里不是必需的;您只需要知道加密是使用一些非常高级的数学和非常大的(质数)数字完成的。这些数学参数是在客户端和服务器之间在单独的 TLS特有的加密握手期间协商的。就像 TCP 握手一样,这种协商可能需要一些时间。在旧版本的 TLS(例如 1.2 及更低版本)中,这通常需要两次网络往返。幸运的是,较新版本的 TLS(1.3 是最新版本)将其减少到只有一次往返。这主要是因为 TLS 1.3 将可以协商的不同数学算法严格限制为少数几个(最安全的几个)。这意味着客户端可以立即猜测服务器将支持哪些算法,而不必等待明确的列表,从而节省了往返行程。

程序员

在 Internet 的早期,加密流量的处理成本很高。此外,它也不被认为对所有用例都是必要的。从历史上看,TLS 一直是一个完全独立的协议,可以选择在 TCP 之上使用。这就是我们区分 HTTP(没有 TLS)和 HTTPS(有 TLS)的原因。

随着时间的推移,我们对互联网安全的态度当然已经转变为“默认安全”。因此,虽然理论上 HTTP/2 可以在没有 TLS 的情况下直接在 TCP 上运行(这甚至在 RFC 规范中定义为 HTTP/2 明文),但没有(流行的)Web 浏览器真的支持这种模式。在某种程度上,浏览器厂商有意识地以性能为代价换取了更高的安全性。

鉴于这种向永远启用 TLS 的明显演变(尤其是对于 Web 流),QUIC 的设计者决定将这一趋势提升到一个新的水平也就不足为奇了。他们没有简单地为 HTTP/3 定义明文模式,而是选择将加密深深地嵌入到 QUIC 本身中。虽然 QUIC 的第一个特定于 Google 的版本为此使用了自定义设置,但标准化的 QUIC 直接使用现有的 TLS 1.3。

为此,它打破了协议栈中协议之间典型的干净分离,正如我们在上图中看到的那样。虽然 TLS 1.3 仍然可以在 TCP 之上独立运行,但 QUIC 封装了 TLS 1.3。换句话说,没有 TLS 就无法使用 QUIC;QUIC(以及扩展的 HTTP/3)始终是完全加密的。此外,QUIC 还加密了几乎所有的数据包标头字段。QUIC 中的中介不再能读取传输层信息(例如从不被 TCP 加密的数据包编号)(甚至某些数据包标头标志也已加密)。

程序员

对于所有这些,QUIC 或多或少地先使用 TLS 1.3 握手(就像TLS-over-TCP)来建立数学加密参数。然而,此后 QUIC 接管并自行加密数据包,而使用 TLS-over-TCP,TLS 是自己加密。这个看似很小的差异代表了向始终启用加密的根本性的概念变化,该加密在更低的协议层强制执行。

这种方法为 QUIC 提供了几个好处:

  • QUIC 对其用户来说更安全
    没有办法运行明文的 QUIC,因此攻击者和窃听者监听的选项也更少。(最近的研究表明HTTP/2 的明文选项是多么危险
  • QUIC 的连接设置更快
    对于 TLS-over-TCP,(TLS和TCP)两种协议都需要各自单独的握手,但 QUIC 将传输和加密握手合二为一,从而节省了往返行程(见上图)。我们将在第 2 部分更详细地讨论这一点。
  • QUIC 可以更容易地发展
    因为它是完全加密的,所以网络中的中间件不能再像对 TCP 一样可以观察和解释其内部工作。因此,它们再也不能因为未能更新(对QUIC的适配),(意外地)在较新版本的 QUIC 中失效。如果我们想在未来为 QUIC 添加新功能,我们“只”需更新终端设备,而不是所有的中间件。

然而,除了这些好处之外,广泛加密也有一些潜在的缺点:

  • 许多网络会犹豫是否允许 QUIC
    公司可能希望在他们的防火墙上阻止它,因为检测不需要的流量变得更加困难。ISP 和中间网络可能会阻止它,因为不再容易获得平均延迟和丢包百分比等指标,这使得检测和诊断问题变得更加困难。这一切都意味着 QUIC 可能永远不会普遍可用,我们将在第 3 部分中进行更多讨论。
  • QUIC 具有更高的加密开销
    QUIC 使用 TLS 加密每个单独的数据包,而 TLS-over-TCP 可以同时加密多个数据包。对于高吞吐量场景,这可能会使 QUIC 变慢(正如我们将在第 2 部分中看到的那样)。
  • QUIC 使网络更加集中
    我经常遇到的抱怨是,“QUIC 正在被 Google 推动,因为它让他们可以完全访问数据,而不会与其他人共享任何数据”。我不太同意这一点。首先,与 TLS-over-TCP 相比,QUIC 不会向外部观察者隐藏更多(或更少!)用户级信息(例如,您正在访问的 URL)(QUIC 保持现状)。其次,虽然 Google 发起了 QUIC 项目,但我们今天讨论的最终协议是由 Internet 工程任务组 (IETF) 中更广泛的团队设计的。IETF 的 QUIC 在技术上与 Google 的 QUIC 非常不同。尽管如此,IETF 的人员确实大部分来自 Google 和 Facebook 等大公司以及 Cloudflare 和 Fastly 等 CDN公司。由于 QUIC 的复杂性,那些有所需知识去正确和高效部署(例如,实践中的 HTTP/3)的公司将是推动此协议的主力。这可能会导致 (此协议) 更加集中在这些公司,这真的令人担忧。

个人笔记:
这是我写这些类型的文章并进行大量技术讨论的原因之一:确保更多的人了解协议的细节,并且可以独立于这些大公司使用它们。

TAKEAWAY

这里的关键点是 QUIC 默认是深度加密的。这不仅提高了其安全性和隐私特性,还有助于其可部署性和可演化性。它使协议运行起来有点重,但作为回报,它允许其他优化,例如更快的连接建立。

QUIC KNOWS ABOUT MULTIPLE BYTE STREAMS

TCP 和 QUIC 之间的第二大区别是技术性更强,我们将在第 2 部分更详细地探讨它的影响。目前我们可以从高层次上来理解主要方面。

你知道吗?
即使是一个简单的网页也是由许多独立的文件和资源组成的。有 HTML、CSS、JavaScript、图像等。这些文件中的每一个都可以被视为一个简单的“二进制 blob”——由浏览器以某种方式解释的 0 和 1 的集合。通过网络发送这些文件时,我们不会一次全部传输它们。相反,它们被细分为更小的块(通常每个大约 1400 字节)并以单个数据包的形式发送。因此,我们可以将每个资源视为一个单独的“字节流”,因为数据会随着时间的推移而零碎地下载或“流式传输”。

对于 HTTP/1.1,资源加载过程非常简单,因为每个文件都有自己的 TCP 连接并完整下载。例如,如果我们有文件 A、B 和 C,我们将有三个 TCP 连接。第一个将看到 AAAA 的字节流,第二个 BBBB,第三个 CCCC(每个字母重复是一个 TCP 数据包)。这可行,但也非常低效,因为每个新连接都有一些开销。

在实践中,浏览器对可以使用的并发连接数量(也就是可以并行下载的文件数量)施加了限制——通常在每个页面只能加载 6 到 30 个之间。只有前一个文件完全传输,才能重新使用该连接来下载新文件。这些限制最终开始阻碍现代化页面上的 Web 性能,(因为)这些页面通常加载超过 30 个资源。

改善这种情况是 HTTP/2 的主要目标之一。该协议不再为每个文件打开一个新的 TCP 连接,而是通过单个 TCP 连接下载不同的资源。这是通过“多路复用”不同的字节流来实现的。这是一种奇特的说法,即我们在传输时混合不同文件的数据。对于我们的三个示例文件,我们将获得一个 TCP 连接,并且传入的数据可能看起来像 AABBCCAABBCC(尽管许多其他排序方案是可能的)。这看起来很简单,并且确实工作得很好,使 HTTP/2 通常与 HTTP/1.1 一样快或快一点,但开销要少得多。

让我们仔细看看区别:

程序员

然而,TCP 端存在一个问题。注意,TCP 是一个很老的协议,而不仅仅用于加载网页,所以它不知道 A、B 或 C。在内部,TCP 认为它只传输单个文件 X,它并不关心它认为的 XXXXXXXXXXXX 实际上是 HTTP 级别的 AABBCCAABBCC。在大多数情况下,这无关紧要(它实际上使 TCP 非常灵活!),但是当网络上出现数据包丢失时,情况就会改变。

假设第三个 TCP 数据包丢失(包含文件 B 的第一个数据包),但所有其他数据都已送达。TCP 通过在新数据包中重新传输丢失数据的新副本来 处理这种丢失。但是,此重传可能需要一段时间才能到达(至少一个 RTT)。您可能认为这不是一个大问题,因为我们看到资源 A 和 C 没有损失。因此,我们可以在等待 B 丢失数据的同时开始处理它们,对吗?

悲催的是,事实并非如此,因为重传逻辑发生在 TCP 层,而 TCP 不知道 A、B 和 C!相反,TCP 认为单个 X 文件的一部分已经丢失,因此它认为它必须阻止 X 的其余数据被处理,直到漏洞被填满。换句话说,虽然在 HTTP/2 级别,我们知道我们已经可以处理 A 和 C,但 TCP 不知道这一点,这导致处理速度比可能的要慢 。这种低效率是“线头(HoL)阻塞”问题的一个例子。

解决传输层的 HoL 阻塞是 QUIC 的主要目标之一 。与 TCP 不同,QUIC 清楚地意识到它正在复用多个独立 的字节流。当然,它不知道它正在传输 CSS、JavaScript 和图像。它只知道这些流是分开的。因此,QUIC 可以在每个流的基础上执行丢包检测和恢复逻辑。

在上面的场景中,它只会保留流 B 的数据,与 TCP 不同的是,它会尽快将 A 和 C 的任何数据传递到 HTTP/3 层(如下图所示)理论上,这可以提高性能。然而,在实践中,这个故事要微妙得多,正如我们将在第 2 部分中讨论的那样。

程序员

可以看到,现在在 TCP 和 QUIC 之间有了根本的区别。顺便说一句,这也是我们不能像在 QUIC 上那样运行 HTTP/2 的主要原因之一。正如我们所说,HTTP/2 还包括在单个 (TCP) 连接上运行多个流的概念。因此,HTTP/2-over-QUIC 将有两个不同且相互竞争的流抽象。

让它们很好地协同工作将非常复杂且容易出错;因此,HTTP/2 和 HTTP/3 之间的主要区别之一是后者删除了 HTTP 流逻辑并重用了 QUIC 流 。但是,正如我们将在第 2 部分中看到的那样,这会对服务器推送、标头压缩和优先级等功能的实现方式产生其他影响。

TAKEAWAY

这里的关键点是 TCP 从未设计为通过单个连接传输多个独立文件。因为这是网络浏览所需要的,所以多年来这导致了许多低效率。QUIC 通过将多字节流作为传输层的核心概念并在每个流的基础上处理数据包丢失来解决这个问题。

QUIC SUPPORTS CONNECTION MIGRATION

QUIC 的第三个主要改进是连接可以保持更长时间。

你知道吗?

在谈论 Web 协议时,我们经常使用“连接”的概念。然而,究竟什么是连接?通常,一旦两个端点(例如,浏览器或客户端与服务器)之间发生握手,人们就会谈论一个 TCP 连接。这就是为什么 UDP 经常(有点被误导)被称为“无连接”的原因,因为它不做这样的握手。但是,握手实际上并没有什么特别之处:它只是发送和接收具有特定形式的几个数据包。它有几个目标,其中主要是确保另一边有一些东西,并且它愿意并且能够与我们交谈。值得在这里重复一下,QUIC 也会执行握手,即使它是通过 UDP 运行,而 UDP 本身并不会握手。

那么,问题就变成了,这些数据包如何到达正确的目的地?在 Internet 上,IP 地址用于在两台不同的机器之间路由数据包。然而,仅仅拥有手机和服务器的 IP 是不够的,因为两者都希望能够同时运行多个联网程序。

这就是为什么每个单独的连接也在两个端点上分配一个端口号 ,以区分连接和它们所属的应用程序。服务器应用程序通常根据其功能有一个固定的端口号(例如端口 80 和 443 用于 HTTP(S),端口 53 用于 DNS),而客户端通常为每个连接(半)随机的选择其端口号。

因此,要定义跨机器和应用程序的唯一连接,我们需要这四个东西,即所谓的 4 元组:客户端 IP 地址 + 客户端端口 + 服务器 IP 地址 + 服务器端口

在 TCP 中,连接仅由 4 元组标识。因此,如果这四个参数中只要有一个发生了变化,则连接将变为无效并需要重新建立(包括新的握手)。要理解这一点,请想象一下停车场问题:您当前在带有 Wi-Fi 的建筑物内使用智能手机。因此,您在此 Wi-Fi 网络上有一个 IP 地址。

如果您现在走到外面,您的手机可能会切换到蜂窝 4G 网络。因为这是一个新网络,它将获得一个全新的 IP 地址,因为这些IP地址是特定于网络的。现在,服务器将看到来自它以前从未见过的客户端 IP 的 TCP 数据包(当然,这两个端口和服务器 IP 可以保持不变)。如下图所示。

程序员

但是服务器怎么知道这些来自一个新IP的数据包属于老的连接呢?它如何知道这些数据包不属于来自蜂窝网络中另一个客户端的 连接,该客户端碰巧选择了相同的(随机)客户端端口(这很容易发生)?可悲的是,它无法知道这一点。

因为 TCP 是在我们甚至梦想到蜂窝网络和智能手机之前发明的,例如,没有任何机制允许客户端让服务器知道它已经更改了 IP。甚至没有办法“关闭”连接,因为发送到旧 4 元组的 TCP 重置或结束命令甚至无法到达客户端。因此,在实践中,每次网络更改都意味着无法再使用现有的 TCP 连接

必须执行新的 TCP(可能还有 TLS)握手以建立新连接,并且根据应用程序级协议,需要重新启动进程中的操作。例如,如果您正在通过 HTTP 下载一个大文件,则可能必须就重新请求该文件(例如,如果服务器不支持范围请求)。另一个例子是实时视频会议,在切换网络时您可能会遇到短暂的停电。

请注意,4 元组可能会更改还有其他原因(例如,NAT 重新绑定),我们将在第 2 部分中详细讨论。

因此,重新启动 TCP 连接可能会产生严重影响(等待新的握手、重新启动下载、重新建立上下文)。为了解决这个问题,QUIC 引入了一个名为连接标识符(CID) 的新概念。每个连接在 4 元组之上分配了另一个编号,该编号在两个端点之间唯一标识它。

至关重要的是,因为这个 CID 是在 QUIC 本身的传输层定义的,所以在网络之间移动时它不会改变!如下图所示。为了实现这一点,CID 包含在每个 QUIC 数据包的前面(很像 IP 地址和端口也出现在每个数据包中)。(它实际上是 QUIC 数据包标头中为数不多的未加密的东西之一!)

程序员

通过这种设置,即使 4 元组中的某一项发生了变化,QUIC 服务器和客户端只需查看 CID 即可知道它是同一个旧连接,然后他们可以继续使用它。不需要重新握手,下载状态可以保持原样。此功能通常称为连接迁移 。从理论上讲,这对性能更好,但正如我们将在第 2 部分中讨论的那样,这当然又是一个微妙的故事。

CID 还有其他挑战需要克服。例如,如果我们确实只使用一个 CID,那么黑客和窃听者就可以非常容易地跨网络跟踪用户,进而推断出他们的(近似)物理位置。为了防止这种隐私噩梦,QUIC 每次使用新网络时都会更改 CID

不过,这可能会让您感到困惑:我刚才不是说 CID 在网络中应该是相同的吗?好吧,那是过于简单化了。真正在内部发生的是客户端和服务器在一个共同的(随机生成的)CID 列表上 达成一致,这些 CID都映射到相同的概念“连接”。

例如,他们都知道 CID K、C 和 D 实际上都映射到连接 X。因此,虽然客户端可能在 Wi-Fi 上使用 K 标记数据包,但它可以切换到在 4G 上使用 C。这些通用列表在 QUIC 中协商完全加密,因此潜在的攻击者不会知道 K 和 C 真的是 X,但客户端和服务器会知道这一点,并且他们可以保持连接处于活动状态。

程序员

情况变得更加复杂,因为客户端和服务器将具有他们自己选择的不同 CID 列表(就像它们具有不同的端口号一样)。这主要是为了支持大规模服务器设置中的路由和负载均衡,我们将在第 3 部分中更详细地看到。

TAKEAWAY

这里的关键点是,在 TCP 中,连接由四个参数 定义,这些参数可以在端点更改网络时更改。因此,这些连接有时需要重新启动,从而导致一些停机时间。QUIC 添加了另一个参数,称为连接 ID。QUIC 客户端和服务器都知道哪些连接 ID 映射到哪些连接,因此对网络变化更加健壮。

QUIC IS FLEXIBLE AND EVOLVABLE

QUIC 的最后一个特点是它经过专门设计,易于发展。这是通过几种不同的方式实现的。首先,正如所讨论的,QUIC 几乎是完全加密的,这意味着如果我们想要部署更新版本的 QUIC,我们只需要更新端点(客户端和服务器),而不是所有中间件。这仍然需要时间,但通常是几个月,而不是几年。

其次,与 TCP 不同,QUIC 不使用单个固定的数据包头来发送所有协议元数据。相反,QUIC 具有较短的数据包标头,并在数据包有效负载内使用各种“帧”(有点像微型专用数据包)来传达额外信息。例如,有一个ACK帧(用于确认)、一个NEW_CONNECTION_ID帧(用于帮助建立连接迁移)和一个STREAM帧(用于承载数据),如下图所示。

这主要是作为一种优化完成的,因为不是每个数据包都携带所有可能的元数据(因此 TCP 数据包标头通常会浪费相当多的字节 - 另见上图)。然而,使用帧的一个非常有用的副作用是,将来将新的帧类型定义为 QUIC 的扩展将非常容易。例如,一个非常重要的帧是DATAGRAMframe,它允许通过加密的 QUIC 连接发送不可靠的数据。

程序员

第三,QUIC 使用自定义 TLS 扩展来携带所谓的传输参数。这些参数允许客户端和服务器为 QUIC 连接选择配置。这意味着他们可以协商启用哪些功能(例如,是否允许连接迁移、支持哪些扩展等)并传达某些机制的合理默认值(例如,支持的最大数据包大小、流量控制限制)。虽然 QUIC 标准定义了一个很长的列表,但它也允许扩展定义新的参数,使协议更加灵活。

最后,虽然并不是 QUIC 本身真正的要求,但目前大多数实现都是在“用户空间”中完成的(与 TCP 不同,后者通常在“内核空间”中完成)。细节在第 2 部分中讨论,但这意味着它比 TCP 更容易试验和部署 QUIC 实现变体和扩展。

TAKEAWAY

虽然 QUIC 现在已经标准化,但它确实应该被视为QUIC 版本 1 (在征求意见 (RFC)中也明确说明了这一点),并且有明确的意图创建版本 2 和更快的速度。最重要的是,QUIC 允许轻松定义扩展,因此可以实现更多用例。

Conclusion

让我们总结一下我们在这一部分中学到的东西。我们主要讨论了无处不在的 TCP 协议,以及它是如何在对当今许多挑战未知的(古老)时代设计的。当我们试图发展 TCP 以跟上(时代)时,很明显这在实践中会很困难,因为几乎每个设备都有自己的 TCP 板载实现,这都需要更新(几乎是不可能的)。

为了在改进 TCP 的同时绕过这个问题,我们创建了新的 QUIC 协议 (实际上是 TCP 2.0)。为了使 QUIC 更易于部署,它运行在 UDP 协议(大多数网络设备也支持)之上,并确保它可以在未来发展,默认情况下它几乎完全加密并使用灵活的帧机制。

除此之外,QUIC 主要反映已知的 TCP 特性 ,例如握手、可靠性和拥塞控制。除了加密和帧之外的两个主要变化是对多字节流的认识和连接 ID 的引入。然而,这些更改足以阻止我们直接在 QUIC 之上运行 HTTP/2,因此需要创建 HTTP/3(实际上是 HTTP/2-over-QUIC)。

QUIC 的新方法让位于许多性能改进,但它们的潜在收益比 QUIC 和 HTTP/3 文章中通常传达的更为细微。现在我们了解了基础知识,我们可以在本系列的[下一部分中更深入地讨论这些细微差别。

本作品采用《CC 协议》,转载必须注明作者和本文链接
日拱一卒
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
93
粉丝
85
喜欢
153
收藏
121
排名:71
访问:11.4 万
私信
所有博文
社区赞助商