SPECIFICATION
第 6 章 資料傳輸
6.1 雙向資料流
DTP 實作 必須 支援以下兩個獨立的資料流方向:
| 方向 | 名稱 | 傳送方 | 接收方 |
|---|---|---|---|
| Terminal → Fay | 資料歸集(Collection) | Slave | Master |
| Fay → Terminal | 資料注入(Injection) | Master | Slave |
兩個方向 必須 滿足:
- 使用相同的 LogicalFrame 格式與處理流程。
- 維護獨立的序列號空間。
- 維護獨立的續傳狀態(未確認 Fragment 快取、最高已確認序列號等)。
- 互不干擾:一個方向的狀態變化 不得 影響另一個方向。
6.2 資料歸集流程(Terminal → Fay)
資料歸集 必須 遵循以下流程:
6.2.1 傳送方(DTP_Slave)流程
- 接收終端應用提交的資料。
- 建構 Fragment:
a. 產生新的 Fragment_ID(UUID v4)。
b. 設定
agreementId為當前 active 約定的 Agreement_ID。 c. 設定originTimestamp為資料實際產生時刻的 UTC 毫秒時間戳記。不得 使用當前時刻。 d. 附加結構化的 ContextMetadata(參見第 4.11 節)。 e. 附加dagDependencies(可 為空陣列)。 - 驗證 DAG 依賴:
a. 呼叫 DAG Manager 驗證不會形成環路(參見第 6.7 節)。
b. 如偵測到環路,必須 拒絕該 Fragment 並返回
DAG_CYCLE_DETECTED錯誤(4001)。 - 建構 LogicalFrame:
a. 設定
frameType = "data"。 b. 套用 Agreement_ID 壓縮規則(參見第 4.5 節)。 c. 分配單調遞增的sequenceNumber(資料歸集方向)。 d. 設定加密中繼資料。 - 加密 Payload:使用 CAP 預協商的金鑰加密 Fragment 的
data欄位。 - 序列化 LogicalFrame。
- 呼叫 Transport_Adapter 傳送二進位資料。
- 將 Fragment 加入未確認快取(參見第 8.2 節)。
6.2.2 接收方(DTP_Master)流程
- 接收 Transport_Adapter 傳遞的二進位資料。
- 反序列化為 LogicalFrame。如失敗,必須 丟棄訊框並返回
FRAME_DESERIALIZATION_FAILED錯誤(1001)。 - 驗證協定版本(參見第 10 章)。
- 解析 Agreement_ID(套用第 4.5 節的壓縮規則)。如關聯到未知約定,必須 丟棄訊框並返回
AGREEMENT_NOT_FOUND錯誤(3001)。 - 解密 Payload。如失敗,必須 丟棄訊框並返回
DECRYPTION_FAILED錯誤(2001)。 - 反序列化為 Fragment。
- 驗證 DAG 依賴:
a. 如所有依賴目標 Fragment 已存在,必須 接受並標記為
accepted。 b. 如存在依賴目標 Fragment 尚未到達,必須 標記為pending並快取(參見第 6.7 節)。 c. 如偵測到環路,必須 拒絕並返回DAG_CYCLE_DETECTED錯誤(4001)。 - 更新接收狀態:將該 Fragment 的 sequenceNumber 設為該方向的最高已接收序列號(如果它遞增)。
- 傳送確認(參見第 8 章)。
- 將 Fragment 持久化到 Personal Data Heap。
6.3 資料注入流程(Fay → Terminal)
資料注入 必須 遵循以下流程:
6.3.1 傳送方(DTP_Master)流程
- 從 Personal Data Heap 查詢並按約定
dataRange過濾資料,產生最小化資料集。 - 建構 Fragment(同 6.2.1 的步驟 2-3)。
- 建構 LogicalFrame、加密 Payload、序列化、傳送(同 6.2.1 的步驟 4-8),但使用資料注入方向的序列號空間。
6.3.2 接收方(DTP_Slave)流程
- 反序列化、驗證版本、解析 Agreement_ID、解密、反序列化 Fragment、驗證 DAG(同 6.2.2 的步驟 1-7)。
- 更新接收狀態、傳送確認(同 6.2.2 的步驟 8-9)。
- 將 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 壓縮約束
實作 必須 滿足:
- 可 選擇不進行壓縮(即所有 Fragment 都攜帶完整 Agreement_ID)。
- 如選擇壓縮,必須 嚴格遵循 4.5 節規則。
- 接收方 必須 同時支援壓縮與非壓縮兩種模式。
- 上下文 Agreement_ID 在會話暫停恢復後 必須 重置為 null(即恢復後第一個 Fragment 必須 攜帶完整 ID)。
6.5 序列號管理
6.5.1 單調遞增
每個 Fragment 的 sequenceNumber 必須 滿足:
- 在單次會話內單調遞增。
- 應 嚴格連續(即每次遞增 1)。
- 不得 重複或回退。
6.5.2 雙向獨立
資料歸集方向與資料注入方向 必須 維護獨立的序列號空間:
資料歸集方向 (collection): seq 1, 2, 3, 4, 5, ...
資料注入方向 (injection): seq 1, 2, 3, 4, 5, ...
實作 不得 在兩個方向之間共用序列號空間。
6.5.3 重啟與會話
序列號 必須 滿足:
- 新會話開始時序列號 必須 重置(應 從 0 或 1 開始)。
- 會話從
Suspended恢復時,序列號 必須 保持暫停前的值,不得 重置。 - 如序列號接近實作定義的最大值,傳送方 必須 主動新建會話以避免溢位。
6.6 原始時間戳記保全
實作 必須 保證 Origin_Timestamp 在傳輸過程中不變:
- 傳送方 必須 記錄資料實際產生時刻為 Origin_Timestamp。
- 序列化、加密、傳輸、解密、反序列化 不得 修改 Origin_Timestamp。
- 接收方 不得 修改接收到的 Origin_Timestamp。
- 接收方持久化時 必須 保留 Origin_Timestamp。
實作 應 在訊框標頭之外維護一個獨立的「傳輸時間戳記」欄位(實作定義),但 不得 與 Origin_Timestamp 混淆。
6.7 DAG 依賴處理
6.7.1 加入 Fragment 到 DAG
接收方接收到 Fragment 時,必須 透過 DAG Manager 處理依賴:
- 提取 Fragment 的
dagDependencies。 - 對每條依賴: a. 檢查目標 Fragment_ID 是否已在 DAG 中。 b. 檢查加入該邊是否會形成環路。
- 根據檢查結果返回三種結果之一:
| 結果 | 含義 | 後續動作 |
|---|---|---|
accepted | 所有依賴已解析,無環路 | 必須 將 Fragment 加入 DAG |
pending | 部分依賴未解析(目標 Fragment 未到達),無環路 | 必須 快取該 Fragment,等待依賴解析 |
rejected | 偵測到環路 | 必須 拒絕該 Fragment 並返回 DAG_CYCLE_DETECTED 錯誤(4001) |
6.7.2 延遲解析
當 Fragment 處於 pending 狀態時:
- 實作 必須 在該 Fragment 快取中持續等待依賴。
- 當依賴目標 Fragment 到達時,必須 重新評估快取中所有相關
pendingFragment。 - 可 設定最大快取時間(實作定義)。逾時後 應 返回
DAG_DEPENDENCY_UNRESOLVED錯誤(4002)並丟棄。
6.7.3 環路偵測演算法
實作 必須 在加入新 Fragment 前偵測環路。偵測演算法 應 使用 DFS(深度優先搜尋)或 Tarjan 演算法。
具體演算法:從新 Fragment 的每個依賴目標出發,進行 DFS。如能從依賴目標到達新 Fragment 自身,則形成環路。
6.8 多約定交錯傳輸
當多個 active 約定共存時,傳送方 必須:
- 在每個 Fragment 的訊框標頭中透過 Agreement_ID 關聯到正確的約定。
- 在切換約定時(即下一個 Fragment 屬於不同約定),必須 在該 Fragment 中攜帶完整的 Agreement_ID(即不能使用 null 壓縮)。
- 應 按約定的
priority排程傳送順序。
