众所周知,TCP与UDP有三点不同:

  1. TCP是面向连接的,UDP是无连接的。
  2. TCP是可靠的,UDP是不可靠的。
  3. TCP是面向字节流的,UDP是面向报文的。

这使得二者有着不同的应用场景:

  • TCP适合需要可靠传输的应用,如HTTP、FTP等。
  • UDP适合对实时性要求高、对可靠性要求低的应用,如视频会议、在线游戏等。

那能不能在UDP上实现TCP的功能呢?结合UDP的速度和TCP的可靠。答案是肯定的。QUIC就是一个基于UDP的传输层协议,它实现了可靠传输、流量控制、拥塞控制等功能。

TCP的问题

TCP主要有以下四个问题,可以简单的记忆为“升延阻迁”:

  1. :TCP已经写到各种OS,升级工作很麻烦。
  2. :TCP的连接建立需要三次握手,连接关闭需要四次挥手,延迟较高。
  3. :TCP没有解决接受窗口队头阻塞问题,一旦有报文没有确认,会导致窗口阻塞。
  4. :TCP通过IP地址和端口号来标识一个连接,这在设备迁移时会导致连接中断。

QUIC是基于UDP实现的可靠传输协议,解决了TCP的这些问题,已经被应用在HTTP/3中。接下来逐一介绍QUIC的设计目标和实现细节,但首先来了解下TCP是怎么实现可靠传输的?

TCP的可靠传输

TCP的可靠传输主要是通过以下几个机制实现的:

  1. 确认和超时重传机制:解决丢包问题。
  2. 序列号机制:解决乱序和重复问题。
  3. 流量控制机制(滑动窗口):匹配收发速率。
  4. 拥塞控制机制:适应网络状况。
  5. 校验和机制:保证数据完整性。
  6. 连接管理机制:确保可靠通道。

QUIC协议

QUIC协议的报文格式

QUIC协议报文在UDP头部和http报文之间,还有三层头部:

  • Packet Header:分为长包头和短包头,其中的包号是严格递增的,重传时也保证包号递增。(这里解决了TCP的重传歧义问题,并且支持乱序确认,避免发送窗口阻塞)
    • 长包头:用于连接建立和连接迁移,包含原目的连接ID、版本号、包号等信息。
    • 短包头:用于数据传输,包含目的连接ID、包号等信息。
  • QUIC Frame Header:一个Packet报文中可以存放多个QUIC Frame,QUIC Frame是QUIC协议的基本单位,类似于TCP的报文段。每一个类型的QUIC Frame的功能和格式都不同。比较常见的是Stream Frame。一个Stream相当于一条HTTP连接,包含流ID、偏移量、长度等信息。
    • Stream ID:多个并发的HTTP连接可以在一个QUIC连接中复用,Stream ID用于区分不同的HTTP连接。
    • Stream Offset:表示当前Stream的偏移量。类似序列号,保证顺序。
    • Stream Length:表示当前Stream的长度。
  • HTTP3 Frame Header

QUIC协议是怎么实现可靠传输的?

解决丢包、乱序重复问题需要三种机制:有序的编号、确认机制和重传机制。QUIC在Packet Header层面解决这些问题:

  • 有序的编号:QUIC的Packet Header中的包号是严格递增的,重传时也保证包号递增。这样就可以通过包号来判断是否丢包。QUIC的Stream Frame中也有Stream ID和Stream Offset,Stream ID用于区分不同的HTTP连接,Stream Offset用于判断当前Stream的偏移量。这样每个Stream帧也是有序的。
  • 确认机制:QUIC只进行包级别的确认:一旦接收方接受到一个包,会通过StreamID和Stream Offset来检查这个包的Stream帧是否丢失或完整,完整的Stream帧会被接受,组装到一起。如果这个包内的帧出现问题,接收方不会确认这个包。
  • 重传机制:发送方没有收到某一个包的确认帧,会把这个包重传,但是会保证包号递增。

QUIC协议是怎么解决队头阻塞问题的?

  • TCP队头阻塞:当TCP接收端没有接收到接受窗口第一个包时,窗口不能往后滑动。TCP发送端没有收到发送端的确认包时,窗口也不能往后滑动。这样就会导致发送端和接收端都阻塞。
  • HTTP/2队头阻塞:HTTP/2使用多路复用,把多个HTTP连接复用在一个TCP连接中,这个一个HTTP流的报文阻塞,会导致整个TCP窗口的队头阻塞。
  • QUIC解决队头阻塞:QUIC基于UDP,每个Stream独立维护发送和接受窗口,互不影响。每一个Stream是有可能出现队头阻塞的,但是QUIC不会导致整个连接的队头阻塞。

QUIC协议是怎么做流量控制的?

QUIC协议通过两个层面进行流量控制:

  • Stream层面:每个Stream维护自己的发送和接收窗口,互不影响。每个Stream的发送和接收窗口是独立的,互不影响。
  • 连接层面:接收方和发送方会告诉对端自己能接受的字节数,限制连接中所有Stream加起来的字节数。

QUIC协议是怎么做拥塞控制的?

  • 拥塞机制和TCP拥塞控制类似。
  • 更新迭代更快速、可以对应用做控制。

QUIC协议是怎么做连接迁移的?

QUIC不通过四元组来标记连接,而是通过连接ID来标记连接。TCP迁移的时候需要重新建立连接,而QUIC协议可以复用连接ID,避免重新建立连接。即使IP变化,QUIC也可以通过连接ID标记连接,只要迁移保留连接ID和密钥等上下文即可。

QUIC协议是怎么做连接管理的?

QUIC内部包含TLS,并且使用TLS 1.3,所以连接只需要一个RTT。具体怎么样,后面再分析吧。

总结

QUIC总体来说有几个特点:包号严格增长,流ID和流偏移量标记帧在流中的位置,解决TCP队头阻塞问题,每个Stream维护自己的窗口、连接管理只要一个RTT,连接迁移不用重新建立连接。