iT邦幫忙

2025 iThome 鐵人賽

DAY 6
0
Rust

30 天玩轉 Zenoh:Rust 助力物聯網、機器人與自駕的高速通訊系列 第 6

Day 06: Zenoh 內部結構:理解 Link Layer(連結層)

  • 分享至 

  • xImage
  •  

Zenoh 內部結構:理解 Link Layer(連結層)

在上一篇文章中,我們簡單探討了 Zenoh 的網路層
今天,我們將更往下一層,深入了解 連結層(Link Layer) —— 為 Zenoh 節點之間提供連線的基礎。

Zenoh 橫跨自 資料連結層 一直到 應用層,提供一個統一的通訊框架。在這個系統的核心就是 連結層,它將各種不同的傳輸協定細節抽象化,並為更高層元件提供一致的介面。

連結層展現了 Zenoh 傳輸協定無關(transport-agnostic) 的設計理念 —— 無論你是透過 TCP、QUIC、UDP multicast、WebSocket,甚至是 Unix 管道來通訊,高階 API 都是一致的。

┌─────────────────────────────────────────────────────────────┐
│                  Application Layer                          │
│  (Publishers, Subscribers, Querables, Query Clients)        │
├─────────────────────────────────────────────────────────────┤
│                  Session Layer                              │
│        (Session management, Key Expressions)                │
├─────────────────────────────────────────────────────────────┤
│                 Network Layer                               │
│     (HAT - Hierarchical Abstraction for Transport)          │
├─────────────────────────────────────────────────────────────┤
│                Transport Layer                              │
│  (Protocol semantics, Batching, Fragmentation)              │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────┐ │
│ │                Link Layer           <---- 我們正在這裡  │ │
│ │  (Transport abstraction, link management)               │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │  TCP   │  TLS   │  UDP   │  QUIC  │  WS   │  Serial  │  │ │
│ │  Unix  │  Pipe  │  Vsock │  ...   │       │          │  │ │
│ └─────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│              Physical Network Layer                         │
│        (Ethernet, WiFi, Bluetooth, etc.)                    │
└─────────────────────────────────────────────────────────────┘

什麼是 Zenoh 的 Link Layer?

Zenoh 的連結層由一系列 內部 crate 與抽象層(位於 io/zenoh-link*)所組成,用於支援多種傳輸協定,例如 TCP、UDP、QUIC、TLS、WebSocket、Serial、Unix socket、vsock 等。

這種抽象設計讓 Zenoh 與傳輸無關:上層並不需要知道資料是透過 TCP 還是 QUIC 流動,開發者也可以輕鬆新增新的Link類型。每種傳輸都實作在獨立的 crate 內(如 zenoh-link-tcp, zenoh-link-quic),但都遵循 zenoh-link-commons 所定義的共通 trait。

👉 若要了解建立在 Link Layer 之上的路由抽象,請參考 HAT (Hierarchical Abstraction for Transport)


架構與組件

連結層採取 模組化設計,將 Link實作Link管理 分離。

  • Link Trait 階層
    定義於 zenoh-link-commons,這些 traits(如 LinkUnicastTrait, LinkMulticastTrait)規範了所有傳輸類型的共通 API。

  • Link Manager 階層
    Link Manager(如 LinkManagerUnicastTcp, LinkManagerMulticastUdp)負責建立、接受與維護連線。

  • Link 類型

    • 單播 (Unicast):兩個節點之間的點對點通訊

      • 常見操作:read/write, close, get_src/get_dst, get_mtu, is_reliable, is_streamed
    • 多播 (Multicast):一對多的傳輸

      • 目前僅支援 UDP (LinkMulticastUdp)
      • 不可靠傳輸,但適合群組訊息傳遞

支援的協定

Zenoh 支援廣泛的傳輸協定,每個協定都有不同的取捨。

