Asynchronous messaging (Pub/Sub)
While SyMqtt excels at synchronous request-response patterns, it natively supports the core asynchronous Publish-Subscribe architecture of MQTT. This pattern is ideal for one-way data streaming, periodic status broadcasting, or processing asynchronous notifications sent by remote controllers and devices.
Publishing asynchronous messages
Section titled “Publishing asynchronous messages”To stream data or fire a command to a specific topic without waiting for a reply (One-Way Messaging), you use the PublishSerialMessageAsync method. You can identify the target destination either by a predefined SyMqttSenderTopicHolder object or by its unique registered string ID.
Method signature
public Task<GenericResult<bool>> PublishSerialMessageAsync( string topicHolderId, SerialMessage serialMessage, SyMqttSendMessageOptions messageOptions, SyMqttDeliveryOptions deliveryOptions = null, CancellationToken cancellationToken = default)Here is how to instantiate an outbound message and broadcast it to your network:
using SyMqtt;
// 1. Create a sender topic holder and register itvar dataStreamTopic = new SyMqttSenderTopicHolder("engine_data", "factory/floor1/engine3/status");mqttBox.AddTopic(dataStreamTopic);
// ... initialize and open connection ...
// 2. Prepare the payload and instantiate the SerialMessage envelopestring payloadData = "TEMPERATURE_CELSIUS:42";var message = SerialMessage.NewMessage(payloadData);
// 3. Publish asynchronously using the topic IDvar result = await mqttBox.PublishSerialMessageAsync("engine_data", message, messageOptions: null);
if (result.Success){ Console.WriteLine("Data published successfully.");}else{ Console.WriteLine($"Publish failed: {result.ErrorDescription}");}Advanced message customization options
Section titled “Advanced message customization options”When publishing, you can control the routing and quality of service by passing optional configuration objects to PublishSerialMessageAsync method.
1. SyMqttSendMessageOptions
Section titled “1. SyMqttSendMessageOptions”This class controls dynamic target identification at the application layer.
| Property | Type | Default Value | Description |
|---|---|---|---|
TargetTopicSuffix | string | null | A unique string appended automatically as a trailing postfix to the outbound topic (delimited by /). Allows a single topic holder to target specific hardware nodes dynamically. |
2. SyMqttDeliveryOptions
Section titled “2. SyMqttDeliveryOptions”This class controls low-level MQTT protocol flags. Properties marked for MQTT v5 require the box to be initialized with SyMqttProtocolVersion.Mqtt500.
| Property | Type | Default Value | Description |
|---|---|---|---|
QualityOfServiceLevel | int | 0 | MQTT QoS level. Can be 0 (At most once), 1 (At least once), or 2 (Exactly once). Applicable to all protocol versions. |
Retain | bool | false | When true, instructs the broker to cache this message for future subscribers. Applicable to all protocol versions. |
MessageExpiryIntervalInSeconds | uint? | null | MQTT v5 only. Time lifetime of the message in seconds inside the broker before it expires. |
SubscriptionIdentifier | uint? | null | MQTT v5 only. An identifier associated with the client’s subscription to track routing origins. |
TopicAlias | ushort? | null | MQTT v5 only. A numeric value used to replace the long topic string, optimizing network bandwidth. |
Fallback Mechanism: If you pass
nullfor thedeliveryOptionsparameter,PublishSerialMessageAsyncautomatically falls back to the values defined in the globalDefaultDeliveryOptionsproperty of yourSyMqttBoxinstance.If your application uses the same QoS or retention flags for most messages, you can configure them once in the global property after instantiation and pass
nullin your individual publish calls to keep your code clean.
Subscribing via topic holders & handlers
Section titled “Subscribing via topic holders & handlers”Receiving incoming data streams asynchronously revolves around the SyMqttReceiverTopicHolder. Instead of forcing you to handle a giant centralized message-received event loop, SyMqtt lets you bind targeted processing logic directly to individual topic wrappers using the AddReceivedMessageHandler method.
Defensive execution architecture
Section titled “Defensive execution architecture”Under the hood, SyMqtt executes incoming message handlers with a strict fire-and-forget defensive mechanism. When an underlying MQTT message lands on a topic:
- It extracts the raw string and triggers the delegate chain using
.GetInvocationList(). - Each attached handler is invoked inside an isolated task frame.
- If a specific user-defined handler crashes, the engine catches the exception inside a faulted state logger instead of allowing the error to propagate and crash the main MQTT connection network thread.
Here is how to register an inbound topic and attach a processing event handler:
using System;using System.Threading.Tasks;using SyMqtt;
// 1. Define an inbound topic wrappervar alarmTopic = new SyMqttReceiverTopicHolder("critical_alarms", "factory/floor1/+/alarms");
// 2. Attach an asynchronous event handleralarmTopic.AddReceivedMessageHandler((payload, messageInfo) =>{ // SyMqtt automatically provides metadata inside the response info envelope Console.WriteLine($"[ALERT] Received on real topic: {messageInfo.PublishTopic}"); Console.WriteLine($"[ALERT] Message Payload: {payload}"); Console.WriteLine($"[ALERT] Network QoS Level: {messageInfo.Qos} | Retained flag: {messageInfo.IsRetained}");
// Return completed task or handle async IO work safely return Task.CompletedTask;});
// 3. Register the topic into the box while disconnectedmqttBox.AddTopic(alarmTopic);Namespace and routing mechanisms
Section titled “Namespace and routing mechanisms”Using topic prefixes
Section titled “Using topic prefixes”If your application shares a multi-tenant broker or requires isolated namespaces, you can set the TopicsPrefix property on the SyMqttBox instance (e.g., mqttBox.TopicsPrefix = "company_abc").
When the box establishes a connection, it automatically appends the prefix followed by a forward slash (company_abc/) to all registered sender and receiver topics during the network handshake, keeping your operational business code clean and free of hardcoded prefix variables.
Dynamic destination routing via TargetTopicSuffix
Section titled “Dynamic destination routing via TargetTopicSuffix”While TopicsPrefix handles the global namespace at the beginning of a topic string, you can dynamically target specific hardware nodes at the end of the topic path using the TargetTopicSuffix property found within SyMqttSendMessageOptions.
If TargetTopicSuffix is populated, the execution engine automatically appends its value as a trailing postfix (delimited by a forward slash /) to the final outbound network topic. This allows a single registered topic holder to target specific, individual controllers dynamically without bloating your application topography with separate static declarations for every device on the factory floor.
Note: The controllers must apply the same rules when subscribing to the corresponding topics. They must know their isolated namespace (
TopicsPrefix) and append their individual ID as a topic postfix.
Here is an example of an asynchronous point-to-point broadcast using options:
using SyMqtt;
var options = new SyMqttSendMessageOptions{ TargetTopicSuffix = "controller_05"};
var message = SerialMessage.NewMessage("FORCE_REBOOT");
// If the TopicsPrefix is "smartcontrol" and the base topic of "engine_data" topic holder is "factory/floor1/engine3/status",// the message is published to: "smartcontrol/factory/floor1/engine3/status/controller_05"await mqttBox.PublishSerialMessageAsync("engine_data", message, messageOptions: options);Now that you know how to build asynchronous streaming topographies, let’s explore the next communication pattern.
Move to the next step to see how to handle strict, synchronous request-response execution channels.