QUIC 的大多数杀手级功能都不明显或没有记录在案。 是时候改变这种状况了。
基础知识
在我们介绍高级内容之前,我们需要介绍基础知识。
您仍然可以在 [CURRENT_YEAR] 中使用 TCP(或 WebSockets),但您错过了。QUIC 是块上的新传输协议,它将慢慢接管互联网。
QUIC公司
想象一下,你是一个像我一样谦虚(和英俊)的应用程序开发人员。 你从这个很酷的网站听说这个很酷的新协议,有一个很酷的域名。 生活是美好的,但你总是可以用一些新技术来增添趣味。
在我们回答“为什么”之前,让我们先来介绍一下 HOW。 什么是 QUIC API?我该如何使用它?
为了简化起见,QUIC 为您提供了一个像 TCP 这样的字节流。 但与 TCP 不同的是,在任一端打开/关闭其他流都不需要任何成本。
为什么这很有用?
- 如果您曾经使用过连接池,请删除该狗屎并使用 QUIC。
- 如果您曾经多路复用流,请删除该狗屎并使用 QUIC。
HTTP协议
这正是创建QUIC的原因。 事实证明,根据版本的不同,HTTP会遇到以下两个问题:
- HTTP/1 使用连接池,通常每个域最多 6 个连接。
- HTTP/2 多路复用请求,引入了线头阻塞。
删除那个狗屎并使用 HTTP/3。
然而,作为一个谦虚(和帅气)的应用程序开发人员,我并不关心 HTTP/3。
问题是HTTP版本对我的应用程序是透明的。 如果你访问我的网站,你可以使用任何版本的HTTP,基本上它的工作方式都是一样的。 但是,如果并行发出大量请求以尝试利用 QUIC 的并发性,那么我的应用程序绝对会窒息在较旧的 HTTP 版本上。
不要误会我的意思,HTTP/3 可能是一种改进,但它是一种渐进式改进。 除非您是 CDN 供应商,否则这并不令人兴奋,我将在下面提到的所有原因。
网络传输
坦率地说,这通常不值得与HTTP语义作斗争。 如果你想制作一个使用 QUIC 的 Web 应用程序,那么直接使用 QUIC 即可。
幸运的是,我们有 WebTransport 来做到这一点。 它主要公开 QUIC API,并在 Chrome/Edge/Firefox 中受支持,Safari 即将™推出。
您可以将 WebTransport 视为 WebSocket 的替代品,但使用 QUIC 而不是 TCP。
高级狗屎
无论如何,是时候掌握技术了。 对于日常谦虚(和帅气)的应用程序开发人员来说,其中大部分并不重要,但也许您会发现一些有用的东西。
我在编写自己的 QUIC 实现时弄清楚了其中的大部分。 但不要成为我。 请改用现有实现。 我现在是 Rust 的粉丝,所以我将 Quinn 与 WebTransport 一起使用。 CloudFlare的Quiche也相当不错,可以与C / C++接口。
但是,如果您在飞机上感到非常无聊,则可以阅读RFC。 如果你喜欢这种东西,它们写得非常好,设计得非常好。
连接 ID
如果你上过网络课,我很抱歉。 但您可能还记得,TCP 连接由 4 元组标识。 内核使用此信息将传入的数据包映射到正确的套接字/连接:
- 源 IP
- 源端口
- 目标 IP
- 目标端口
每个 IP 数据包都包含源 ip/端口和目标 ip/端口,因此您知道谁发送了它以及将回复发送到何处。 有点像邮件,如果有人年纪大到可以记住这一点。
QUIC已经不复存在了。
QUIC 连接由连接 ID 标识。 这是一个不透明的数据 blob,由接收方选择,在每个 QUIC 数据包的标头中发送。
乍一看,这似乎无关紧要,但实际上却是一件大事。
漫游
曾经在蜂窝和 WiFi 之间切换过吗? 你当然有,你有一部电话。 切换网络时,源 IP/端口会发生变化。
使用 TCP 时,这会更改 4 元组。 服务器将静默丢弃来自未知来源的任何数据包,从而切断连接。
应用程序需要检测到此问题并重试,拨打新连接并重新发出任何挂起的请求。 重试逻辑总是很痛苦,通常用户只需要手动刷新即可。
使用 QUIC 时,源地址将更改,但连接 ID 保持不变。 在切换到新路径之前,QUIC 堆栈将透明地验证新路径(以防止欺骗攻击)。 该应用程序不需要做任何事情,它就可以工作了™
NAT 重新绑定
在上述场景中,客户端可以知道其 IP 地址在切换网络时发生了变化。 一个好的客户端可以主动重新连接,而不是等待连接超时。
但是,NAT 并非如此。 NAT很烂。 NAT 可以在您不知情的情况下透明地重新绑定您的地址,通常是在一段未指定的不活动时间之后,但有时只是因为它们很糟糕
在 TCP 领域,NAT 重新绑定是致命的,因为 4 元组会发生变化,数据包将被静默丢弃。 应用程序不知道发生了这种情况,并且连接似乎挂起。 这很令人讨厌,也是为什么你需要某种形式的保持活动来检测和抵御任何 NAT 重新绑定。
在 QUIC 领域,连接 ID 可以节省大量时间。 QUIC 仍然需要 1 个 RTT 来验证新路径,但它可以正常工作™
防火墙
为了简化叙述,我一直非常关注 TCP,但现在是时候选择 WebRTC 了。 这很复杂,但 WebRTC 实际上根据 2 元组来识别连接:
- 目标 IP
- 目标端口
这意味着源 IP/端口可以在不切断连接的情况下更改:ez 漫游和 NAT 重新绑定支持!
但是为什么 QUIC 不这样做呢? 两个可怕的词:企业防火墙。
服务器需要为每个连接打开一个唯一的端口。 但是,出于“安全原因”,企业防火墙通常会阻止除少数端口之外的所有端口。 有一次,我调查了 Twitch 公司网络,发现只有大约 ~30 个 UDP 端口处于打开状态;其他一切都被阻止了。
QUIC 改用于所有连接。 这是巨大的,因为防火墙通常允许端口 443 用于 HTTPS,但会阻止几乎所有其他内容。 如果使用 QUIC,则会自动利用此防火墙打孔。udp/443
请注意,您可以(并且我已经)破解WebRTC服务器来监听。 但是,就像 TCP 一样,您将失去漫游支持,因为您依赖 4 元组来识别连接。udp/443
有趣的事实:QUIC 客户端也可以对所有传出连接使用相同的 IP 和端口。 不需要临时端口!
负载均衡
当您使用互联网时,您几乎从未连接到单个服务器。 在负载均衡器后面有一个秘密的服务器队列,用于分配负载。
你看不到他们,但他们在看着。 等待。
连接 ID
正如我之前提到的,连接 ID 是接收方选择的不透明数据 blob。 接收者可以使用多个ID,随意发行或停用,长度不受限制。
这是巨大的。 您友好的邻居服务器管理员垂涎三尺。
由于服务器选择自己的连接 ID,因此它可以将会话信息编码到其中。 这可以包括路由信息,由客户端在每个数据包的标头中回显。 基本上,您可以对所需的任何负载平衡信息进行编码。
有一整份草稿专门介绍你可以用这个做的很酷的狗屎。 这里有一个免费的想法:将后端服务器的名称编码到连接 ID 中。 现在,您拥有没有查找表的粘性会话!
话虽如此:
- 开销很重要:不要占用具有 1KB 连接 ID 的 1.2KB QUIC 数据包。
- 安全性:连接 ID 是唯一的明文数据,因此只需确保它是加密的/不可猜测的。
但是你可以做很多有创意的事情!
任播
哦,博伊。 这个隐藏得非常好,但非常强大。
在握手期间,服务器可以回复一个可选的、有点晦涩难懂的preferred_address。 当然,客户端将在验证数据包后尝试将数据包发送到此地址。 这到底为什么重要?
所以有一种叫做任播的东西。 Anycast 允许多个服务器通告相同的 IP 地址,任何数据包都将路由到“最近”的服务器(基于 BGP)。 这对于全局负载平衡来说是惊人的,因为它还使网络管理员能够控制路由。 最近服务器的路径可能拥塞,因此 ISP 可以选择其他“最近”的服务器。
但是,如果“最近”的服务器因任何原因而更改(抖动),不幸的是,其他服务器将收到您的数据包。 即使使用 QUIC,新服务器也会丢弃这些未知数据包并切断连接。
由于此抖动问题,Anycast 通常保留给 DNS 等 UDP 协议。 通常最好将单播与 TCP 一起使用,以确保一致的路由。
但是,如果您可以使用任播进行发现,然后切换到单播以保持一致性,那会怎样? 这正是它的用途。preferred_address
每个服务器只能通告用于 TLS 握手的共享任播地址。 之后,客户端被告知切换到服务器的唯一单播地址。 最终结果是与最近的服务器建立稳定的连接,对应用程序透明!
今天还有其他方法可以实现这一点,但它们有点糟糕。GeoDNS非常不准确,因为它(通常)涉及一个粗略的IP查找数据库。 应用程序可以使用像 Happy Eyeballs 这样的东西来并行尝试多个地址,但它既昂贵又繁琐。
我大胆的预测是,一旦 QUIC 的任播握手被广泛部署,它就会接管。
隐私
QUIC对隐私采取偏执的态度。 TLS 加密是必需的,甚至数据包标头也被加密以惹恼中间盒。
什么是中间盒? 嗯,对于路由器来说,这是一个花哨的词,它弄清楚了如何将 IP 数据包从 A 点传输到 B 点。 问题在于,这些中间盒可以检查并可能修改数据包。 一个怀有恶意的中间盒可以监控流量,限制流量,甚至注入广告。
QUIC 的解决方案是加密所有内容*。 甚至数据包号也被加密,因为他妈的中间盒。 但请注意,这并不完美:
- QUIC 握手使用硬编码的初始密钥。 中间盒可以解密此流量以确定服务器名称,除非您使用的是 ESNI。
- QUIC 连接 ID 将泄漏有关连接数及其活动的信息。MASQUE 允许您嵌套多个 QUIC 连接,有效地作为 VPN 运行。
只是为了多选择 WebRTC;SRTP 标头(包括任何扩展名)未加密。 中间盒可以轻松检查标头,确定 WebRTC 流量的类型,甚至找出当前正在通话的参与者。 当然,WebRTC 因泄露您的 IP 地址而臭名昭著。
攻击
UDP 协议通常非常容易受到攻击,通常会意外地放大它们。 TCP SYN 洪水也是一种常见的 DDoS 向量。QUIC 也不能幸免于攻击,但有一些巧妙的缓解技术。
当然,您现在应该知道连接 ID 很棒。 QUIC 服务器可以对连接 ID 进行签名,以防止客户端篡改或欺骗它。
为什么这很重要? 现在,协作路由器 (L3) 可以在滥用数据包进入网络时丢弃它们。 否则,数据包必须先到达 QUIC 服务器 (L7),然后才能进行处理。 这可以在硬件中完成,以获得最大效率,特别是因为它不需要状态。
事实上,如果路由器检测到滥用,甚至可以发送无状态重置以关闭连接。 服务器在握手期间选择重置令牌,如果它是确定性的,那么路由器也可以计算它。
这里有一个充满创意的建筑世界,等待着我们去探索。 我敢肯定,谷歌和CloudFlare的人现在正在做一些很酷的事情。
拥塞控制
QUIC 的拥塞控制是以 TCP 为模型的,但有一些重要的区别需要指出:
- QUIC 数据包具有唯一的编号,因此接收方可以区分重传。
- QUIC ACK 包括无限*范围,因此接收器可以更准确地报告单个损耗。
- QUIC ACK 包括批处理延迟,因此接收器具有准确的 RTT 测量值。
这有明显的区别吗? 不是真的,但 QUIC 的拥塞控制应该稍微好一点。 QUIC 实现仍在调整和优化中,因此请对这些初始结果持保留态度:
有趣的事实:QUIC ACK 本身是被确认的,因此您可以有效地确认 ACK。 后续的 ACK 不包含 ACK 的 ACK 数据包编号,节省带宽!
部署
但是,TCP 和 QUIC 拥塞控制之间存在主要区别。 TCP 是在内核中实现的,这意味着它很难或不可能修改。
- Windows 客户端卡在 Windows 中糟糕的 TCP 实现。
- OSX 客户端被 OSX 中蹩脚的 TCP 实现卡住了。
- Android 客户端被 Android 中蹩脚的 TCP 实现卡住了。
- iOS 客户端在 iOS 中被蹩脚的 TCP 实现卡住了。
- Linux 客户端被 Linux 中蹩脚的 TCP 实现卡住了。
你明白了。 据我所知,每个操作系统的默认 TCP 拥塞控制都是基于损失的,并且存在缓冲区膨胀,因此对于延迟敏感的应用程序来说很差。 请注意,您可以配置或安装自定义内核来更改 TCP 的行为,但这主要适用于高级用户或服务器操作员。
另一方面,QUIC 尚未在内核中实现。 当您交付客户端时,您将交付自己的供应商 QUIC 实现。 这意味着您的应用程序可以更好地控制拥塞算法(例如 BBR),这在迄今为止是不可能的。
事实上,您甚至可以尝试自己的拥塞控制算法。 我对 BBR 进行了一些改进,以更好地支持实时视频等受应用程序限制的流量。 在用户空间中运行实验也要容易得多。
数据报
数据报基本上是 UDP 数据包。 它们不可靠、无序且具有最大大小(至少 1.2KB)。
QUIC 通过扩展支持数据报。 此扩展是 WebTransport 的一部分所必需的,这意味着浏览器中的数据报支持!
但是,有一些注意事项:
- 数据报是拥塞控制的。QUIC 数据报在后台确认,仅用于计算最大发送速率。应用程序无法实现自己的拥塞控制,因为它受到 QUIC 拥塞控制的限制。
- 数据报被确认,但不公开。应用程序必须实现自己的可靠性机制,因此 QUIC ACK 大多被浪费了。
- 数据报不能发送到任意目标。如果要数据包到多个端口,则必须建立单独的 QUIC 连接。
- 数据报可以合并到单个 QUIC 数据包中。这对效率非常有用,因为这意味着发送的数据包更少。但是,这意味着应用程序不能依赖于数据报实际上是独立的,这就是重点。
热门警报:切勿使用 QUIC 数据报。 它们具有 UDP 的所有缺点,没有任何好处,并且将一些脚枪扔进去。 他们很烂。
您应该改用 QUIC 流。 为每个逻辑单元(例如视频帧)创建一个 QUIC 流,并根据需要确定它们的优先级/关闭它们。 您可以免费获得碎片、排序、可靠性、流量控制等,再也不用考虑 MTU。
基于 QUIC 的媒体
我知道这是一篇积极的博客文章,但我想更多地扣篮 QUIC 数据报。
数据报的使用实际上是 Media over QUIC 和 RTP over QUIC 工作之间的核心区别。 两者都在努力改进WebRTC,但我在团队中“制作一个全新的协议”。
上述所有原因实际上都阻止了使用 QUIC 数据报而不是 UDP 天真地实现 RTP:
- 数据报是拥塞控制的。这意味着您不能在 QUIC 数据报之上实现 GCC 或 SCReAM。
- 数据报已确认,但您不能使用它们。您将发送不同层的 ACK 和 NACK,从而增加开销并损害性能。
- 数据报不能发送到任意目标。这意味着您不能将 RTP 和 RTCP 发送到不同的端口;它们必须被复用。(没什么大不了的)
- 数据报可以合并到单个 QUIC 数据包中。这使得实现 FEC 变得更加困难,因为 QUIC 数据报可能会被秘密合并。
这只是 RTP 的一个示例,但您最喜欢的基于 UDP 的协议可能也是如此。 请改用 QUIC 流。
注意:基于 QUIC 的媒体可能支持数据报,主要用于实验。 我已经表达了我的观点,但不幸的是,我不是IETF的老板。
STREAM_FIN
由@kixelated撰写。
我对 QUIC 和 WebTransport 非常着迷。 现在是成为传输协议书的最佳时机。
就我个人而言,我现在有一份有报酬的工作。 这意味着我得到报酬是为了真正制作有用的东西,而不是写内容丰富的博客文章。 对你来说很不幸,但对我来说很幸运,因为我现在有了健康保险(感谢美国)。
我无法花那么多时间为 Media 而不是 QUIC 的啦啦队,但标准仍在全力以赴。 请记住:它是由来自 Google、Meta、Cisco、Akamai 以及整个 IETF 的个人共同撰写的。
不过,请加入 discord 服务器。
原文地址: https://quic.video/blog/quic-powers/