Quic如何实现udp可靠传输

发布于:2025-05-16 ⋅ 阅读:(8) ⋅ 点赞:(0)

QUIC(Quick UDP Internet Connections)是由 Google 设计并被 IETF 标准化的传输层协议,它基于 UDP 实现,但提供了类似 TCP 的可靠性和更高级的功能(如多路复用、0-RTT 握手、TLS 加密等)。

尽管 UDP 是不可靠的,QUIC 实现了自己的机制来提供:

可靠传输、顺序保证、流控、拥塞控制、加密通信


🚀 一句话理解:

QUIC 在 UDP 之上自己造了一个“更好的 TCP”,通过用户态实现可靠传输。


🧩 QUIC 如何在 UDP 上实现可靠传输?

下面是 QUIC 提供可靠性的核心机制:


✅ 1. 数据分片与多路复用(Streams)

  • QUIC 使用 Stream 概念将数据拆分成多个逻辑流,每个流有自己的 ID 和顺序控制。

  • 每个数据包中可能包含多个流的数据片段。

  • 每个 Stream 保持自己的有序性和重传机制,互不影响,解决了 TCP 的“队头阻塞(Head-of-line blocking)”问题。


✅ 2. ACK 机制与重传

  • 每个 QUIC 包都有一个唯一的包序号(Packet Number)。

  • 接收方会通过 ACK frame 显式告诉发送方收到了哪些包(可能是一个范围段,例如 ACK [5,6,7,9])。

  • 发送方根据 ACK 结果进行 选择性重传(Selective Retransmission),而不是像 TCP 那样使用窗口回退(Go-Back-N)。


✅ 3. 流控(Flow Control)

QUIC 实现了两级流控:

类型 控制内容
连接级流控 控制整个连接中所有数据的大小
流级流控 控制单个 Stream 可发送的字节量

避免发送方淹没接收方,防止内存爆炸。

QUIC 实现流控(Flow Control)是为了防止发送方发送太多数据而压垮接收方的内存,确保 发送速度与接收能力匹配

QUIC 的流控机制比 TCP 更灵活,它支持:

连接级流控(Connection-level) + 流级流控(Stream-level)


✅ 一句话总结:

QUIC 使用 MAX_DATAMAX_STREAM_DATA 帧,让接收方“显式告诉”发送方它还能接受多少数据,从而实现精细的流控。


🔍 QUIC 的两个流控层次

类型 控制范围 控制方式
连接级流控 控制整个连接总共能发多少数据 使用 MAX_DATA 帧通知
流级(单条流)流控 控制某条 stream 能发多少数据 使用 MAX_STREAM_DATA 帧通知

🧩 工作机制详解

1. 🚦 初始窗口(Initial Flow Control Window)

  • 每当连接或某个 stream 建立时,接收方通过 Transport Parameters 告诉发送方:

    initial_max_data = 65536       # 整个连接最多能发 64KB
    initial_max_stream_data = 32768 # 每个 stream 最多能发 32KB
    

2. 📤 发送方:按窗口大小发数据

  • 每个 packet 中的数据都有位置偏移(offset)

  • 发送方不能发送超过 offset > max_data 的内容

  • 如果快达到限制,就暂停发送,等接收方“放行”


3. 📥 接收方:接收 + 消费数据后,发送 MAX_*

当接收方:

  • 消费了部分数据(应用层读取了),或者

  • 有更多缓冲空间

就会发以下帧通知发送方放宽限制:

帧类型 作用
MAX_DATA 提高连接级可发送总量
MAX_STREAM_DATA 提高某个 stream 的可发送数据量

📝 例子:

MAX_STREAM_DATA(StreamID=5, MaximumData=64000)
MAX_DATA(MaximumData=128000)

4. 🚧 如果发送方超过限制会怎样?

  • 接收方会直接 丢弃该数据包

  • 并可能发送 FLOW_CONTROL_ERROR 帧,强制断开连接


🔁 动态调整窗口(流控窗口增长)

QUIC 支持动态调整窗口大小,类似 TCP 的“滑动窗口”机制,但更灵活:

  • 接收方可以基于自身处理速度、缓冲区情况等策略动态调整

  • 应用层可参与窗口策略调整(QUIC 是用户态协议)


📊 示例图:QUIC 流控流程简图

       [Sender]                           [Receiver]
          │                                   │
          │───── Data (offset: 0~32767) ─────▶│   ✅ 在窗口范围内
          │───── Data (offset: 32768~65535) ─▶│   ❌ 超出流窗口限制
          │                                   │
          │◀───── MAX_STREAM_DATA (64000) ────│   ✅ 允许再发更多

✅ 总结:QUIC 的流控机制比 TCP 更灵活的地方

特性 TCP QUIC
支持多路复用下的流控 ❌ 需应用层实现 ✅ 原生支持 per-stream 流控
流控信息粒度 每个连接一组窗口 每个流都有单独窗口
流控窗口通告方式 被动通告(ACK + 窗口值) 主动通告(MAX_* 帧)
动态扩展 有,但实现不易 简洁灵活,用户态易扩展

