第 8 章 可靠性保障

8.1 可靠性模型

DTP 实现 必须 提供以下可靠性保障:

  1. 续传:在底层连接中断后恢复传输,不得 重传已成功接收的数据。
  2. 确认:接收方 必须 向发送方确认已成功接收的 Fragment。
  3. 重传:发送方 必须 在超时未收到确认时重传未确认 Fragment。
  4. 会话持久化:底层连接中断时 必须 持久化会话状态。

8.2 续传机制

8.2.1 续传协议

续传 必须 基于序列号实现,遵循以下流程:

发送方                              接收方
   |                                   |
   |-- Fragment (seq=1) -------------->|  ✓ 接收
   |-- Fragment (seq=2) -------------->|  ✓ 接收
   |-- Fragment (seq=3) -------------->|  ✓ 接收
   |-- Fragment (seq=4) ------- ✗ ----|  连接断开
   |                                   |
   |        [连接恢复]                  |
   |                                   |
   |<-- ResumeReport (highest=3) ------|
   |                                   |
   |-- Fragment (seq=4) -------------->|  从断点继续
   |-- Fragment (seq=5) -------------->|

8.2.2 ResumeReport

接收方在连接恢复后 必须 发送 ResumeReport,定义如下:

interface ResumeReport {
  collectionHighest: SequenceNumber;
  injectionHighest: SequenceNumber;
}
字段规范性要求
collectionHighest必须 为接收方在数据归集方向上已成功接收的最高序列号
injectionHighest必须 为接收方在数据注入方向上已成功接收的最高序列号

如某方向尚未接收任何 Fragment,对应字段 必须 为 -1 或实现定义的"未接收"标识值。

8.2.3 续传一致性

发送方收到 ResumeReport 后 必须 严格遵循:

  1. 从下一个序列号继续:从 highest + 1 开始重新发送。
  2. 不重复发送不得 重新发送序列号 <= highest 的 Fragment。
  3. 不跳过不得 跳过未确认的 Fragment(即未在缓存中确认接收的)。

8.3 确认机制

8.3.1 确认方式

实现 必须 提供 Fragment 接收确认。确认 通过以下方式之一实现:

  1. 显式 ACK 控制帧:接收方发送 ControlFrame,controlType = "ack",details 包含已确认的最高序列号。
  2. 累积 ACK:在每个数据帧的扩展字段中携带反向方向的最高已接收序列号(推荐)。
  3. 批量 ACK:每接收 N 个 Fragment 发送一次 ACK(实现定义 N,推荐 为 16)。

具体确认方式 由实现选择,但接收方与发送方 必须 就所选方式达成一致。

8.3.2 确认时序

接收方 必须 满足:

  1. 在 Fragment 通过解密、Agreement 验证、DAG 验证后才能确认。
  2. 不得 在 DAG 处于 pending 状态时确认(应在依赖解析后再确认)。
  3. ACK 中的最高序列号 必须 是已确认序列号集合的连续前缀的最大值。

8.4 重传机制

8.4.1 重传策略

发送方 必须 在以下条件下重传:

  1. 在协议配置的重传超时时间内未收到对应序列号的 ACK。
  2. 收到 ResumeReport 时,对未确认的 Fragment 重传。

8.4.2 重传配置

实现 提供以下可配置参数:

