第 6 章 資料傳輸

6.1 雙向資料流

DTP 實作 必須 支援以下兩個獨立的資料流方向:

方向名稱傳送方接收方
Terminal → Fay資料歸集(Collection)SlaveMaster
Fay → Terminal資料注入(Injection)MasterSlave

兩個方向 必須 滿足:

  1. 使用相同的 LogicalFrame 格式與處理流程。
  2. 維護獨立的序列號空間。
  3. 維護獨立的續傳狀態(未確認 Fragment 快取、最高已確認序列號等)。
  4. 互不干擾:一個方向的狀態變化 不得 影響另一個方向。

6.2 資料歸集流程(Terminal → Fay)

資料歸集 必須 遵循以下流程:

6.2.1 傳送方(DTP_Slave)流程

  1. 接收終端應用提交的資料。
  2. 建構 Fragment: a. 產生新的 Fragment_ID(UUID v4)。 b. 設定 agreementId 為當前 active 約定的 Agreement_ID。 c. 設定 originTimestamp 為資料實際產生時刻的 UTC 毫秒時間戳記。不得 使用當前時刻。 d. 附加結構化的 ContextMetadata(參見第 4.11 節)。 e. 附加 dagDependencies 為空陣列)。
  3. 驗證 DAG 依賴: a. 呼叫 DAG Manager 驗證不會形成環路(參見第 6.7 節)。 b. 如偵測到環路,必須 拒絕該 Fragment 並返回 DAG_CYCLE_DETECTED 錯誤(4001)。
  4. 建構 LogicalFrame: a. 設定 frameType = "data"。 b. 套用 Agreement_ID 壓縮規則(參見第 4.5 節)。 c. 分配單調遞增的 sequenceNumber(資料歸集方向)。 d. 設定加密中繼資料。
  5. 加密 Payload:使用 CAP 預協商的金鑰加密 Fragment 的 data 欄位。
  6. 序列化 LogicalFrame。
  7. 呼叫 Transport_Adapter 傳送二進位資料。
  8. 將 Fragment 加入未確認快取(參見第 8.2 節)。

6.2.2 接收方(DTP_Master)流程

  1. 接收 Transport_Adapter 傳遞的二進位資料。
  2. 反序列化為 LogicalFrame。如失敗,必須 丟棄訊框並返回 FRAME_DESERIALIZATION_FAILED 錯誤(1001)。
  3. 驗證協定版本(參見第 10 章)。
  4. 解析 Agreement_ID(套用第 4.5 節的壓縮規則)。如關聯到未知約定,必須 丟棄訊框並返回 AGREEMENT_NOT_FOUND 錯誤(3001)。
  5. 解密 Payload。如失敗,必須 丟棄訊框並返回 DECRYPTION_FAILED 錯誤(2001)。
  6. 反序列化為 Fragment。
  7. 驗證 DAG 依賴: a. 如所有依賴目標 Fragment 已存在,必須 接受並標記為 accepted。 b. 如存在依賴目標 Fragment 尚未到達,必須 標記為 pending 並快取(參見第 6.7 節)。 c. 如偵測到環路,必須 拒絕並返回 DAG_CYCLE_DETECTED 錯誤(4001)。
  8. 更新接收狀態:將該 Fragment 的 sequenceNumber 設為該方向的最高已接收序列號(如果它遞增)。
  9. 傳送確認(參見第 8 章)。
  10. 將 Fragment 持久化到 Personal Data Heap。

6.3 資料注入流程(Fay → Terminal)

資料注入 必須 遵循以下流程:

6.3.1 傳送方(DTP_Master)流程

  1. 從 Personal Data Heap 查詢並按約定 dataRange 過濾資料,產生最小化資料集。
  2. 建構 Fragment(同 6.2.1 的步驟 2-3)。
  3. 建構 LogicalFrame、加密 Payload、序列化、傳送(同 6.2.1 的步驟 4-8),但使用資料注入方向的序列號空間。

6.3.2 接收方(DTP_Slave)流程

  1. 反序列化、驗證版本、解析 Agreement_ID、解密、反序列化 Fragment、驗證 DAG(同 6.2.2 的步驟 1-7)。
  2. 更新接收狀態、傳送確認(同 6.2.2 的步驟 8-9)。
  3. 將 Fragment 交付給終端應用。

6.4 Agreement_ID 壓縮傳輸

實作 必須 嚴格遵循第 4.5 節定義的 Agreement_ID 壓縮規則。

