1 管理收到的数据包包号
每收到一个数据包,在数据包中的所有帧都处理结束之后,调用ngx_quic_ack_pacjet
函数,将当前包的包号放入ACK Range中。
注意
- 有些数据包不需要确认,但是仍然需要将数据包包号放入ACK Range中进行管理
- 有些数据包需要确认,但是不会马上确认,而且延迟确认
要点
假设当前数据包包号空间中已经收到的数据包包号为N
- 当前收到的数据包是连接上的第一个数据包,即包号为0,不会发送ACK帧
- 当前收到的数据包包号是N,即重复包,不会发送ACK帧
- 当前收到的数据包包号是N+1,即连续的数据包,不会发送ACK帧
- 当前收到的数据包包号>(N+1),不会发送ACK帧,放入ACK Range缓冲区中,如果数据包需要确认,设置数据包待确认标记
- 收到的数据包将两个连续的数据包连了起来,不会发送ACK帧
- 收到的数据包和其中一个连续的数据包连了起来,不会发送ACK帧
- 收到的数据包导致一个间隙变成两个间隙,不会发送ACK帧,放入ACK Range缓冲区中,如果数据包需要确认,设置数据包待确认标记
- 在当前的数据包放入ACK Range缓冲区时,如果缓冲区已满,判断之前的数据包是否需要确认,如果需要确认,发送ACK帧
- 收到的数据包很旧在保存的ACK Ranges中找不到位置(ACK Ranges最多保存10个),如果该数据包需要确认,则发送ACK帧(不会包含当前帧的确认信息,而是将之前收到的包的地图发送给对端)
综上:
- 收到重复的数据包不会发送ACK帧
- 收到将已有的ACK Range的数量变少或不变的数据包,即收到和已有的包序列连续的数据包不会发送ACK帧
- 收到将已有的ACK Range的数量变多的数据包,在放入ACK Range缓冲区之前,如果ACK Range已满,而且ACK Range中有数据包需要确认,则发送ACK帧;反之ACK Range不满,或ACK Range中没有数据包需要确认,不会发送ACK帧
- 收到很旧的、已经无迹可寻的数据包发送ACK帧
综上:
ngx_quic_ack_packet
函数的功能不是发送ACK帧,而是将当前包的信息放入ACK Range中。
2 确认收到的数据包
在调用函数 ngx_quic_output
发送数据包时,在每个数据包号空间中调用 ngx_quic_generate_ack
函数,在该函数中判断是否需要发送ACK帧。
2.1 发送ACK帧的条件
以下三个条件同时满足,则等待一段时间再发送ACK帧,否则在当前数据包中发送ACK帧。
- 没有要发送的帧
- ctx->send_ack小于2
- 和上一次发送ACK帧的时间间隔小于传输层参数max_ack_delay的取值
2.2 ctx->send_ack取值变化
ngx_quic_ack_packet
中设置 ctx->send_ack
的取值,如下:
- 收到需要确认的数据包,
ctx->send_ack++
- 收到乱序的数据包,
ctx->send_ack = 2
ngx_quic_generate_ack
中,在将ACK帧放入发送队列之后,设置 ctx->send_ack = 0