QUIC协议 - 5 - 流量控制
流量控制的目的是保护接收端。确保发送方的发送速度不会超过接收方的处理速度,从而防止接收方的内存缓冲区被占满溢出。
QUIC 的流量控制机制是基于信用而非滑动窗口,并且QUIC拥有两个层次的控制:连接层和流层。
TCP的的队头阻塞
对于TCP来说,假设发送了5,6,7,8序号的包,如果5号包的ACK丢了,那么即使6、7、8都已经收到ACK了,那也没办法往右滑动,必须得等5的ACK回来了,才能往右滑动,这就是经典的队头阻塞问题。
QUIC杜绝了这个问题。
QUIC的流量控制
QUIC采用了一种更直观的“信用”模型:
- 授予信用: 接收方通过发送控制帧,明确告知发送方“你最多可以向我发送多少字节的数据”。这个字节数就是“信用额度”或“流量控制窗口”。
- 消耗信用: 发送方每发送一个字节的数据,就消耗一点信用额度。
- 等待信用: 当发送方耗尽了接收方授予的信用额度后,它必须停止发送该范围内的数据,直到接收方处理完已收到的数据,并发送新的控制帧来授予更多信用(即更新窗口)。
双层控制
QUIC不仅在整个连接上进行流量控制,还在每个独立的流上进行控制。
连接层流量控制
- 目的: 保护接收方为整个 QUIC 连接分配的总内存缓冲区。这是对所有流的一个全局上限。
- 工作方式:
    - 接收方设定一个连接级别的最大可接收字节数。
- 发送方必须确保,在所有流上发送的数据总字节数,不能超过这个上限。
- 当接收方处理并向上层应用交付数据后,会释放这部分缓冲区空间,并通过发送 MAX_DATA帧来提高这个上限,从而授予发送方更多“信用”。
 
流层流量控制
- 目的:
    - 保护接收方为单个流分配的内存缓冲区。
- 防止队头阻塞:避免一个大流量、高延迟或者被应用层暂停读取的流,耗尽整个连接的流量控制窗口,从而阻塞其他正常流的数据传输。
 
- 工作方式:
    - 接收方为每一个独立的流都设定一个最大可接收字节数。
- 发送方在某个特定的流上发送数据时,消耗的是该流的信用额度。
- 当接收方处理完某个流的数据后,会通过发送 MAX_STREAM_DATA帧,只为那个特定的流增加信用额度。
 
控制帧
QUIC 使用四种特定的控制帧来管理流量控制:
接收方 -> 发送方 (授予信用)
- **MAX_DATA帧 **- 作用: 增大连接层的流量控制窗口。
- 内容: 包含一个字段 Maximum Data,表示在该连接上允许发送的总字节数的绝对值上限。
 
- MAX_STREAM_DATA帧- 作用:增大指定流的流量控制窗口。
- 内容: 包含两个字段:Stream ID(指明要为哪个流更新窗口)和Maximum Stream Data(表示该流上允许发送的总字节数的绝对值上限)。
 
发送方 -> 接收方 (通知阻塞)
- DATA_BLOCKED帧- 作用: 当发送方想发送数据,但因为连接层的流量控制窗口耗尽而被阻塞时,它会发送此帧。
- 目的: 告知接收方:“我已经因为总信用额度不足而被阻塞了。” 这可以提示接收方尽快发送 MAX_DATA帧(如果它有能力处理更多数据),同时也有助于网络问题的诊断。
 
- STREAM_DATA_BLOCKED帧- 作用: 当发送方在某个特定流上想发送数据,但因为该流的流量控制窗口耗尽而被阻塞时,发送此帧。
- 目的: 告知接收方:“我在 Stream X 上因为该流的信用额度不足而被阻塞了。”
 
更新策略
- 
    握手时传输参数设置 QUIC在建联握手的过程中,双方会传递传输参数,传输参数中的以下几个值与流控有关: connection级别: - initial_max_data:这个connection的最大可接收字节数
 流级别: id name 含义 0x05 initia_max_stream_data_bidi_local本端建立双向流时,本端的最大接收窗口 0x06 initial_max_stream_data_bidi_remote对端建立双向流时,本端的最大接收窗口 0x07 initial_max_stream_data_uni对端建立单向流时,本端的最大接收窗口 
- 
    当接收方处理的数据量,使得剩余的窗口大小低于某个阈值时(小于总窗口大小的一半),它就会主动发送一个 MAX_DATA或MAX_STREAM_DATA帧。
- 
    新的窗口上限通常会被设置为当前已接收数据量的两倍。例如,如果初始窗口是 64 KB,当接收方处理了 32 KB 数据后,它会发送一个 MAX_DATA帧,将窗口上限更新到32KB + 64KB = 96KB或者直接是2 * 64KB = 128KB(但会受到其物理缓冲区大小的严格约束)。