6.4.1 壓縮範例

以下是符合規範的傳輸序列:

Fragment 1: agreementId = "abc-123"   (新約定,完整 ID)
Fragment 2: agreementId = null        (沿用 "abc-123")
Fragment 3: agreementId = null        (沿用 "abc-123")
Fragment 4: agreementId = "def-456"   (切換到新約定,完整 ID)
Fragment 5: agreementId = null        (沿用 "def-456")

6.4.2 壓縮約束

實作 必須 滿足:

  1. 選擇不進行壓縮(即所有 Fragment 都攜帶完整 Agreement_ID)。
  2. 如選擇壓縮,必須 嚴格遵循 4.5 節規則。
  3. 接收方 必須 同時支援壓縮與非壓縮兩種模式。
  4. 上下文 Agreement_ID 在會話暫停恢復後 必須 重置為 null(即恢復後第一個 Fragment 必須 攜帶完整 ID)。

6.5 序列號管理

6.5.1 單調遞增

每個 Fragment 的 sequenceNumber 必須 滿足:

  1. 在單次會話內單調遞增。
  2. 嚴格連續(即每次遞增 1)。
  3. 不得 重複或回退。

6.5.2 雙向獨立

資料歸集方向與資料注入方向 必須 維護獨立的序列號空間:

資料歸集方向 (collection):  seq 1, 2, 3, 4, 5, ...
資料注入方向 (injection):   seq 1, 2, 3, 4, 5, ...

實作 不得 在兩個方向之間共用序列號空間。

6.5.3 重啟與會話

序列號 必須 滿足:

  1. 新會話開始時序列號 必須 重置( 從 0 或 1 開始)。
  2. 會話從 Suspended 恢復時,序列號 必須 保持暫停前的值,不得 重置。
  3. 如序列號接近實作定義的最大值,傳送方 必須 主動新建會話以避免溢位。

6.6 原始時間戳記保全

實作 必須 保證 Origin_Timestamp 在傳輸過程中不變:

  1. 傳送方 必須 記錄資料實際產生時刻為 Origin_Timestamp。
  2. 序列化、加密、傳輸、解密、反序列化 不得 修改 Origin_Timestamp。
  3. 接收方 不得 修改接收到的 Origin_Timestamp。
  4. 接收方持久化時 必須 保留 Origin_Timestamp。

實作 在訊框標頭之外維護一個獨立的「傳輸時間戳記」欄位(實作定義),但 不得 與 Origin_Timestamp 混淆。

6.7 DAG 依賴處理

6.7.1 加入 Fragment 到 DAG

接收方接收到 Fragment 時,必須 透過 DAG Manager 處理依賴:

  1. 提取 Fragment 的 dagDependencies
  2. 對每條依賴: a. 檢查目標 Fragment_ID 是否已在 DAG 中。 b. 檢查加入該邊是否會形成環路。
  3. 根據檢查結果返回三種結果之一:
結果含義後續動作
accepted所有依賴已解析,無環路必須 將 Fragment 加入 DAG
pending部分依賴未解析(目標 Fragment 未到達),無環路必須 快取該 Fragment,等待依賴解析
rejected偵測到環路必須 拒絕該 Fragment 並返回 DAG_CYCLE_DETECTED 錯誤(4001)

6.7.2 延遲解析

當 Fragment 處於 pending 狀態時:

  1. 實作 必須 在該 Fragment 快取中持續等待依賴。
  2. 當依賴目標 Fragment 到達時,必須 重新評估快取中所有相關 pending Fragment。
  3. 設定最大快取時間(實作定義)。逾時後 返回 DAG_DEPENDENCY_UNRESOLVED 錯誤(4002)並丟棄。

6.7.3 環路偵測演算法

實作 必須 在加入新 Fragment 前偵測環路。偵測演算法 使用 DFS(深度優先搜尋)或 Tarjan 演算法。

具體演算法:從新 Fragment 的每個依賴目標出發,進行 DFS。如能從依賴目標到達新 Fragment 自身,則形成環路。

6.8 多約定交錯傳輸

當多個 active 約定共存時,傳送方 必須

  1. 在每個 Fragment 的訊框標頭中透過 Agreement_ID 關聯到正確的約定。
  2. 在切換約定時(即下一個 Fragment 屬於不同約定),必須 在該 Fragment 中攜帶完整的 Agreement_ID(即不能使用 null 壓縮)。
  3. 按約定的 priority 排程傳送順序。