Skip to content

Using MQTT in .NET: MQTTnet and Mosquitto Implementation

TLDR

  • MQTT uses a publish/subscribe pattern, with core roles including Broker, Publisher, Subscriber, and Topic.
  • Topics should follow a hierarchical naming convention (e.g., home/living-room/temperature), and avoid starting with $ (reserved for system use).
  • QoS levels determine transmission reliability: QoS 0 (at most once), QoS 1 (at least once), QoS 2 (exactly once).
  • The actual transmission QoS depends on the minimum value between the Publisher and Subscriber settings.
  • When deploying Mosquitto using Docker, ensure persistence is configured to retain messages, and handle permissions and account initialization via entrypoint.sh.
  • In MQTTnet implementation, Last Will is only triggered during abnormal disconnections; Retained Message is suitable for storing the latest state.
  • For MQTT 5.0 persistent sessions, both CleanStart = false and SessionExpiryInterval > 0 must be set.

MQTT Basic Concepts and Operation

MQTT (Message Queuing Telemetry Transport) is a lightweight publish/subscribe messaging protocol suitable for IoT device communication.

Core Roles

  • Broker: The core of the system, responsible for receiving, filtering, and distributing messages.
  • Publisher: The client that publishes messages.
  • Subscriber: The client that subscribes to topics to receive messages.
  • Topic: A message classification label, with a structure similar to file paths.

Topic Naming Rules and Restrictions

When do naming issues occur: If conventions are not followed when designing topic hierarchies, it can easily lead to subscription failures or permission conflicts.

  • Hierarchy Separator: Use / to separate levels.
  • Case Sensitivity: Home/Temp and home/temp are different topics.
  • System Topics: Topics starting with $ (e.g., $SYS/) are reserved for the Broker and should not be used by applications.
  • Wildcards: + (single level) and # (multi-level) can only be used for subscriptions, not for publishing.

QoS (Quality of Service)

When do QoS selection difficulties occur: When the system has different tolerances for message loss or duplication, the corresponding QoS level must be selected.

  • QoS 0: Highest performance, messages may be lost, suitable for frequently updated sensor data.
  • QoS 1: Ensures delivery, but duplicate messages may be received, suitable for control commands.
  • QoS 2: Four-way handshake, ensures no loss and no duplication, suitable for financial or billing systems.

Final QoS Calculation Formula: Actual QoS = min(Publish QoS, Subscribe QoS).


Deploying Mosquitto with Docker Compose

When do deployment issues occur: In a containerized environment, if permissions or persistence are not configured correctly, it will lead to data loss after a restart or the inability to write to configuration files.

Key Configuration File (mosquitto.conf)

conf
listener 1883
protocol mqtt
allow_anonymous false
password_file /mosquitto/config/password.txt
persistence true
persistence_location /mosquitto/data/

Startup Script (entrypoint.sh)

Must ensure that UID 1883 inside the container has folder permissions:

shell
#!/bin/sh
chown -R 1883:1883 /mosquitto/config /mosquitto/log /mosquitto/data
exec mosquitto -c /mosquitto/config/mosquitto.conf

.NET Implementation with MQTTnet

Basic Publish and Subscribe

Example of using MQTTnet to establish a connection and subscribe to a topic:

csharp
// Subscription example
var subscribeOptions = new MqttClientSubscribeOptionsBuilder()
    .WithTopicFilter(f => f.WithTopic("test/topic").WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce))
    .Build();
await client.SubscribeAsync(subscribeOptions);

// Publishing example
var message = new MqttApplicationMessageBuilder()
    .WithTopic("test/topic")
    .WithPayload("Hello, MQTT!")
    .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce)
    .Build();
await client.PublishAsync(message);

Advanced Feature Implementation

Last Will and Testament

When to use: When you need to monitor whether a device has "abnormally disconnected."

  • A normal call to DisconnectAsync() will not trigger the Last Will.
  • The Broker will only automatically publish the pre-defined Last Will message when the network is interrupted or the program crashes.

Persistent Session

When to use: When you want the client to receive QoS 1/2 messages accumulated during offline periods after reconnecting.

MQTT 5.0 Implementation Recommendation:

csharp
var options = new MqttClientOptionsBuilder()
    .WithClientId("client-001")
    .WithCleanStart(false)              // Attempt to use existing Session
    .WithSessionExpiryInterval(600)     // Retain for 10 minutes after disconnection
    .Build();

TIP

The complete executable example for this article: CloudyWing/MqttNetMosquittoSample.


Changelog

    • Initial document creation.
    • Added link to the corresponding GitHub sample project.