QUIC 包管理

ReceivedPacketTracker 类详细分析

核心功能概述

ReceivedPacketTracker 是 QUIC 协议实现中的一个关键组件,负责跟踪接收到的数据包 并管理 ACK 生成和丢包检测所需的状态信息。该类位于 AgentLinkCoro/src/base/net/quic/quic_packet_sorter.cc 文件中。

设计思路

1. 区间合并优化

该类通过组合使用 ReceivedPacketHistory 类来高效管理已接收数据包的历史记录。核心设计思想是使用区间合并算法而非简单的位图或哈希表,这样可以:

  • 节省内存:特别是在高丢包率情况下
  • 高效追踪:快速确定数据包是否已接收、是否丢失
  • 优化 ACK 帧大小:使用合并的区间构造紧凑的 ACK 帧

2. ACK 策略设计

实现了复杂的 ACK 触发和延迟策略,包括:

  • 数据包计数触发:累积一定数量的需确认数据包后触发 ACK
  • 延迟 ACK 机制:设置 ACK 延迟定时器,平衡延迟和带宽
  • 即时 ACK 场景:对首次数据包、重传数据包和丢包检测场景立即发送 ACK

3. 丢包检测机制

通过观察数据包接收模式来检测潜在的丢包:

  • 当接收到的数据包编号跳跃过大时(最高 ACK 范围起始值大于上一次最大确认值+1),且该数据包是单数据包时,认为存在丢包

核心组件关系

scss 复制代码
ReceivedPacketTracker
       |
       └──► ReceivedPacketHistory (管理接收历史记录)
       |             |
       |             └──► PacketInterval (连续的数据包编号区间)
       |
       └──► 与 SentPacketHandler 交互 (发送 ACK 和接收丢包通知)

主要方法分析

1. 构造函数

cpp 复制代码
ReceivedPacketTracker::ReceivedPacketTracker()
{
    m_packet_history = std::make_shared<ReceivedPacketHistory>();
}

初始化数据包历史记录器。

2. 数据包处理

cpp 复制代码
void ReceivedPacketTracker::receivedPacket(QuicPacketNumber pn, uint64_t recv_time, bool should_instigate_ack)

处理接收到的数据包,主要步骤:

  • 加锁保护并发访问
  • 忽略编号过小的数据包
  • 检查数据包是否为之前标记为丢失的
  • 更新最大观察到的数据包编号和时间
  • 将数据包添加到历史记录中
  • 根据需要触发 ACK 处理

3. ACK 决策

cpp 复制代码
void ReceivedPacketTracker::maybeQueueAck(QuicPacketNumber pn, uint64_t recv_time, bool was_missing)

决定是否应该将 ACK 帧加入发送队列,触发条件包括:

  • 首次接收到数据包
  • 接收到之前丢失的数据包
  • 累积了足够数量(>= PacketsBeforeAck)的需确认数据包
  • 检测到新的丢失数据包

4. ACK 帧生成

cpp 复制代码
QuicAckFrame::ptr ReceivedPacketTracker::getAckFrame(bool only_if_queued)

同一个数据包的ACK可能会被发送多次,直到该范围被忽略。 生成 ACK 帧并重置相关状态:

  • 收集当前所有 ACK 范围
  • 计算 ACK 延迟(从接收数据包到发送 ACK 的时间)
  • 重置 ACK 相关计数器和标志

5. 丢包检测

cpp 复制代码
bool ReceivedPacketTracker::hasNewMissingPackets()

检查是否有新的丢失数据包需要报告,判断条件:

  • 最高 ACK 范围的起始值大于上一次最大确认值+1
  • 该范围长度为1(单数据包)

这表明收到了一个跳过中间编号的数据包,暗示中间数据包可能丢失。

6. 数据包状态检查

cpp 复制代码
bool ReceivedPacketTracker::isMissing(QuicPacketNumber pn)

检查数据包是否应该被视为丢失,判断条件:

  • 数据包编号小于最大已确认编号
  • 数据包未在 ACK 范围中

7. 历史记录优化

cpp 复制代码
void ReceivedPacketTracker::ignoreBelow(QuicPacketNumber pn)

忽略所有编号小于指定值的数据包,用于优化内存使用。

SentPacketHandler 类详细分析

核心功能概述

SentPacketHandler 是 QUIC 协议实现中的另一个核心组件,负责管理已发送数据包的状态、处理收到的 ACK 帧、执行丢包检测和实现拥塞控制 。该类位于 AgentLinkCoro/src/base/net/quic/quic_packet_sorter.cc 文件中,与 ReceivedPacketTracker 协同工作,共同保证 QUIC 协议的数据传输可靠性。

设计思路

1. 数据包生命周期管理

通过 SentPacketHistory 类实现数据包的完整生命周期管理:

  • 发送跟踪:记录所有已发送数据包的信息
  • 状态维护:维护数据包的各种状态(已发送、已确认、已丢失、已跳过)
  • 内存优化:定期清理已确认或已丢失的旧数据包,避免内存泄漏
  • 高效查找:提供快速查找和迭代已发送数据包的能力

2. 丢包检测与重传机制

实现了多层次的丢包检测策略:

  • 基于时间的丢包检测:当数据包超过特定时间阈值未收到 ACK 时视为丢失
  • 基于编号的丢包检测:当后续数据包已确认而中间数据包未确认时视为丢失
  • PTO(超时探测)机制:当定时器超时时发送探测包确认连接状态
  • 自适应超时策略:基于 RTT 统计信息动态调整超时时间

3. ACK 处理与 RTT 估计

精心设计的 ACK 处理流程:

  • ACK 有效性验证:防止接收错误的 ACK 信息
  • RTT 精确计算:基于 ACK 延迟和数据包发送/接收时间计算真实 RTT
  • ACK 帧处理:解析 ACK 帧,找出所有已确认的数据包
  • 帧级确认通知:通知每个帧它们已被确认,触发相应的回调处理