協定 可靠性 安全性 流式傳輸 多播 認證方式 對應 Crate
TCP ✅ 可靠 ❌ 無 ✅ Stream ❌ 無 ❌ 無 zenoh-link-tcp
TLS ✅ 可靠 ✅ TLS ✅ Stream ❌ 無 ✅ 憑證 zenoh-link-tls
UDP ❌ 不可靠 ❌ 無 ❌ Datagram ✅ 支援 ❌ 無 zenoh-link-udp
QUIC ✅ 可靠 ✅ TLS ✅ Stream ❌ 無 ✅ 憑證 zenoh-link-quic
QUIC Datagram ❌ 不可靠 ✅ TLS ❌ Datagram ❌ 無 ✅ 憑證 zenoh-link-quic-datagram
WebSocket ✅ 可靠 ❌ 無 ❌ Message ❌ 無 ❌ 無 zenoh-link-ws
Unix Socket ✅ 可靠 ❌ 無 ✅ Stream ❌ 無 ❌ 無 zenoh-link-unixsock-stream
Serial ❌ 不可靠 ❌ 無 ❌ Datagram ❌ 無 ❌ 無 zenoh-link-serial
Unix Pipe ✅ 可靠 ❌ 無 ✅ Stream ❌ 無 ❌ 無 zenoh-link-unixpipe
Vsock ✅ 可靠 ❌ 無 ✅ Stream ❌ 無 ❌ 無 zenoh-link-vsock

協定實作

每種傳輸協定皆針對其特性實作了專屬的優化與功能:

  • TCP (zenoh-link-tcp):可靠、基於串流的傳輸,使用 tokio::TcpStream

    • 功能:支援 TCP_NODELAY 以降低延遲,TCP_LINGER 設定優雅關閉超時
    • 根據 IP 版本計算 MTU(IPv4: 40-byte 標頭,IPv6: 60-byte 標頭)
    • Unix 系統上可進行平台特定的 MSS(最大分段大小)優化
  • TLS (zenoh-link-tls):在 TCP 上提供安全傳輸,使用 X.509 憑證驗證。

    • 憑證鏈驗證及過期監控
    • 可配置的加密套件與 TLS 版本選擇
    • 透過憑證的 Common Name 進行身份驗證
  • UDP (zenoh-link-udp):不可靠的資料報傳輸,支援 單播多播

    • 多播功能:IPv4/IPv6 群組加入/離開、TTL 設定、介面選擇
    • 支援 DSCP 標記以提升 QoS(服務品質)
    • 封包資訊提取,用於介面綁定
  • QUIC (zenoh-link-quic):基於 UDP 的可靠、安全傳輸,支援多路複用串流。

    • 內建 TLS 1.3 加密與憑證驗證
    • 支援連線遷移與 0-RTT 連線建立
    • 單一連線內多路串流複用
  • WebSocket (zenoh-link-ws):基於 TCP 的訊息導向傳輸,使用 tokio-tungstenite

    • 支援分幀訊息與內建分段處理
    • 可穿越 HTTP 代理與防火牆
  • Unix Domain Socket 與 Pipe:高效能本機進程間通訊(IPC)。

    • Unix Socket Stream (zenoh-link-unixsock-stream):基於串流的 IPC
    • Unix Pipe (zenoh-link-unixpipe):命名管道,用於跨進程訊息傳遞
  • Serial (zenoh-link-serial):透過序列埠進行嵌入式系統的直接通訊。

  • Vsock (zenoh-link-vsock):虛擬 Socket 介面,用於虛擬機與 Host 之間的通訊。


程式碼重點

以下展示 Zenoh 原始碼中一些範例,說明連結層的結構與使用方式。

1. 共用連結 Trait 與型別

// 檔案: io/zenoh-link-commons/src/lib.rs

