Zenoh 封包格式
讓我們實際看看 Zenoh 實際的封包格式,這邊一樣要分成 Scouting 和 Transport 的部份
Scout & Hello 封包
Scouting 的部份有 Scout 和 Hello 兩種封包
SCOUT 封包
- 第一個 byte 有 ID 和 flag,ID 固定是 0x01 代表 Scout message,flag 則是用來表示是否有 extension
- 第二個 byte 是版本,目前為 0x09
- 第三個 byte 有兩個功能,如果 I=1 代表有 ZenohID,WHAT 則是用來表示自身角色 (Router, Peer, Client)
- 第四個 byte 之後就是看前面的 flag 有沒有要加上 ZenohID 和 extension
Flags:
Z If Z==1, extension chain follows.
7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+
|Z|X|X| SCOUT | ID = 0x01
+-+-+-+---------+
| version | Protocol version (u8); current value: 0x09
+---------------+
|zid_len|I|WHAT | Packed byte (see below)
+-+-+-+-+-+-+-+-+
~ ZID ~ if I==1: ZenohID (1 + zid_len bytes)
+---------------+
~ [ScoutExts] ~ if Z==1: extension chain
+---------------+
HELLO 封包
- 第一個 byte 一樣是 ID 和 flag,ID 固定是 0x02,flag 則是表達有沒有可連接清單和 extension
- 第二個 byte 一樣是版本,0x09
- 第三個 byte 則是 ZenohID 長度和當前的角色 (Router, Peer, Client)
- 後面的部份就是實際的 ZenohID、連接清單、extension
Flags:
L If L==1, explicit locator list is present.
If L==0, the source address of the packet is the locator.
Z If Z==1, extension chain follows.
7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+
|Z|X|L| HELLO | ID = 0x02
+-+-+-+---------+
| version | Protocol version (u8); current value: 0x09
+---------------+
|zid_len|X|X|WAI| Packed byte (see below)
+-+-+-+-+-+-+-+-+
~ ZID ~ ZenohID (1 + zid_len bytes)
+---------------+
% loc_count: z8 % if L==1: number of locator strings
~ locators ~ if L==1: loc_count × <utf8;z8> locator strings
+---------------+
~ [HelloExts] ~ if Z==1: extension chain
+---------------+
Transport 封包
Transport Messages 有多個,例如 SYN、OPEN、FRAME、KEEPALIVE、CLOSE 等等,其中最重要的 FRAME。
FRAME 底下還可以切分成更細的 Network Messages,例如 OAM、RESPONSE、REQUEST、DECLARE、PUSH、INTEREST 等等。
而最常用的 Network Messages,PUSH、REQUEST、RESPONSE 底下還可以有 Data Sub-Messages,如 PUT、DEL、QUERY、REPLY、ERR 等等。
Transport Messages
INIT 封包
7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+
|Z|S|A| INIT | ID = 0x01
+-+-+-+---------+
| version | Protocol version (u8); current = 0x09
+---------------+
|zid_len|X|X|WAI| Packed byte: bits 7:4 = zid_len, bits 1:0 = WhatAmI
+-+-+-+-+-+-+-+-+
~ ZID ~ (1 + zid_len) bytes
+---------------+
|X|X|X|X|RID|FSN| if S==1: Resolution byte
+---------------+
| batch_lo | if S==1: Batch size (u16 LE)
+---------------+
| batch_hi |
+---------------+
~ <u8;z16> ~ Cookie — InitAck (A==1) ONLY
+---------------+
~ [InitExts] ~ if Z==1
+---------------+
OPEN 封包
7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+
|Z|T|A| OPEN | ID = 0x02
+-+-+-+---------+
% lease % VLE-encoded lease duration (unit per T flag)
+---------------+
% initial_sn % VLE-encoded initial sequence number
+---------------+
~ <u8;z16> ~ Cookie (OpenSyn, A==0 only) — MUST match cookie from InitAck
+---------------+
~ [OpenExts] ~ if Z==1
+---------------+
CLOSE 封包
KEEPALIVE 封包
FRAME 封包
- 第一個 byte 是 id (0x05 代表 frame) 和 flag
- r:是否 reliable
- Z:是否用到 extensions
- 第二個 byte 是 squence number
- 使用 Variable-length integer(VarInt),長度會隨著 seq 增長而增加,如果 0-127 只需要一個 byte,更大的話用到 2-4 bytes 都可以
- 第三個部份則是可擴充的欄位,是 TLV 格式
- 如果 Header 有設定 flag 這邊才會出現,例如 QoS、timestamp 都是在這邊
- 最後的部份則是會放入多個 Network Messages
FRAGMENT 封包
Network Messages
OAM 封包
Orchestration, Administration, Maintainence (OAM) 相關的封包PUSH 封包
用在 Publish 資料上面 7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+
|Z|M|N| PUSH | ID = 0x1D
+-+-+-+---------+
% key_scope:z16 % VLE ExprId (0 = global scope)
+---------------+
~ key_suffix ~ if N==1: <u8;z16> suffix string
+---------------+
~ [push_exts] ~ if Z==1 (QoS, Timestamp, NodeId)
+---------------+
~ PushBody ~ PUT (0x01) or DEL (0x02) sub-message
+---------------+
REQUEST 封包
用在發送 Query 資料上 7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+
|Z|M|N| Request | ID = 0x1C
+-+-+-+---------+
% request_id:z32% VLE request identifier
+---------------+
% key_scope:z16 % VLE ExprId
+---------------+
~ key_suffix ~ if N==1: <u8;z16>
+---------------+
~ [req_exts] ~ if Z==1 (QoS, Timestamp, NodeId, QueryTarget, Budget, Timeout)
+---------------+
~ RequestBody ~ QUERY (0x03) sub-message
+---------------+
RESPONSE 封包
用在回覆 Query 資訊上面 7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+
|Z|M|N| Response| ID = 0x1B
+-+-+-+---------+
% request_id:z32% VLE — correlates to the originating REQUEST
+---------------+
% key_scope:z16 %
+---------------+
~ key_suffix ~ if N==1: <u8;z16>
+---------------+
~ [reply_exts] ~ if Z==1 (QoS, Timestamp, ResponderId)
+---------------+
~ ResponseBody ~ REPLY (0x04) or ERR (0x05) sub-message
+---------------+
RESPONSE_FINAL 封包
DECLARE 封包
DECLARE 有幾個用途,把 key expression 對應到 ID 減少傳輸量、宣告 subscriber、queryables、liveliness token。 值得注意的是 publish 或 query 並不需要 declare,這可以減少協定的複雜度。 7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+
|Z|X|I| DECLARE | ID = 0x1E
+-+-+-+---------+
%interest_id:z32% if I==1: VLE interest ID this declare responds to
+---------------+
~ [decl_exts] ~ if Z==1 (QoS, Timestamp, NodeId)
+---------------+
~ declaration ~ DeclareBody sub-message (see xref:session:declarations.adoc[])
+---------------+
INTEREST 封包
比較晚加入的節點如果要得知當前網路上的狀態,就必須送出 INTEREST 來得到之前已經送出過的 DECLARE。 7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+
|Z|Mod| INTEREST| ID = 0x19; Mod in bits 6:5
+-+-+-+---------+
% id : z32 % VLE — Interest identifier
+---------------+
|A|M|N|R|T|Q|S|K| Options byte (present only when Mod != Final=0b00)
+---------------+
% key_scope:z16 % if Mod!=Final && R==1: VLE ExprId
+---------------+
~ key_suffix ~ if Mod!=Final && R==1 && N==1: <u8;z16>
+---------------+
~ [int_exts] ~ if Z==1 (QoS, Timestamp, NodeId)
+---------------+
Data Sub-Messages
以下是 Network Messages 會對應到的 Data Sub-Messages
- PUSH: PUSH, DEL
- REQUEST: QUERY
- RESPONSE: REPLY, ERR
PUT 封包
7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+
|Z|E|T| PUT | ID = 0x01
+-+-+-+---------+
~ ts: <u8;z16> ~ if T==1: Timestamp
+---------------+
~ encoding ~ if E==1: Encoding field (z32 with S-bit + optional schema)
+---------------+
~ [put_exts] ~ if Z==1 (SourceInfo, Shm, Attachment)
+---------------+
~ pl: <u8;z32> ~ Payload bytes
+---------------+