CommsChampion Ecosystem MQTT v5 Client
MQTT v5 Client Library.
|
The MQTT v5 Client Library from the CommsChampion Ecosystem provides simple, asynchronous, non-blocking, and easy to use interface to operate MQTT v5 client. The library doesn't make any assumption on the system it is running on, as well as on the type of I/O link being used to communicate its data to the MQTT v5 capable broker.
It is a responsibility of the calling application to manage network connectivity as well as measure time required for the correct operation of the MQTT v5 protocol.
The library allows the application to have a full control over the raw data for any extra analysis and/or manipulation, such as encryption or extra framing.
The version is of the library applicable to this documentation is defined in the cc_mqtt5_client/common.h file using the following defines:
To use this MQTT v5 Client Library use the following single include statement:
The library supports multiple independent MQTT v5 client sessions. The allocation of data structures relevant to a single client is performed using cc_mqtt5_client_alloc() function.
All other functions are client specific, they receive the returned handle as their first parameter.
When work with allocated client is complete, it must be freed using cc_mqtt5_client_free() function.
When working with C++ it is advised to use a smart pointer with a custom deleter.
IMPORTANT: The function cc_mqtt5_client_free() must NOT be called from within a callback. Use next event loop iteration.
In order to properly function the library requires setting several callbacks.
To client application must assign a callback for the library to be able to send binary data out to the connected broker.
See also the documentation of the CC_Mqtt5SendOutputDataCb callback function definition.
In the invoked callback the application is responsible to send the provided data over the I/O link. The application can also perform extra data manipulation like encryption.
The reported data resides in internal data structures of the client library, which can be updated / deleted right after the callback function returns. It means the data may need to be copied into some other buffer, which will be held intact until the send over I/O link operation is complete.
The client application must assign a callback for the library to report discovered broker disconnection.
See also the documentation of the CC_Mqtt5BrokerDisconnectReportCb callback function definition.
When the broker disconnection is due to reception of the DISCONNECT message from the broker the "info" parameter will report a disconnection reason as well as extra information reported by the broker. If the broker disconnection is due to other reasons, the "info" parameter will be NULL. See also Unsolicited Broker Disconnection below for details.
The client application must assign a callback for the library to report application level messages received from the broker.
See also the documentation of the CC_Mqtt5MessageReceivedReportCb callback function definition.
For the correct operation of the MQTT v5 client side of the protocol, the library requires an ability to measure time. This responsibility is delegated to the application.
The easiest (although not very efficient or very precise) method is to periodically (say every 20ms - 50ms) call the cc_mqtt5_client_tick() function reporting the amount of elapsed milliseconds:
The library will check if some inner timer has expired and may initiate some response via invocation one of the registered callbacks.
Another (recommended) method is to register a callback so the library may request the time measurement from the application, and when the requested time expires, the application is expected to call the cc_mqtt5_client_tick() function reporting amount of elapsed milliseconds.
It is allowed to invoke the cc_mqtt5_client_tick() before the actual requested timeout has expired, just make sure that the correct amount of elapsed milliseconds is reported. When the cc_mqtt5_client_tick() is invoked, it is assumed that the previously requested tick programming has been cancelled and the registered callback requesting to re-program the timer may be invoked again from within the cc_mqtt5_client_tick().
See also the documentation of the CC_Mqtt5NextTickProgramCb callback function definition.
In case of callback approach for time measurement is chosen, another callback function (in addition to requesting the new timer programming) to allow interruption of the previously programmed timer must also to be registered.
See also the documentation of the CC_Mqtt5CancelNextTickWaitCb callback function definition.
Usually the callbacks of canceling the previously programmed tick and programming a new one will be invoked as a side effect of other events, like report of the incoming data or client requesting to perform one of the available operations.
Sometimes the library may exhibit unexpected behaviour, like rejecting some of the parameters. To allow getting extra guidance information of what went wrong it is possible to register optional error logging callback.
See also the documentation of the CC_Mqtt5ErrorLogCb callback function definition.
It is the responsibility of the application to receive data from the broker and report it to the library. The report is performed using the cc_mqtt5_client_process_data() function.
The application is responsible to maintain the input buffer. The value returned from the cc_mqtt5_client_process_data() function reports amount of consumed bytes. In case not all of the reported bytes were consumed the application is responsible to keep them and report again with new appended data when such arrives.
When new data chunk is reported the library may invoke several callbacks, such as reporting received message, sending new data out, as well as canceling the old and programming new tick timeout.
The library abstracts away multiple MQTT v5 protocol based "operations". Every such operation has multiple stages:
During the send stage the application is expected to provide the callback to report to the application when the operation is complete. One of the parameters of the callback is always "status" of the CC_Mqtt5AsyncOpStatus type. It indicates whether the operation was successfully complete. In addition to the status it reports some extra information reported by the broker. The information from the broker is available if and only if the status is CC_Mqtt5AsyncOpStatus_Complete.
The send stage function also returns CC_Mqtt5ErrorCode value to indicate whether the send was successfully performed. The provided callback will be invoked if and only if the send returns CC_Mqtt5ErrorCode_Success.
After the send stage the handle returned in the prepare stage can be discarded (no explicit de-allocation is needed / supported) regardless of the return code. After successful send the handle still remains valid until the callback invocation and can be used to cancel the operation. Note that in case the appropriate message has already be sent to the broker, cancelling the outstanding operation can be dangerous. When broker sends a response and client is not expecting it any more, unexpected behaviour (like treating the unexpected message as "protocol error" and disconnecting from the broker) may happen.
In case something went wrong during the configure stage, it is possible to de-allocate the prepared operation using the cancel request. After performing the cancel stage the allocated handle is no longer valid.
IMPORTANT LIBRARY LIMITATION: Once an operation is prepared, it must be be immediately configured and sent (or cancelled) before any other other operation can be prepared. For example:
After sending any operation request to the broker, the client library has to allow some time for the broker to process the request. If it takes too much time, the client must report that operation has failed via the set callback. By default the client library allows 2 seconds for such response to arrive. Changing this default value is possible using the cc_mqtt5_client_set_default_response_timeout() function, and retrieving of the currently configured value can be done using the cc_mqtt5_client_get_default_response_timeout() function.
To connect to broker use connect operation.
NOTE that the cc_mqtt5_client_connect_prepare() cannot be called from within a callback. For example, if the broker disconnection is reported via callback then the cc_mqtt5_client_connect_prepare() cannot be invoked right away. It needs to be postponed until the next event loop iteration.
When created, the "connect" operation inherits the Default Response Timeout configuration. It can be changed for the allocated operation using the cc_mqtt5_client_connect_set_response_timeout() function.
To retrieve the configured response timeout use the cc_mqtt5_client_connect_get_response_timeout() function.
The "basic" configuration means no extra MQTT v5 properties assigned to the message.
IMPORTANT: MQTT v5 specification allows reconnection to the broker while requesting previous session restoration (via "clean start" bit). By default, the client library verifies that the message received from the broker was actually subscribed to before reporting the message to the application (see Receiving Messages for details). To prevent potential errors of the client and broker inner states being out of sync, the first "connect" operation requires setting the CC_Mqtt5ConnectBasicConfig::m_cleanStart value to true. The only exception to this rule is when the subscription verification on message reception was disabled (described in the Receiving Messages section below). In case the subscription verification is still enabled and the CC_Mqtt5ConnectBasicConfig::m_cleanStart value is NOT set to true, the function rejects the configuration with the CC_Mqtt5ErrorCode_BadParam error code. Any subsequent reconnection attempts will allow setting the value to false.
See also documentation of the CC_Mqtt5ConnectBasicConfig structure.
To perform will configuration use the cc_mqtt5_client_connect_config_will() function.
See also documentation of the CC_Mqtt5ConnectWillConfig structure.
IMPORTANT: The cc_mqtt5_client_connect_config_will() function mustn't be called more than once for a single "connect" operation. Otherwise it may result in setting multiple will properties of the same type, which is the "Protocol Error" according to the MQTT v5 specification.
The MQTT v5 specification allows adding several "User Properties" specific to the will. The library allows such assignment using multiple invocations of the cc_mqtt5_client_connect_add_will_user_prop() function.
See also documentation of the CC_Mqtt5UserProp structure.
To add extra MQTT v5 specific properties to the connection request use cc_mqtt5_client_connect_config_extra() function.
See also documentation of the CC_Mqtt5ConnectExtraConfig structure.
IMPORTANT: The cc_mqtt5_client_connect_config_extra() function mustn't be called more than once for a single "connect" operation. Otherwise it may result in setting multiple properties of the same type, which is the "Protocol Error" according to the MQTT v5 specification.
In case the extended authentication handshake is needed use cc_mqtt5_client_connect_config_auth() function.
During the "connect" operation, both client and the broker can exchange multiple AUTH messages. When such message arrive the library will invoke provided handshake callback. The responsibility of the callback is to analyze the incoming authentication data and populate the response information. The client can fail the handshake by returning the CC_Mqtt5AuthErrorCode_Disconnect instead of the CC_Mqtt5AuthErrorCode_Continue.
When client side fails the authentication, the DISCONNECT message is sent to the broker and "connect" operation callback (see Sending Connection Request below) will be invoked with CC_Mqtt5AsyncOpStatus_Aborted as status report. The broker disconnection report callback will NOT be invoked, because the connection hasn't really happened yet.
IMPORTANT: The cc_mqtt5_client_connect_config_auth() function mustn't be called more than once for a single "connect" operation. Otherwise it may result in setting multiple properties of the same type, which is the "Protocol Error" according to the MQTT v5 specification.
The MQTT v5 specification allows attaching any number of the "User Properties" to the CONNECT message. The library allows such assignment using multiple invocations of the cc_mqtt5_client_connect_add_user_prop() function.
See also documentation of the CC_Mqtt5UserProp structure.
When all the necessary configurations are performed for the allocated "connect" operation it can actually be sent to the broker. To initiate sending use the cc_mqtt5_client_connect_send() function.
The provided callback will be invoked when the "connect" operation is complete if and only if the function returns CC_Mqtt5ErrorCode_Success.
The handle returned by the cc_mqtt5_client_connect_prepare() function can be discarded (there is no free / de-allocation) right after the cc_mqtt5_client_connect_send() invocation regardless of the returned error code. However, the handle remains valid until the callback is called (in case the CC_Mqtt5ErrorCode_Success was returned). The valid handle can be used to cancel the operation before the completion callback is invoked.
When the "connect" operation completion callback is invoked the reported "response" information is present if and only if the "status" is CC_Mqtt5AsyncOpStatus_Complete.
NOTE that only single "connect" operation is allowed at a time, any attempt to prepare a new one via cc_mqtt5_client_connect_prepare() will be rejected until the "connect" operation completion callback is invoked or the operation is cancelled.
IMPORTANT: The response information from the broker may report that some features on the broker side are disabled. The library will reject any subsequent operation configuration which contradict the broker's capabilities. For example, attempt to subscribe to the topic containing wildcards, when the reported CC_Mqtt5ConnectResponse::m_wildcardSubAvailable is false, will be rejected with CC_Mqtt5ErrorCode_BadParam.
Quote from the MQTT v5 specification:
When the callback reporting the connection status is invoked, it is responsibility of the application to check the CC_Mqtt5ConnectResponse::m_reasonCode value. If it's CC_Mqtt5ReasonCode_UnspecifiedError or greater, the application is responsible to close the network connection and retry the "connect" operation after the network connection to the broker is re-established. The same should be done when the "connect" operation is not properly completed, i.e. the reported status is NOT CC_Mqtt5AsyncOpStatus_Complete.
While the handle returned by the cc_mqtt5_client_connect_prepare() is still valid it is possible to cancel / discard the operation.
In case the cc_mqtt5_client_connect_send() function was successfully called before the cc_mqtt5_client_connect_cancel(), the operation is cancelled without callback invocation.
In many use cases the "connect" operation can be quite simple with a lot of defaults. To simplify the sequence of the operation preparation and handling of errors, the library provides several wrapper functions that can be used:
For example:
Note that the wrapper functions do NOT expose the handle returned by the cc_mqtt5_client_connect_prepare(). It means that it's not possible to cancel the "connect" operation before its completion.
At any time it is possible to check the internal state of the library of whether it's properly connected to the broker.
To intentionally disconnect from broker use disconnect operation. The unsolicited disconnection from the broker is described in ref Unsolicited Broker Disconnection section below.
The configuration of the "disconnect" operation is performed using a single cc_mqtt5_client_disconnect_config() function.
The MQTT v5 specification allows attaching any number of the "User Properties" to the DISCONNECT message. The library allows such assignment using multiple invocations of the cc_mqtt5_client_disconnect_add_user_prop() function.
See also documentation of the CC_Mqtt5UserProp structure.
When the necessary configuration is performed for the allocated "disconnect" operation it can be sent to the broker. To initiate sending use the cc_mqtt5_client_disconnect_send() function.
NOTE that the cc_mqtt5_client_disconnect_send() function doesn't receive any callback because there is no expected response to the DISCONNECT message from the broker. The disconnection effect is immediate. The application is expected to terminate the network connection (while making sure that the the requested data is actually sent). The handle returned by the cc_mqtt5_client_disconnect_prepare() must be discarded.
In case there are other asynchronous operations that hasn't been completed yet, their completion callback is automatically invoked with CC_Mqtt5AsyncOpStatus_Aborted status.
IMPORTANT: In case of sending the explicit disconnection request the registered unsolicited disconnection callback is NOT invoked.
After the disconnection the application can re-establish network connection to the broker and perform the connect operation again.
While the handle returned by the cc_mqtt5_client_disconnect_prepare() is still valid it is possible to cancel / discard the operation.
In many use cases the "disconnect" operation can be quite simple with a lot of defaults. To simplify the sequence of the operation preparation and handling of errors, the library provides wrapper function(s) that can be used:
For example:
To subscribe to receive incoming messages use subscribe operation. The application can issue multiple "subscribe" operations in parallel.
When created, the "subscribe" operation inherits the Default Response Timeout configuration. It can be changed for the allocated operation using the cc_mqtt5_client_subscribe_set_response_timeout() function.
To retrieve the configured response timeout use the cc_mqtt5_client_subscribe_get_response_timeout() function.
Single SUBSCRIBE message can carry multiple topic subscriptions. Use separate cc_mqtt5_client_subscribe_config_topic() function invocation to configure each such subscription.
See also documentation of the CC_Mqtt5SubscribeTopicConfig structure.
By default the library will perform the analysis of the submitted topic format and reject it if topic format is incorrect. However, for performance reasons it is possible to disable such verification when client application ensures that no invalid topics are used. That includes not submitting topics with wildcards when the broker doesn't support them (see CC_Mqtt5ConnectResponse), or not using shared subscription for the same reason.
NOTE that the configuration is global per client and not per "subscribe" operation.
Also note that the same function controls the verification of the "subscribe", "unsubscribe" and "publish" topic formats.
To retrieve the current configuration use cc_mqtt5_client_get_verify_outgoing_topic_enabled() function.
To add extra MQTT v5 specific properties to the subscription request use cc_mqtt5_client_subscribe_config_extra() function.
See also documentation of the CC_Mqtt5SubscribeExtraConfig structure.
Note that explicitly initializing the CC_Mqtt5SubscribeExtraConfig configuration object is not really necessary, because it has only a single CC_Mqtt5SubscribeExtraConfig::m_subId member, which is going to be overwritten as part of the configuration. However, calling the cc_mqtt5_client_subscribe_init_config_extra() is still recommended to make the application's code future updates proof. Potentially the MQTT v5 specification and as the result this library can be updated by adding new property to the CC_Mqtt5SubscribeExtraConfig struct. Having the "unnecessary" call to the cc_mqtt5_client_subscribe_init_config_extra() function makes sure that newly added property gets initialized to the default value without any need to update the application's code.
Also note that allocating and managing the "Subscription Identifier" property values is completely application's responsibility. The broker is responsible to report the assigned value when sending relevant message, and the client can use the reported value for easier dispatching of the received message to appropriate handling functionality.
The MQTT v5 specification allows attaching any number of the "User Properties" to the SUBSCRIBE message. The library allows such assignment using multiple invocations of the cc_mqtt5_client_subscribe_add_user_prop() function.
See also documentation of the CC_Mqtt5UserProp structure.
When all the necessary configurations are performed for the allocated "subscribe" operation it can actually be sent to the broker. To initiate sending use the cc_mqtt5_client_subscribe_send() function.
The provided callback will be invoked when the "subscribe" operation is complete if and only if the function returns CC_Mqtt5ErrorCode_Success.
The handle returned by the cc_mqtt5_client_subscribe_prepare() function can be discarded (there is no free / de-allocation) right after the cc_mqtt5_client_subscribe_send() invocation regardless of the returned error code. However, the handle remains valid until the callback is called (in case the CC_Mqtt5ErrorCode_Success was returned). The valid handle can be used to cancel the operation before the completion callback is invoked.
Note that the callback function receives the "subscribe" operation handle as its second parameter. Although the handle is already invalid and cannot be used in any other function, it allows the application to identify the original "subscribe" request if multiple have been issued in parallel and use the same callback function for all of them.
When the "subscribe" operation completion callback is invoked the reported "response" information is present if and only if the "status" is CC_Mqtt5AsyncOpStatus_Complete.
While the handle returned by the cc_mqtt5_client_subscribe_prepare() is still valid it is possible to cancel / discard the operation.
In case the cc_mqtt5_client_subscribe_send() function was successfully called before the cc_mqtt5_client_subscribe_cancel(), the operation is cancelled without callback invocation.
In many use cases the "subscribe" operation can be quite simple with a lot of defaults. To simplify the sequence of the operation preparation and handling of errors, the library provides several wrapper functions that can be used:
For example:
Note that the wrapper functions do NOT expose the handle returned by the cc_mqtt5_client_subscribe_prepare(). It means that it's not possible to cancel the "subscribe" operation before its completion or identify the subscribe operation by the reported handle when the completion callback is invoked.
To unsubscribe from receiving incoming messages use unsubscribe operation. The application can issue multiple "unsubscribe" operations in parallel.
When created, the "unsubscribe" operation inherits the Default Response Timeout configuration. It can be changed for the allocated operation using the cc_mqtt5_client_unsubscribe_set_response_timeout() function.
To retrieve the configured response timeout use the cc_mqtt5_client_unsubscribe_get_response_timeout() function.
Single UNSUBSCRIBE message can carry multiple topic unsubscriptions. Use cc_mqtt5_client_unsubscribe_config_topic() function to configure each such unsubscription.
See also documentation of the CC_Mqtt5UnsubscribeTopicConfig structure.
Note that explicitly initializing the CC_Mqtt5UnsubscribeTopicConfig configuration object is not really necessary, because it has only a single CC_Mqtt5UnsubscribeTopicConfig::m_topic member, which is going to be overwritten as part of the configuration. However, calling the cc_mqtt5_client_unsubscribe_init_config_topic() is still recommended to make the application's code future updates proof. Potentially the MQTT v5 specification and as the result this library can be updated by adding new fields to the CC_Mqtt5UnsubscribeTopicConfig struct. Having the "unnecessary" call to the cc_mqtt5_client_unsubscribe_init_config_topic() function makes sure that newly added field gets initialized to the default value without any need to update the application's code.
By default the library will perform the analysis of the submitted topic format and reject it if topic format is incorrect. However, for performance reasons it is possible to disable such verification when client application ensures that no invalid topics are used.
NOTE that the configuration is global per client and not per "unsubscribe" operation.
Also note that the same function controls the verification of the "subscribe", "unsubscribe" and "publish" topic formats.
To retrieve the current configuration use cc_mqtt5_client_get_verify_outgoing_topic_enabled() function.
The MQTT v5 specification allows attaching any number of the "User Properties" to the UNSUBSCRIBE message. The library allows such assignment using multiple invocations of the cc_mqtt5_client_unsubscribe_add_user_prop() function.
See also documentation of the CC_Mqtt5UserProp structure.
When all the necessary configurations are performed for the allocated "unsubscribe" operation it can actually be sent to the broker. To initiate sending use the cc_mqtt5_client_unsubscribe_send() function.
The provided callback will be invoked when the "unsubscribe" operation is complete if and only if the function returns CC_Mqtt5ErrorCode_Success.
The handle returned by the cc_mqtt5_client_unsubscribe_prepare() function can be discarded (there is no free / de-allocation) right after the cc_mqtt5_client_unsubscribe_send() invocation regardless of the returned error code. However, the handle remains valid until the callback is called (in case the CC_Mqtt5ErrorCode_Success was returned). The valid handle can be used to cancel the operation before the completion callback is invoked.
Note that the callback function receives the "unsubscribe" operation handle as its second parameter. Although the handle is already invalid and cannot be used in any other function, it allows the application to identify the original "unsubscribe" request if multiple have been issued in parallel and use the same callback function for all of them.
When the "unsubscribe" operation completion callback is invoked the reported "response" information is present if and only if the "status" is CC_Mqtt5AsyncOpStatus_Complete.
While the handle returned by the cc_mqtt5_client_unsubscribe_prepare() is still valid it is possible to cancel / discard the operation.
In case the cc_mqtt5_client_unsubscribe_send() function was successfully called before the cc_mqtt5_client_unsubscribe_cancel(), the operation is cancelled without callback invocation.
In many use cases the "unsubscribe" operation can be quite simple with a lot of defaults. To simplify the sequence of the operation preparation and handling of errors, the library provides several wrapper functions that can be used:
For example:
Note that the wrapper functions do NOT expose the handle returned by the cc_mqtt5_client_unsubscribe_prepare(). It means that it's not possible to cancel the "unsubscribe" operation before its completion or identify the unsubscribe operation by the reported handle when the completion callback is invoked.
To publish messages to the broker use publish operation.
When publishing messages with QoS value CC_Mqtt5QoS_AtLeastOnceDelivery or above, the response from the broker is expected.
When created, the "publish" operation inherits the Default Response Timeout configuration. It can be changed for the allocated operation using the cc_mqtt5_client_publish_set_response_timeout() function.
To retrieve the configured response timeout use the cc_mqtt5_client_publish_get_response_timeout() function.
The MQTT v5 specification has a mechanism of insured delivery of the published message to the broker. In the case of not 100% reliable connection the messages can get lost and needs to be re-sent. The default amount of re-sends is 2, i.e. when the first send is not acknowledged by the broker, it is tried again with DUP flag is set in the PUBLISH message. When the second attempt is not acknowledged, then the publish operation is terminated with appropriate status report. It is possible to change the default by using cc_mqtt5_client_publish_set_resend_attempts() function. The DUP flag will be set in all the re-sent PUBLISH messages.
To retrieve the configured resend attempts number use the cc_mqtt5_client_publish_get_resend_attempts() function.
The "basic" configuration means no extra MQTT v5 properties assigned to the message, with one small exception of using topic alias (covered below as a separate subject).
See also documentation of the CC_Mqtt5PublishBasicConfig structure.
By default the library will perform the analysis of the submitted topic format and reject it if topic format is incorrect. However, for performance reasons it is possible to disable such verification when client application ensures that no invalid topics are used.
To retrieve the current configuration use the cc_mqtt5_client_get_verify_outgoing_topic_enabled() function.
NOTE that the configuration is global per client and not per "publish" operation.
Also note that the same function controls the verification of the "subscribe", "unsubscribe", and "publish" filter / topic formats.
To add extra MQTT v5 specific properties to the publish request use cc_mqtt5_client_publish_config_extra() function.
See also documentation of the CC_Mqtt5PublishExtraConfig structure.
The MQTT v5 specification allows attaching any number of the "User Properties" to the PUBLISH message. The library allows such assignment using multiple invocations of the cc_mqtt5_client_publish_add_user_prop() function.
See also documentation of the CC_Mqtt5UserProp structure.
When all the necessary configurations are performed for the allocated "publish" operation it can actually be sent to the broker. To initiate sending use the cc_mqtt5_client_publish_send() function.
The provided callback will be invoked when the "publish" operation is complete if and only if the function returns CC_Mqtt5ErrorCode_Success.
The callback pointer can also be NULL. In such case the completion of the publish operation is silent.
When QoS value is CC_Mqtt5QoS_AtMostOnceDelivery (see the basic configuration), there is no broker response to wait for and the callback is invoked right away after sending the serialized data.
The handle returned by the cc_mqtt5_client_publish_prepare() function can be discarded (there is no free / de-allocation) right after the cc_mqtt5_client_publish_send() invocation regardless of the returned error code. However, the handle remains valid until the callback is called (in case the CC_Mqtt5ErrorCode_Success was returned). The valid handle can be used to cancel the operation before the completion callback is invoked.
Note that the callback function receives the "publish" operation handle as its second parameter. Although the handle is already invalid and cannot be used in any other function, it allows the application to identify the original "publish" request if multiple have been issued in parallel and use the same callback function for all of them.
When the "publish" operation completion callback is invoked the reported "response" information is present if and only if the "status" is CC_Mqtt5AsyncOpStatus_Complete and the publish QoS was 1 (CC_Mqtt5QoS_AtLeastOnceDelivery) or higher.
When the callback reporting the publish status is invoked, it is responsibility of the application to check the CC_Mqtt5PublishResponse::m_reasonCode value. If it's CC_Mqtt5ReasonCode_UnspecifiedError or greater means the "publish" operation wasn't successful.
While the handle returned by the cc_mqtt5_client_publish_prepare() is still valid it is possible to cancel / discard the operation.
In case the cc_mqtt5_client_publish_send() function was successfully called before the cc_mqtt5_client_publish_cancel(), the operation is cancelled without callback invocation.
The MQTT v5 specification allows usage of the numeric topic aliases to be able to reduce amount of bytes per publish. In order for the library to start using topic aliases, they need to be allocated (registered) using the cc_mqtt5_client_pub_topic_alias_alloc() function.
Note that the broker reports maximum amount of allowed topic aliases in its response to the "connect" request via the CC_Mqtt5ConnectResponse::m_topicAliasMax ("Topic Alias Maximum" property). It means that the allocation of the topic aliases is allowed only after the successful "connect" operation.
Also note that the topic aliases are not part of the session state. For every re-connection attempt all the previously allocated topic aliases get cleared in the internal data structures and they need to be allocated again after the successful connection.
The last parameter passed to the cc_mqtt5_client_pub_topic_alias_alloc() function is used to specify how many times the topic alias is reported to the broker alongside the topic string in the "publish" operation when only QoS0 publishes are used. For the QoS1 and QoS2 "publish" operations there is an acknowledgement message from the broker indicating that the topic alias is received and known to the broker. However, when QoS0 only publishes are performed, the reception of the topic alias registration request by the broker is not ensured. Also the MQTT v5 specification explicitly states that it is "Protocol Error" to use unknown topic alias.
Based on the said above, it can be prone to unexpected protocol error disconnection to use single QoS0 publish message to report topic alias to the broker, so the last parameter of the cc_mqtt5_client_pub_topic_alias_alloc() function indicates how many times the topic alias needs to be reported to the broker alongside the topic string itself before client is allowed to send topic alias only without the topic string. However if any QoS1 or QoS2 messages are successfully published to the broker with the same topic alias, then all the subsequent QoS0 message will use topic alias without the topic string.
To stop using topic alias for the specific topic, use cc_mqtt5_client_pub_topic_alias_free() function to free the allocated alias.
To inquire how many topic aliases have already been allocated, use the cc_mqtt5_client_pub_topic_alias_count() function.
To check whether a topic alias is allocated for specific topic use the cc_mqtt5_client_pub_topic_alias_is_allocated() function.
Once the topic alias is successfully allocated, the basic configuration of the "publish" operation allows control of whether and how to use topic alias via the CC_Mqtt5PublishBasicConfig::m_topicAliasPref data member.
When the connect operation is complete, the "Receive Maximum" property set by the broker is reported via the CC_Mqtt5ConnectResponse::m_highQosSendLimit value. The application can monitor amount of incomplete "publish" operations using the cc_mqtt5_client_publish_count() function and decide whether to issue any new publishes.
However, when the "Receive Maximum" limit is reached and a new publish is issued by the application, the library will postpone sending the actual PUBLISH message until one of the previous "publish" operations is complete. It is possible to inquire whether the operation was initiated (PUBLISH message is sent) via the cc_mqtt5_client_publish_was_initiated() function. If the function returns false, the "publish" operation can be safely cancelled without any possible side effects.
Beware of calling cc_mqtt5_client_publish_was_initiated() when QoS0 publish operation is issued. In most cases the operation is completed right away and the operation handle is not valid any more when the cc_mqtt5_client_publish_send() invocation returns.
The library implements strict message ordering for the same QoS messages required by the MQTT v5 specification. However, when different QoS messages are sent, the messages may arrive to the broker as well as recipient client in different order. For example, if all the publishes below issued together in the specified order, the Message2 and Message3 will probably arrive before Message1.
The library also provides an ability to force strict message ordering for all values of QoS using the cc_mqtt5_client_publish_set_ordering() function.
The current configuration can be retrieved using cc_mqtt5_client_publish_get_ordering() function. The default publish ordering is CC_Mqtt5PublishOrdering_SameQos.
When a strict message ordering for all the messages is enabled (by setting CC_Mqtt5PublishOrdering_Full), The PUBLISH message is postponed if any of the statements below are true:
The rules above allow sending the same as well as increasing QoS messages in parallel. For example, if all the publishes below issued together in the specified order, the Message11 and Message12 will be sent, while Message13 and Message14 are postponed until the delivery of the Message12 is complete.
In many use cases the "publish" operation can be quite simple with a lot of defaults. To simplify the sequence of the operation preparation and handling of errors, the library provides several wrapper functions that can be used:
For example:
Note that the wrapper functions do NOT expose the handle returned by the cc_mqtt5_client_publish_prepare(). It means that it's not possible to cancel the "publish" operation before its completion or identify the publish operation by the reported handle when the completion callback is invoked.
Right after the successful "connect" operation, the library starts expecting the arrival of the new messages and reports it via the registered callback. By default the library monitors the topics the client application subscribed to and does not report "rogue" messages from the broker.
Note that only topics themselves are monitored, and not the requested maximal QoS values. It means that if application requested maximal QoS1 for a particular subscription, but the broker sends message using the QoS2, the message is still going to be reported to the application.
However, if the broker is trusted to do the right thing, i.e. fully comply to the specification, it is possible to enable / disable the incoming topics check using the cc_mqtt5_client_set_verify_incoming_msg_subscribed() for performance reasons.
To retrieve the current configuration use the cc_mqtt5_client_get_verify_incoming_msg_subscribed() function.
WARNING: When the incoming message subscription verification is disabled, the library does NOT track any new subscription requests, which can result in dropping legit messages and not reporting them to the application after the subscription verification is re-enabled later.
Similarly, the library also by default verifies the incoming topic format, that it doesn't contain wildcard characters. To enable / disable such verification use the cc_mqtt5_client_set_verify_incoming_topic_enabled() function.
To retrieve the current configuration use the cc_mqtt5_client_get_verify_incoming_topic_enabled() function.
To prioritize the in-order reception of the messages, the message report callback is invoked immediately on reception of the QoS2 PUBLISH message. Just like it is shown in the "Figure 4.3" of the MQTT v5 specification.
The QoS2 publish operation initiated by the broker requires exchange of multiple messages between the broker and the client. When the library responds with the PUBREC message, the broker is expected to send PUBREL back. The library uses the Default Response Timeout configuration to measure the time frame during which it allows the reception of the corresponding PUBREL message. If the latter doesn't arrive in time, the inner state of the message reception gets discarded resulting in the future rejection of the relevant PUBREL from the broker (if such arrives). Depending on case whether the PUBREC message was actually received by the broker and how the broker is implemented one of the following scenarios is possible:
With all said above it might be necessary to increase the response timeout for slow networks.
The MQTT v5 specification allows performing re-authentication after the successful "connect" operation using the same authentication method. To do so use the reauth operation.
When created, the "reauth" operation inherits the Default Response Timeout configuration. It can be changed for the allocated operation using the cc_mqtt5_client_reauth_set_response_timeout() function.
To retrieve the configured response timeout use the cc_mqtt5_client_reauth_get_response_timeout() function.
To configure the re-authentication handshake use the cc_mqtt5_client_reauth_config_auth() function. It is very similar to the Extended Authentication Handshake Configuration of the "connect" operation.
During the "reauth" operation, both client and the broker exchange multiple AUTH messages. When such message arrive the library will invoke provided handshake callback. The responsibility of the callback is to analyze the incoming authentication data and populate the response information. The client can fail the handshake by returning the CC_Mqtt5AuthErrorCode_Disconnect instead of the CC_Mqtt5AuthErrorCode_Continue.
In case the application fails the re-authentication handshake by returning the CC_Mqtt5AuthErrorCode_Disconnect value library preforms the following steps:
IMPORTANT: The cc_mqtt5_client_reauth_config_auth() function mustn't be called more than once for a single "reauth" operation. Otherwise it may result in setting multiple properties of the same type, which is the "Protocol Error" according to the MQTT v5 specification.
When all the necessary configurations are performed for the allocated "reauth" operation it can actually be sent to the broker. To initiate sending use the cc_mqtt5_client_reauth_send() function.
The provided callback will be invoked when the "reauth" operation is complete if and only if the function returns CC_Mqtt5ErrorCode_Success.
The handle returned by the cc_mqtt5_client_reauth_prepare() function can be discarded (there is no free / de-allocation) right after the cc_mqtt5_client_reauth_send() invocation regardless of the returned error code. However, the handle remains valid until the callback is called (in case the CC_Mqtt5ErrorCode_Success was returned). The valid handle can be used to cancel the operation before the completion callback is invoked.
When the "reauth" operation completion callback is invoked the reported "response" information is present if and only if the "status" is CC_Mqtt5AsyncOpStatus_Complete.
NOTE that only single "reauth" operation is allowed at a time, any attempt to prepare a new one via cc_mqtt5_client_reauth_prepare() will be rejected until the "reauth" operation completion callback is invoked or the operation is cancelled.
While the handle returned by the cc_mqtt5_client_reauth_prepare() is still valid it is possible to cancel / discard the operation.
In case the cc_mqtt5_client_reauth_send() function was successfully called before the cc_mqtt5_client_reauth_cancel(), the operation is cancelled without callback invocation.
In case the re-authentication is rejected by the broker, the latter is expected to send the DISCONNECT message resulting in the Unsolicited Broker Disconnection. In such case the completion callback will be invoked with the CC_Mqtt5AsyncOpStatus_BrokerDisconnected status.
In many use cases the "reauth" operation can be quite simple with a lot of defaults. To simplify the sequence of the operation preparation and handling of errors, the library provides wrapper function(s) that can be used:
For example:
Note that the wrapper functions do NOT expose the handle returned by the cc_mqtt5_client_reauth_prepare(). It means that it's not possible to cancel the "reauth" operation before its completion.
When broker disconnection is detected all the incomplete asynchronous operations (except publish) will be terminated with the an appropriate status report. The incomplete publish operations may be preserved (depending on "Session Expiry Interval" configuration during the connect operation) due to the following spec clause:
The unsolicited broker disconnection can happen in one of the following scenarios:
The broker can initiate disconnection any time by simply sending DISCONNECT message. In such case the library responds in the following way:
When there was no message from the broker for the "keep alive" timeout (configured during the connect operation) and the broker doesn't respond to the PING message. In such case the library responds in the following way:
In case the broker doesn't fully comply with the MQTT v5 specification or there is some unexpected data corruption the library responds in the following way:
With all said above it means that if application receives CC_Mqtt5AsyncOpStatus_ProtocolError or CC_Mqtt5AsyncOpStatus_BrokerDisconnected status in the operation callback, then the application is expected to wait for the disconnection report callback which will follow.
When the disconnection report callback is called the application is expected to go through the following steps:
In case the "Session Expiry Interval" was set during the connect operation, the library may still require timer measurement, depending on whether there are incomplete publish operations and/or incomplete message reception that need to be preserved for some time until the session expires or client re-connects to the broker. In case the "Session Expiry Interval" is not set or when the session is actually expires (based on the reported ticks), then all the incomplete publish operations will also get terminated with the CC_Mqtt5AsyncOpStatus_BrokerDisconnected status and the incomplete message reception will be discarded.
In case the client re-connects to the broker before the "Session Expiry Interval" expires and the broker reports "clean session", then all the incomplete publish operations will be terminated with the CC_Mqtt5AsyncOpStatus_Aborted status.
In addition to the Unsolicited Broker Disconnection the network connection can be suddenly terminated. Such network disconnection is usually detected by the failing read or write operations on I/O socket. When the application detects such network disconnection, it is expected to report it to the library using cc_mqtt5_client_notify_network_disconnected() function.
When the network disconnection is reported, the similar workflow to one described in the Unsolicited Broker Disconnection section above is performed, excluding the invocation of the broker disconnection report callback. When the incomplete operations get terminated the CC_Mqtt5AsyncOpStatus_BrokerDisconnected status is reported.
When the Unsolicited Broker Disconnection is detected prior to the detection of the network disconnection itself and the broker disconnection report callback is invoked, there is no need to report the network disconnection to the library.
Inquiry about current network disconnection status can be done using the cc_mqtt5_client_is_network_disconnected() function.
Note that when the new "connect" op is prepared, the library will assume that the network connection is re-established and the subsequent call to the cc_mqtt5_client_is_network_disconnected() function will return false.
In general the library is NOT thread safe. To support multi-threading the application is expected to use appropriate locking mechanisms before calling relevant API functions. However, if each thread operates on a separate allocated client, then the locking is required only for the client allocation logic.
The client library uses COMMS_ASSERT() macro to check some internal pre- and post-conditions. It is a bug if the assertion fails, please report if encountered. By default it is like standard assert() but allows overriding the default failure behavour, which can be more suitable for bare-metal systems. Please refer to the documentation for details.