Working with exactly once delivery
First, we will use wildcards to subscribe to a topic filter with QoS level 2 and then we will publish one messages to a topic that will match the topic filter with QoS level 2. This way, we will analyze how both publishing and subscription work with QoS level 2.
We will use the mosquitto_sub
command-line utility included in Mosquitto to generate a simple MQTT client that subscribes to a topic filter with QoS level 1 and prints all the messages it receives. Open a Terminal in macOS or Linux, or a Command Prompt in Windows, go to the directory in which Mosquitto is installed and run the following command:
mosquitto_sub -V mqttv311 -t sensors/drone03/# -q 2 -d
The previous command will create an MQTT client that will establish a connection with the local MQTT server and then will make the client subscribe to the topic filter specified after the -t
option: sensors/drone03/#
. We specify that we want to use QoS level 1 to subscribe to the topic filter with the -q 2
option. We specify the -d
option to enable debug messages that will allow us to understand what happens under the hoods and the differences with publishing a message with QoS levels 0 and 1.
The Terminal or Command Prompt window will display debug messages similar to the following lines. Take into account that the generated ClientId
will be different from the one shown after Client mosqsub/1795-Gastons-Ma
. Notice that QoS: 2
indicates that the subscription is done with QoS level 2.
Client mosqsub/2017-Gastons-Ma sending CONNECT
Client mosqsub/2017-Gastons-Ma received CONNACK
Client mosqsub/2017-Gastons-Ma sending SUBSCRIBE (Mid: 1, Topic: sensors/drone03/#, QoS: 2)
Client mosqsub/2017-Gastons-Ma received SUBACK
Subscribed (mid: 1): 2
We will use the mosquitto_pub
command-line utility included in Mosquitto to generate a simple MQTT client that publishes a message to a topic with QoS level 2 instead of the QoS levels 0 and 1 that we used when we published messages before. Open a Terminal in macOS or Linux, or a Command Prompt in Windows, go to the directory in which Mosquitto is installed and run the following command:
mosquitto_pub -V mqttv311 -t sensors/drone03/speed/rotor/1 -m "362 f" -q 2 -d
The previous command will create an MQTT client that will establish a connection with the local MQTT server and then will make the client publish a message to the topic specified after the -t
option: sensors/drone03/speed/rotor/1
. We specify the payload for the message after the -m
option: "362 f"
. We specify that we want to use QoS level 2 to publish the message with the -q 2
option. We specify the -d
option to enable debug messages that will allow us to understand what happens under the hoods and the differences with publishing a message with QoS levels 0 and 1.
The Terminal or Command Prompt window will display debug messages similar to the following lines. Take into account that the generated ClientId
will be different from the one shown after Client
: mosqpub/2026-Gastons-Ma
. After publishing the message, the client disconnects.
Client mosqpub/2026-Gastons-Ma sending CONNECT
Client mosqpub/2026-Gastons-Ma received CONNACK
Client mosqpub/2026-Gastons-Ma sending PUBLISH (d0, q2, r0, m1, 'sensors/drone03/speed/rotor/1', ... (5 bytes))
Client mosqpub/2026-Gastons-Ma received PUBREC (Mid: 1)
Client mosqpub/2026-Gastons-Ma sending PUBREL (Mid: 1)
Client mosqpub/2026-Gastons-Ma received PUBCOMP (Mid: 1)
Client mosqpub/2026-Gastons-Ma sending DISCONNECT
The previous lines show that the generated MQTT client, that is, the publisher, had the following packet exchanges with the MQTT server:
- The publisher sent a
PUBLISH
packet to the MQTT server. - The publisher received a
PUBREC
packet from the MQTT server. - The publisher sent a
PUBREL
packet to the MQTT server. - The publisher received a
PUBCOMP
packet from the MQTT server.
Now, go back to the Terminal or Command Prompt window in which you executed the mosquitto_sub
command and subscribed to the sensors/drone03/#
topic filter. You will see lines similar to the following ones:
Client mosqsub/2017-Gastons-Ma received PUBLISH (d0, q2, r0, m1, 'sensors/drone03/speed/rotor/1', ... (5 bytes))
Client mosqsub/2017-Gastons-Ma sending PUBREC (Mid: 1)
Client mosqsub/2017-Gastons-Ma received PUBREL (Mid: 1)
362 f
Client mosqsub/2017-Gastons-Ma sending PUBCOMP (Mid: 1)
The previous lines show that the generated MQTT client, that is, the subscriber, had the following packet exchanges with the MQTT server:
- The subscriber received a
PUBLISH
packet from the MQTT server. - The subscriber sent a
PUBREC
packet to the MQTT server. - The subscriber received a
PUBREL
packet from the MQTT server. - The subscriber sends a
PUBCOMP
packet to the MQTT server after it successfully received the message with the payload.
If we clean up the debug messages that start with the Client prefix, we will see just the last line that shows the payloads for the message that we received as a result of our subscription to the sensors/drone03/#
topic filter: 362 f
.
The MQTT client that has already established a connection, that is, the publisher sends a PUBLISH
packet to the MQTT server with the header we have already described, QoS
set to 2
, and including a PacketId
numeric value that will be unique for this client. At this time, the publisher will consider the PUBLISH
packet identified with the PacketId
as an unacknowledged PUBLISH
packet.
The MQTT server reads a valid PUBLISH
packet and it will respond to the publisher with a PUBREC
packet with the same PacketId
value that has been used for the PUBLISH
packet. The PUBREC
packet indicates the MQTT server accepted the ownership of the message. Once the publisher receives the PUBREC
packet, it discards the message and it will store the PacketId
related to the message and the PUBREC
packet.
The publisher sends a PUBREL
packet to the MQTT server as a response to the received PUBREC
packet. This PUBREL
packet will be considered unacknowledged until it receives the PUBCOMP
packet related to the PacketId
from the MQTT server. Finally, the MQTT server sends a PUBCOMP
packet with the PacketId
to the publisher and at this point, both the publisher and the MQTT server are sure that the message has been successfully delivered.
The following diagram shows the interaction between a publisher and an MQTT server to publish a message with a QoS level of 2.
For each subscriber with QoS level 2 to which the message has to be published, the MQTT server will send a PUBLISH
packet and the same packet exchange that we have analyzed between the publisher and the MQTT server will happen between the MQTT server and the subscriber. However, in this case, the MQTT server is the one that acts as a publisher and starts the flow. The following diagram shows the interaction between an MQTT server and the subscribers when a message is published with a QoS level of 2.
If the application isn't able to tolerate duplicates and we have to make sure that the messages arrive only once to the subscribers, QoS level 2 is the appropriate choice. However, magic comes with a price: we must take into account that QoS level 2 has the highest overhead, compared to the other QoS levels.