> 文章列表 > 如何实现一个可靠的 UDP

如何实现一个可靠的 UDP

如何实现一个可靠的 UDP

QUIC是如何实现可靠传输的?

市面上的基于UDP协议实现的可靠传输协议的成熟方案,应用在HTTP/3上。
UDP报文头部和TCP报文头部夹着三层头部
如何实现一个可靠的 UDP

Packet Header

Packet Header细分这两种:

  • Long Packet Header 用于首次建立连接
  • Short Packet Header 用于日常传输数据
    如何实现一个可靠的 UDP
    QUIC也是需要三次握手来建立连接,主要目的是为了协商连接ID,协商出连接ID后,后续传输时,双方只需要固定住连接ID,从而实现连接迁移功能。
    Short Packet Header 中的 Packet Number 是每个报文独一无二的编号,它是严格递增的,也就是说就算 Packet N 丢失了,重传的 Packet N 的 Packet Number 已经不是 N,而是一个比 N 大的值。
    如何实现一个可靠的 UDP
    TCP重传报文时的序列号和原始报文的序列号一样的话:
  • 如果算成原始请求的响应,但实际上是重传请求的响应(上图左),会导致采样 RTT 变大。
  • 如果算成重传请求的响应,但实际上是原始请求的响应(上图右),又很容易导致采样 RTT 过小。

如何实现一个可靠的 UDP
RTO (超时时间)是基于 RTT 来计算的,那么如果 RTT 计算不精准,那么 RTO (超时时间)也会不精确,这样可能导致重传的概率事件增大。
QUIC 报文中的 Pakcet Number 是严格递增的, 即使是重传报文,它的 Pakcet Number 也是递增的,这样就能更加精确计算出报文的 RTT。
QUIC 使用的 Packet Number 单调递增的设计,可以让数据包不再像 TCP 那样必须有序确认,QUIC 支持乱序确认,当数据包Packet N 丢失后,只要有新的已接收数据包确认,当前窗口就会继续向右滑动
Packet Number 单调递增的两个好处:

  • 可以更加精确计算 RTT,没有 TCP 重传的歧义性问题;
  • 可以支持乱序确认,因为丢包重传将当前窗口阻塞在原地,而 TCP 必须是顺序确认的,丢包时会导致窗口不滑动;

QUIC Frame Header

一个Packet报文中可以存放多个QUIC Frame
如何实现一个可靠的 UDP
每个Frame都有明确的类型,针对类型的不同,功能也不桶,自然格式也不同。
比如Stream类型的Frame格式,Stream可以认为就是一条HTTP请求,它长的样子如下:
如何实现一个可靠的 UDP
Stream ID 作用:多个并发传输的 HTTP 消息,通过不同的 Stream ID 加以区别,类似于 HTTP2 的 Stream ID;
Offset 作用:类似于 TCP 协议中的 Seq 序号,保证数据的顺序性和可靠性;
Length 作用:指明了 Frame 数据的长度。
引入 Frame Header 这一层,通过 Stream ID + Offset 字段信息实现数据的有序性,通过比较两个数据包的 Stream ID 与 Stream Offset ,如果都是一致,就说明这两个数据包的内容一致。
下图中,数据包 Packet N 丢失了,后面重传该数据包的编号为 Packet N+2,丢失的数据包和重传的数据包 Stream ID 与 Offset 都一致,说明这两个数据包的内容一致。这些数据包传输到接收端后,接收端能根据 Stream ID 与 Offset 字段信息将 Stream x 和 Stream x+y 按照顺序组织起来,然后交给应用程序处理。
如何实现一个可靠的 UDP
QUIC 通过单向递增的 Packet Number,配合 Stream ID 与 Offset 字段信息,可以支持乱序确认而不影响数据包的正确组装。