#[derive(Clone, Debug, Serialize, Hash, PartialEq, Eq)]
pub struct Link {
    pub src: Locator,
    pub dst: Locator,
    pub group: Option<Locator>,
    pub mtu: BatchSize,
    pub is_streamed: bool,
    pub interfaces: Vec<String>,
    pub auth_identifier: LinkAuthId,
    pub priorities: Option<PriorityRange>,
    pub reliability: Option<Reliability>,
}

#[async_trait]
pub trait LocatorInspector: Default {
    fn protocol(&self) -> &str;
    async fn is_multicast(&self, locator: &Locator) -> ZResult<bool>;
    fn is_reliable(&self, locator: &Locator) -> ZResult<bool>;
}

此程式碼定義了所有傳輸實作的 共用界面。每個 Link 包含來源/目的定位器、MTU 資訊、串流特性與認證細節。


2. 單播連結 Trait 實作

// 檔案: io/zenoh-link-commons/src/unicast.rs

#[async_trait]
pub trait LinkUnicastTrait: Send + Sync {
    fn get_mtu(&self) -> BatchSize;
    fn get_src(&self) -> &Locator;
    fn get_dst(&self) -> &Locator;
    fn is_reliable(&self) -> bool;
    fn is_streamed(&self) -> bool;
    fn get_interface_names(&self) -> Vec<String>;
    fn get_auth_id(&self) -> &LinkAuthId;
    async fn write(&self, buffer: &[u8]) -> ZResult<usize>;
    async fn write_all(&self, buffer: &[u8]) -> ZResult<()>;
    async fn read(&self, buffer: &mut [u8]) -> ZResult<usize>;
    async fn read_exact(&self, buffer: &mut [u8]) -> ZResult<()>;
    async fn close(&self) -> ZResult<()>;
}

每個單播連結實作(如 TCP、QUIC、TLS 等)都必須實作此 Trait,提供一個 統一介面,不論底層協定為何。


3. 以TCP 連結具體實作爲例

// 檔案: io/zenoh-links/zenoh-link-tcp/src/unicast.rs

pub struct LinkUnicastTcp {
    socket: UnsafeCell<TcpStream>,
    src_addr: SocketAddr,
    src_locator: Locator,
    dst_addr: SocketAddr,
    dst_locator: Locator,
    mtu: BatchSize,
}

impl LinkUnicastTcp {
    fn new(socket: TcpStream, src_addr: SocketAddr, dst_addr: SocketAddr) -> Self {
        // 設定 TCP NODELAY 選項
        socket.set_nodelay(true).unwrap_or_else(|err| {
            tracing::warn!("無法設定 NODELAY 選項: {}", err);
        });

        // 根據 IP 版本計算 MTU
        let header = match src_addr.ip() {
            std::net::IpAddr::V4(_) => 40, // IPv4 + TCP header
            std::net::IpAddr::V6(_) => 60, // IPv6 + TCP header
        };
        let mtu = TCP_DEFAULT_MTU - header;
        // ... 其餘初始化 ...
    }
}

每個傳輸協定都會實作具體連結行為,同時遵循共用 Trait。


4. 多播連結實作,以UDP爲例

// 檔案: io/zenoh-links/zenoh-link-udp/src/multicast.rs

pub struct LinkMulticastUdp {
    unicast_addr: SocketAddr,
    unicast_locator: Locator,
    unicast_socket: UdpSocket,
    multicast_addr: SocketAddr,
    multicast_locator: Locator,
    mcast_sock: UdpSocket,
}

#[async_trait]
impl LinkMulticastTrait for LinkMulticastUdp {
    async fn close(&self) -> ZResult<()> {
        match self.multicast_addr.ip() {
            IpAddr::V4(dst_ip4) => {
                self.mcast_sock.leave_multicast_v4(dst_ip4, src_ip4)
            },
            IpAddr::V6(dst_ip6) => {
                self.mcast_sock.leave_multicast_v6(&dst_ip6, 0)
            }
        }
        // ... 錯誤處理 ...
    }
}

UDP 多播 需要特別處理群組成員資格,且單播與多播操作使用不同的 socket。