参数默认值(推荐说明
初始重传超时5 秒第一次重传等待时间
重传退避因子2每次重传后超时翻倍(指数退避)
最大重传次数5超过后通知上层失败
最大重传超时60 秒重传超时上限

实现 必须 实现指数退避算法。

8.4.3 重传失败处理

当重传次数超过上限时:

  1. 发送方 必须 通知上层应用 RETRANSMISSION_TIMEOUT 错误(6002)。
  2. 发送方 触发会话挂起或终止(实现定义)。
  3. 实现 不得 无限重传。

8.5 缓存管理

8.5.1 未确认 Fragment 缓存

发送方 必须 维护未确认 Fragment 缓存,满足:

  1. 每个已发送但未收到确认的 Fragment 必须 保留在缓存中。
  2. 收到确认后,必须 从缓存中移除已确认的 Fragment。
  3. 缓存 必须 有容量上限(实现定义,推荐 不少于 1024 个 Fragment 或 16 MB)。

8.5.2 缓存满处理

当缓存达到容量上限时,发送方 必须

  1. 暂停发送新 Fragment。
  2. 通过 BUFFER_FULL 错误(6001)通知上层应用。
  3. 在确认释放缓存空间后恢复发送。
  4. 不得 静默丢弃 Fragment。

8.6 会话管理

8.6.1 会话建立

CAP 完成身份验证与密钥交换后,DTP_Engine 必须 建立 DTP 会话:

  1. 必须 生成符合 RFC 4122 的唯一 Session_ID(UUID v4)。
  2. 必须 初始化 Session 数据结构(参见第 8.6.3 节)。
  3. 必须 将状态从 WaitingForCAP 转换到 SessionEstablished

8.6.2 会话状态维护

DTP_Engine 必须 在会话期间维护双向的传输状态:

interface DirectionalTransferState {
  currentSequenceNumber: SequenceNumber;
  highestAcknowledgedSequenceNumber: SequenceNumber;
  unacknowledgedFragmentCache: Map<SequenceNumber, Fragment>;
}

实现 必须 为每个传输方向维护独立的 DirectionalTransferState:

方向字段名
数据归集(Terminal → Fay)collectionState
数据注入(Fay → Terminal)injectionState

8.6.3 Session 数据结构

完整的 Session 结构 必须 包含:

interface Session {
  sessionId: SessionID;
  masterIdentity: string;
  slaveIdentity: string;
  state: SessionState;
  activeAgreements: Map<AgreementID, Agreement>;
  collectionState: DirectionalTransferState;
  injectionState: DirectionalTransferState;
  createdAt: number;
  lastActivityAt: number;
  timeoutThreshold: number;
}

8.6.4 会话持久化

当底层传输连接断开时,DTP_Engine 必须

  1. 立即将 SessionState 从 Transmitting 转换到 Suspended
  2. 持久化以下内容到非易失性存储:
    • 完整的 Session 对象(包括所有 active 约定)
    • 双向 DirectionalTransferState
    • 未确认 Fragment 缓存
  3. 持久化 必须 是原子的(要么全部成功,要么全部失败)。

8.6.5 会话恢复

底层连接重新建立后,DTP_Engine 必须 遵循以下恢复流程:

  1. 等待 CAP 重新验证。
  2. CAP 验证成功后,进入 Resuming 状态。
  3. 从持久化存储中恢复 Session 状态。
  4. 接收方发送 ResumeReport(参见第 8.2.2 节)。
  5. 发送方根据 ResumeReport 从断点继续传输。
  6. 续传握手完成后转换到 Transmitting

如恢复失败,DTP_Engine 必须

  1. 转换到 Idle 状态。
  2. 释放所有相关资源。
  3. 通过 SESSION_RESTORE_FAILED 错误(5003)通知上层。

8.6.6 会话超时

实现 必须 实现会话超时机制:

  1. 必须 维护 lastActivityAt 字段,记录最后一次活动时间。
  2. now - lastActivityAt > timeoutThreshold 时,必须 将会话设为超时。
  3. 超时的会话 必须 被关闭,相关资源 必须 被释放。
  4. 默认 timeoutThreshold 推荐 为 30 分钟(1,800,000 ms)。

8.7 双向独立性

实现 必须 严格保证两个传输方向的独立性:

  1. 数据归集方向的状态变化 不得 影响数据注入方向。
  2. 一个方向的连接异常 不得 自动触发另一个方向的状态变更(除非底层连接整体断开)。
  3. 一个方向的序列号 不得 进入另一个方向的序列号空间。
  4. 一个方向的缓存 不得 占用另一个方向的缓存配额。