在 .NET 使用 MQTT:MQTTnet 與 Mosquitto 實作
TLDR
- MQTT 採用發佈/訂閱模式,核心角色包含 Broker、Publisher、Subscriber 與 Topic。
- Topic 命名應具備階層性(如
地區/建築/房間/裝置),避免使用$開頭(保留給系統使用)。 - QoS 等級選擇:QoS 0 適用於感測資料;QoS 1 適用於控制指令;QoS 2 適用於金融交易。
- 實際傳遞的 QoS 為發佈者與訂閱者設定值的最小值。
- 使用 Docker 架設 Mosquitto 時,必須確保
password.txt權限正確,並將資料夾擁有者設為 UID 1883。 - 遺囑訊息(Last Will)僅在異常斷線時觸發,正常呼叫
DisconnectAsync()不會發送。 - 保留訊息(Retained Message)適用於儲存設備最新狀態,新訂閱者連線時可立即獲取。
- MQTT 5.0 的持久會話需同時設定
CleanStart = false與SessionExpiryInterval(大於 0)。
MQTT 基本概念與運作
MQTT 是一種輕量級的發佈/訂閱式訊息傳輸協定。系統核心為 Broker,負責接收 Publisher 發送的訊息,並根據 Subscriber 的訂閱規則進行分發。
Topic 命名規則與建議
什麼情況下會遇到主題命名混亂的問題:在設計 IoT 系統架構初期,若未規劃好主題階層,將導致後續難以擴充與維護。
- 階層結構:建議控制在 3-5 層,使用小寫英文與連字號
-。 - 特殊字元:
+(單層萬用字元)與#(多層萬用字元)僅能用於訂閱,不可用於發佈。 - 系統主題:
$開頭的主題(如$SYS/)保留給 Broker 使用,應用程式應避免佔用。
QoS(服務品質等級)
什麼情況下會遇到訊息遺失或重複的問題:在網路環境不穩定的 IoT 場景中,需根據資料重要性選擇 QoS 等級。
- QoS 0:最多傳送一次,效能最高,適用於頻繁更新的感測資料。
- QoS 1:至少傳送一次,確保送達但可能重複,適用於控制指令。
- QoS 2:確保只傳送一次,使用四次握手,適用於金融或計費系統。
使用 Docker Compose 架設 Mosquitto
什麼情況下會遇到容器啟動失敗或權限錯誤:在 Linux 環境下部署 Mosquitto 容器時,若掛載的目錄權限未正確設定,容器將無法寫入設定檔或資料。
關鍵設定與權限修正
在 entrypoint.sh 中,必須確保目錄擁有者為 1883,這是 Mosquitto 容器內的預設執行使用者:
shell
# 修正資料夾權限
chown -R 1883:1883 /mosquitto/config /mosquitto/log /mosquitto/dataTIP
正式環境建議啟用 TLS 加密,並參考官方文件設定 cafile、certfile 與 keyfile。
.NET 使用 MQTTnet 實作
MQTTnet 是 .NET 生態系中功能最完整的 MQTT 函式庫。
基礎發佈與訂閱範例
以下程式碼展示了如何建立連線並進行訊息交換:
csharp
// 建立連線選項
var options = new MqttClientOptionsBuilder()
.WithTcpServer("localhost", 1883)
.WithCredentials("myuser", "mypassword")
.Build();
// 發佈訊息
var message = new MqttApplicationMessageBuilder()
.WithTopic("test/topic")
.WithPayload("Hello, MQTT!")
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce)
.Build();
await client.PublishAsync(message);IoT 溫度感測器模擬
什麼情況下會遇到背景任務管理困難:在模擬多個感測器或長時間監控時,應使用 CancellationToken 來優雅地停止背景執行緒。
csharp
// 監控系統訂閱
monitorClient.ApplicationMessageReceivedAsync += e => {
string json = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
// 處理接收到的資料...
return Task.CompletedTask;
};進階功能實作
遺囑訊息(Last Will and Testament)
什麼情況下會遇到設備離線無法偵測的問題:當設備因斷電或網路中斷異常離線時,可利用遺囑訊息通知監控系統。
csharp
.WithWillTopic("status/client-001")
.WithWillPayload("offline")
.WithWillRetain(true)持久會話(Persistent Session)
什麼情況下會遇到離線訊息遺失的問題:若希望客戶端在重新連線後能接收離線期間的訊息,必須正確設定持久會話。
- MQTT 3.1.1:設定
CleanSession = false。 - MQTT 5.0:需同時設定
CleanStart = false與SessionExpiryInterval(大於 0 的秒數)。
WARNING
在 MQTT 5.0 中,若未設定 SessionExpiryInterval,即使 CleanStart 設為 false,Broker 也可能不會保留 Session。
參考資源
異動歷程
- 2025-11-15 初版文件建立。