5. 連結管理模式

// 檔案: io/zenoh-link-commons/src/unicast.rs

pub type LinkManagerUnicast = Arc<dyn LinkManagerUnicastTrait>;

#[async_trait]
pub trait LinkManagerUnicastTrait: Send + Sync {
    async fn new_link(&self, endpoint: EndPoint) -> ZResult<LinkUnicast>;
    async fn new_listener(&self, endpoint: EndPoint) -> ZResult<Locator>;
    async fn del_listener(&self, endpoint: &EndPoint) -> ZResult<()>;
    async fn get_listeners(&self) -> Vec<EndPoint>;
    async fn get_locators(&self) -> Vec<Locator>;
}

// 範例: TCP 連結管理器實作
impl LinkManagerUnicastTrait for LinkManagerUnicastTcp {
    async fn new_link(&self, endpoint: EndPoint) -> ZResult<LinkUnicast> {
        let addrs = get_tcp_addrs(endpoint.address()).await?;
        let stream = TcpStream::connect(addr).await?;
        let link = LinkUnicastTcp::new(stream, src_addr, dst_addr);
        Ok(LinkUnicast::from(Arc::new(link)))
    }
}

連結管理器負責管理連線生命週期:建立外部連結、設定監聽器以接受進來的連線,以及管理活躍監聽器的登錄。每個傳輸協定都有自己的管理器實作。

順帶一提,這邊使用的sync_trait自Rust 1.75發佈之後其實就已經內建了,可見其歷史的痕跡XD。


整合至傳輸層

傳輸層 透過完善的介面使用 連結層 的抽象概念:

// 檔案:io/zenoh-transport/src/unicast/link.rs

pub(crate) struct TransportLinkUnicast {
    pub(crate) link: LinkUnicast,
    pub(crate) config: TransportLinkUnicastConfig,
}

impl TransportLinkUnicast {
    pub(crate) async fn send(&self, msg: &TransportMessage) -> ZResult<usize> {
        // 將訊息序列化並寫入底層連結
        let mut link = self.tx();
        link.send(msg).await
    }

    pub(crate) async fn recv(&self) -> ZResult<TransportMessage> {
        // 從連結讀取並將訊息反序列化
        let mut link = self.rx();
        link.recv().await
    }
}

傳輸管理器(Transport Manager) 使用連結層來:

  • 建立與遠端節點的 連線,並使用適當的連結管理器
  • 同時監聽接受多個傳輸上的傳入連線
  • 透過連結傳送/接收 Zenoh 協定訊息(例如:資料、查詢、回覆)
  • 監控連結健康狀態並透明地處理連線失敗
  • 批次最佳化:針對每種連結類型配置 MTU 和批次處理策略

這種分離讓傳輸層能專注於Zenoh 協定語義(路由、分段、壅塞控制),而連結層則負責處理網路連線、通訊端配置以及協定特定最佳化的底層細節。

這樣的設計讓 Transport Layer 專注於 Zenoh 協定邏輯,而 Link Layer 則負責底層網路細節。
筆者將在後續的文章中再進一步討論細節,敬請期待!


使用範例

以下展示 Zenoh 應用中,不同 Link Layer 傳輸方式的實際使用方法:

TCP 傳輸

# 啟動一個 TCP 訂閱者
z_sub --listen tcp/127.0.0.1:7447 --key "demo/example"

# 透過 TCP 連接發佈者
z_pub --connect tcp/127.0.0.1:7447 --key "demo/example" --value "Hello via TCP!"

QUIC

# QUIC 訂閱者使用 TLS
z_sub --listen quic/127.0.0.1:7447 --key "demo/secure"

# QUIC 發佈者
z_pub --connect quic/127.0.0.1:7447 --key "demo/secure" --value "Secure message"

Unix Domain Socket(本地 IPC)

# Unix socket 訂閱者
z_sub --listen unixsock-stream//tmp/zenoh.sock --key "demo/local"