✅ 4. 拥塞控制(Congestion Control)

  • QUIC 可复用 TCP 的拥塞控制算法(如 Reno、CUBIC、BBR 等)。

  • 因为 QUIC 完全在用户态,所以可以快速升级/切换拥塞算法,而不需要内核修改。


✅ 5. 加密与握手(TLS 1.3)


🧵 四、多路复用避免队头阻塞,加速整体数据流

TCP 中多个逻辑流复用一个连接时,某个小包丢失可能导致整个流被阻塞(队头阻塞)。

QUIC 的解决方式:


📶 五、QUIC 支持连接迁移,减少断线重连时间


🔚 小结一句话:

QUIC + UDP 快的根本原因在于它跳过了 TCP 的繁琐握手、内核开销和流阻塞,通过用户态协议 + 0-RTT 加密通信让“发第一个字节”变得更快。


✅ 总结对比表

项目 TCP + TLS QUIC + UDP
握手延迟 至少 1~2 RTT 最快 0-RTT,首次仅需 1 RTT
加密 TLS 分层 内建 TLS 1.3,第一包就加密
队头阻塞 存在 消除(Stream 级别传输)
连接状态 内核态管理 用户态管理,效率高
多路复用 需要上层协议实现 协议原生支持
连接迁移 不支持 原生支持 Connection ID 迁移
适合移动网络 一般 非常适合(连接迁移 + 0-RTT)
  • 所有 QUIC 数据包(除了第一个)都是 强加密 的。

  • 基于 TLS 1.3 实现握手和密钥协商,合并传输和加密握手,支持 0-RTT 和 1-RTT 连接建立

  • 避免了 TCP + TLS 的多轮握手,提高首次连接速度。

  • 🔍 详细原因分析(按阶段分解)


    🟡 一、TCP + TLS 的握手流程 = 至少 3 次往返

    传统的 TCP 连接建立 + TLS 加密握手如下:

    1. TCP 三次握手(1.5 RTT):
       Client → SYN
       Server → SYN-ACK
       Client → ACK
    
    2. TLS 1.2(或 1.3)握手(再来 1~2 RTT)
    
    3. 才能发送加密数据
    

    即:首次连接至少耗时 1~2 RTT(往返时间)


    🟢 二、QUIC 将 握手和加密合并在一次往返中

    QUIC 的握手流程使用 TLS 1.3 直接集成在传输协议中:

  • 首次连接(1-RTT):

  • Client → Initial Packet(携带 TLS Client Hello)   ← 第一次发包就带加密握手
            ← Server Hello + 加密信息(+ 证书等)
    Client → Finished(+ 应用数据)
    
  • 重连(0-RTT):

  • Client → 数据 + 加密握手(直接发数据!)   ← ★ 零延迟
    

    👉 QUIC 可以实现 “0-RTT 数据传输”,几乎是立即发送第一条消息。


    🔧 三、UDP 不需要内核状态建立连接,QUIC 在用户态实现更灵活

  • TCP 是 面向连接的:需要在内核里维护连接状态(socket 状态机)。

  • UDP 是无连接协议,QUIC 在用户态自己管理连接状态,避免了内核/用户态频繁切换。

  • 用户态连接管理允许更快的上下文切换、更容易多路复用。

  • 每个 stream 有独立的传输/重传机制

  • 某一 stream 丢包不会阻塞其他流,数据能更快抵达

  • QUIC 连接绑定在 Connection ID,而不是 IP + Port

  • 移动设备换网(如从 Wi-Fi 切 4G)时无需重新建立连接,延迟更小


✅ 6. 包编号与重排重组

  • QUIC 的包编号是严格递增的,不可重用。

  • 即使包乱序到达,也可通过包号重排序。

  • QUIC 自己实现了类似 TCP 的 SACK(Selective ACK) 功能。 


✅ 7. 头部校验和加密保护

  • 包头也经过加密处理,防止中间人攻击(如修改流量控制字段等)。


📦 总结:QUIC 如何在 UDP 上“可靠传输”

技术点 实现作用
Stream(多路复用) 消除队头阻塞、实现数据隔离
Packet Number + ACK 保证数据到达与丢包检测
重传机制 实现可靠交付
流控 + 拥塞控制 限制数据发送速度,防止拥堵
TLS1.3 加密 确保安全性和身份验证
用户态实现 更灵活,可快速迭代


📌 简图:QUIC vs TCP(在可靠性层面)

         ┌──────────────┐         ┌──────────────┐
         │   应用层     │         │   应用层     │
         └────┬──────▲──┘         └────┬──────▲──┘
              ▼      │                  ▼      │
         ┌──────────────┐         ┌──────────────┐
         │   QUIC 协议  │         │   TCP 协议   │
         └────┬──────▲──┘         └────┬──────▲──┘
              ▼      │                  ▼      │
         ┌──────────────┐         ┌──────────────┐
         │   UDP/IP     │         │   IP层       │
         └──────────────┘         └──────────────┘

QUIC + UDP 连接更快,是因为它在 连接建立阶段(握手)和数据传输机制方面做了大量优化,特别针对 TCP 的延迟瓶颈进行了设计改进。


✅ 简要结论:

QUIC 比 TCP 快,主要因为它省掉了握手轮次 + 消除了 TCP+TLS 分层带来的延迟 + 更高效的连接复用。