4. 拥塞控制集成

与 CUBIC 拥塞控制算法紧密集成:

  • 飞行字节数管理:精确跟踪网络中未确认的字节数
  • 拥塞事件响应:在检测到丢包时通知拥塞控制器
  • 发送决策:根据拥塞状态确定数据包的发送模式
  • 探测包策略:在网络状态不确定时发送探测包

核心组件关系

scss 复制代码
SentPacketHandler
       |
       ├──► SentPacketHistory (管理发送历史记录)
       |
       ├──► RTTStats (RTT统计信息管理)
       |
       ├──► CubicSender (拥塞控制算法实现)
       |
       └──► 与 ReceivedPacketTracker 交互 (接收 ACK 和发送丢包通知)

主要方法分析

1. 构造函数

cpp 复制代码
SentPacketHandler::SentPacketHandler(const RTTStats::ptr &rtt) : m_rtt_stats(rtt)
{
    // 初始化数据包历史记录
    m_data_packets.m_history = std::make_shared<SentPacketHistory>(m_rtt_stats);
    // 初始化CUBIC拥塞控制算法
    m_congestion = std::make_shared<CubicSender>(GetCurrentUS(), rtt, true, 1452);
}

初始化关键组件:数据包历史记录器和拥塞控制器。

2. 数据包发送处理

cpp 复制代码
void SentPacketHandler::sentPacket(QuicPacket::ptr packet, uint64_t now)

处理数据包发送,主要步骤:

  • 更新发送字节数统计
  • 调用内部实现处理数据包
  • 设置数据包时间戳
  • 将数据包记录到历史中
  • 如果是需要 ACK 的数据包,设置丢包检测定时器

3. ACK 帧处理

cpp 复制代码
bool SentPacketHandler::receivedAck(QuicAckFrame::ptr frame, uint64_t recv_time)

处理收到的 ACK 帧,核心流程:

  • 验证 ACK 的有效性(不能确认未发送的数据包)
  • 更新最大确认的数据包编号
  • 检测并移除已确认的数据包
  • 更新 RTT 统计信息
  • 重置 PTO 计数和探测包数量
  • 清理旧的数据包历史
  • 重新设置丢包检测定时器

4. 已确认数据包检测

cpp 复制代码
std::vector<QuicPacket::ptr> SentPacketHandler::detectAndRemoveAckedPackets(QuicAckFrame::ptr frame)

检测并处理已确认的数据包:

  • 遍历 ACK 帧中的所有 ACK 范围
  • 找出所有被确认的数据包
  • 通知每个帧它们已被确认
  • 从历史记录中移除已确认的数据包
  • 返回已确认的数据包列表

5. 丢包检测

cpp 复制代码
bool SentPacketHandler::detectLostPackets(uint64_t now)

基于时间阈值检测丢失的数据包:

  • 计算丢包检测延迟时间(基于 RTT 的倍数)
  • 遍历所有数据包,检查是否超过丢包时间阈值
  • 对于被检测为丢失的数据包,从飞行字节数中移除
  • 通知拥塞控制器发生丢包
  • 更新统计信息

6. 丢包检测超时处理

cpp 复制代码
bool SentPacketHandler::onLossDetectionTimeout()

处理丢包检测定时器超时事件:

  • 执行丢包检测或准备发送探测包
  • 增加 PTO 计数,调整下次超时时间
  • 设置需要发送的探测包数量
  • 重新设置丢包检测定时器

7. 探测包排队

cpp 复制代码
bool SentPacketHandler::queueProbePacket()

准备探测包进行重传:

  • 获取第一个未完成的数据包
  • 标记为丢失并排队其帧进行重传
  • 更新字节流控信息

8. 发送模式决策

cpp 复制代码
PacketSendMode SentPacketHandler::sendMode()

确定当前的数据包发送模式:

  • 检查跟踪的数据包数量,防止内存溢出
  • 根据需要发送的探测包数量决定优先发送内容
  • 根据拥塞控制状态决定是否允许发送新数据
  • 返回适当的发送模式(无发送、只发送ACK、发送探测数据、允许任何发送)

9. 飞行字节数管理

cpp 复制代码
void SentPacketHandler::removeFromBytesInflight(QuicPacket::ptr packet)

从飞行中的字节数统计中移除数据包:

  • 当数据包被确认或丢失时调用
  • 更新飞行中的字节数(拥塞控制的关键参数)
  • 进行防御性检查,避免统计错误
相关推荐
刘孬孬沉迷学习9 小时前
5G网络gNB与核心网(5GC)连接架构及传输协议
网络·网络协议·tcp/ip·5g·架构·udp·信息与通信
lang2015092810 小时前
WebSocket子协议STOMP
网络·websocket·网络协议
饺子大魔王的男人10 小时前
3秒传输GB级文件:FastSend让P2P共享告别云存储依赖
网络·网络协议·p2p
tonydf11 小时前
如何正确统计网络用户数量?别再被连接数和独立IP骗了!
后端·网络协议
我叫汪枫13 小时前
《HTTP 实战:常用调试工具与抓包技巧》
网络·网络协议·http
googleccsdn14 小时前
ENSP Pro Lab笔记:配置STP/RSTP/MSTP(4)
网络·笔记·网络协议
奋斗的蛋黄14 小时前
TCP 和 UDP 的核心区别:从原理到场景的全面解析
网络协议·tcp/ip·udp
普普通通的南瓜16 小时前
金融交易防护:国密 SSL 证书在网银与移动支付中的核心作用
网络·网络协议·安全·arcgis·gitlab·ssl·源代码管理