# Unix socket 發佈者
z_pub --connect unixsock-stream//tmp/zenoh.sock --key "demo/local" --value "Local IPC"

同時使用多種傳輸

# 同時監聽多種傳輸
z_sub --listen tcp/127.0.0.1:7447 \
      --listen udp/224.0.0.1:7448 \
      --listen ws/127.0.0.1:8080 \
      --key "demo/**"

# 發佈者可以連接任意一種傳輸
z_pub --connect tcp/127.0.0.1:7447 --key "demo/tcp" --value "TCP message"
z_pub --connect udp/224.0.0.1:7448 --key "demo/udp" --value "UDP message"
z_pub --connect ws/127.0.0.1:8080 --key "demo/ws" --value "WebSocket message"

Zenoh Link Layer 的 精髓 在於,切換傳輸方式只需更改 Locator 字串,而應用程式程式碼保持不變!


功能閘與配置

Zenoh Link Layer 傳輸透過 Cargo 功能標誌(feature flags) 控制,讓你只編譯所需的傳輸方式,減少二進位檔大小並消除不必要的依賴。

預設功能(預設啟用)

# 檔案: zenoh/Cargo.toml

[features]
default = [
  "auth_pubkey",
  "auth_usrpwd",
  "transport_compression",
  "transport_multilink",
  "transport_quic",
  "transport_quic_datagram",
  "transport_tcp",
  "transport_tls",
  "transport_udp",
  "transport_unixsock-stream",
  "transport_ws",
]

所有傳輸功能

# 個別傳輸功能
transport_tcp = ["zenoh-config/transport_tcp", "zenoh-transport/transport_tcp"]
transport_udp = ["zenoh-transport/transport_udp"]
transport_quic = ["zenoh-transport/transport_quic"]
transport_quic_datagram = ["zenoh-transport/transport_quic_datagram"]
transport_tls = ["zenoh-transport/transport_tls"]
transport_ws = ["zenoh-transport/transport_ws"]
transport_serial = ["zenoh-transport/transport_serial"]
transport_unixpipe = ["zenoh-transport/transport_unixpipe"]
transport_unixsock-stream = ["zenoh-transport/transport_unixsock-stream"]
transport_vsock = ["zenoh-transport/transport_vsock"]

# 額外傳輸功能
transport_compression = ["zenoh-transport/transport_compression"]
transport_multilink = ["zenoh-transport/transport_multilink"]

自訂建置範例

僅 TCP 的最小建置(適用於嵌入式或資源受限環境):

cargo build --no-default-features --features "transport_tcp"

僅安全傳輸(適用於安全敏感的應用):

cargo build --no-default-features --features "transport_tls,transport_quic"

包含所有傳輸及實驗性功能(適用於開發/測試):

cargo build --features "transport_serial,transport_vsock,transport_unixpipe"

這個 模組化功能系統 使 Zenoh 可以從小型嵌入式設備(如僅使用 transport_serial)擴展到大型分散式系統(如使用所有可用傳輸)。


認證與安全性

想了解如何在 Zenoh 中使用 TLS 或 QUIC,請參考 官方文件


總結

Zenoh Link Layer 是:

  • 對各種網路協議的 統一抽象
  • 模組化:每個協議各自封裝於獨立 crate 中,皆遵循共用 trait。
  • 可擴展:可新增傳輸方式而不影響上層程式碼。
  • 基礎性:支撐傳輸層,進而支撐整個 Zenoh。

透過隱藏傳輸層複雜性,Link Layer 使 Zenoh 可運作於多種環境 — 從本地進程間通訊到大型分散式網路。


上一篇
Day 05: Zenoh Router:打造跨協定、低開銷的資料骨幹
下一篇
Day 07: Zenoh 會議層(Session Layer)
系列文
30 天玩轉 Zenoh:Rust 助力物聯網、機器人與自駕的高速通訊10
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言