-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathChapter080-Advanced-MQTT.html
446 lines (330 loc) · 29.5 KB
/
Chapter080-Advanced-MQTT.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
<section data-type="chapter" id="ch_advanced_mqtt" xmlns="http://www.w3.org/1999/xhtml">
<h1>Advanced MQTT</h1>
<p class="lead">In Chapters 6 and 7, we used MQTT to send and receive messages from a native iOS application and a web application. MQTT provides additional features that we did not use to write these applications.<a contenteditable="false" data-primary="MQTT" data-secondary="advanced" data-type="indexterm" id="MQTTadv"> </a> In this chapter, we will take a tour of all these advanced features provided by MQTT.</p>
<p>This chapter covers the latest version of the protocol at the time of this writing (i.e., <a href="http://bit.ly/mqtt-spec">MQTT v3.1</a>, which was released on August 19, 2010).</p>
<section data-type="sect1" id="_authentication_2">
<h1>Authentication</h1>
<p>In Chapters 6 and 7, we connected to the Eclipse <a contenteditable="false" data-primary="MQTT" data-secondary="advanced" data-tertiary="authentication" data-type="indexterm"> </a>public MQTT broker that accepts <em>unauthenticated</em> connections. <a contenteditable="false" data-primary="authentication" data-secondary="in MQTT" data-type="indexterm"> </a>We did not need to pass any user credentials, as they would not be checked by the broker anyway.<a contenteditable="false" data-primary="brokers" data-secondary="secured connections, MQTT brokers accepting" data-type="indexterm"> </a></p>
<p>If you are using an MQTT broker that is configured to accept secured connections, the client needs to pass a <em>username</em> and <em>password</em> when it connects to the broker.</p>
<section data-type="sect2" id="__code_mqttkit_code_example">
<h2>MQTTKit Example</h2>
<p>The <code>MQTTClient</code> has two <code>NSString</code> properties that must be set to authenticate the client, <code>username</code>, and <code>password</code>. <a contenteditable="false" data-primary="MQTTKit" data-secondary="authentication example" data-type="indexterm"> </a>They must be set <em>prior</em> to calling the client’s <code>connect</code> methods in order to take effect.</p>
<p>If the MQTT broker requires authentication, the client can check if the connection was refused due to invalid user credentials using the <code>ConnectionRefusedBadUserNameOrPassword</code> error code from the <code>completionHandler</code>:</p>
<pre data-code-language="objc" data-type="programlisting">
- (void)connect
{
NSString *username = @"...";
NSString *password = @"...";
self.client.username = username;
self.client.password = password;
NSLog(@"Connecting to %@...", kMqttHost);
[self.client connectToHost:kMqttHost
completionHandler:^(MQTTConnectionReturnCode code) {
if (code == ConnectionAccepted) {
NSLog(@"connected to the MQTT broker");
[self subscribe];
} else if (code == ConnectionRefusedBadUserNameOrPassword) {
NSLog(@"Failed to authenticate the user");
} else {
NSLog(@"Failed to connect to the MQTT broker: code=%lu",
(unsigned long)code);
}
}];
}</pre>
</section>
<section data-type="sect2" id="__code_mqttws31_js_code_example">
<h2>mqttws31.js Example</h2>
<p>To authenticate using the <code>mqttws31.js</code> library, you must set the <code>userName</code> and <code>password</code> properties to <a contenteditable="false" data-primary="mqttws31.js" data-secondary="authentication example" data-type="indexterm"> </a>the object passed to the client’s <code>connect</code> method.</p>
<p>If the MQTT broker requires authentication and the client passes invalid user credentials, the client will be notified by having its <code>onFailure</code> handler called with a failur code set to <code>4</code> (the value specified in the MQTT protocol for the error <code>Connection Refused: bad user name or password</code>):</p>
<pre data-code-language="js" data-type="programlisting">
var userName = "...";
var password = "...";
client.connect({onSuccess: function(frame) {
...
},
onFailure: function(failure) {
if (failure.code === 4) {
alert("invalid user credentials");
return;
}
...
},
userName: userName;
password: password;
});</pre>
</section>
</section>
<section data-type="sect1" id="_error_handling_2">
<h1>Error Handling</h1>
<p>The MQTT protocol does not specify any error handling.<a contenteditable="false" data-primary="error handling" data-secondary="in MQTT" data-type="indexterm"> </a> <a contenteditable="false" data-primary="MQTT" data-secondary="advanced" data-tertiary="error handling" data-type="indexterm"> </a>An MQTT broker has no possible way to inform a client that an error occured. The only action that the MQTT broker can take is to close the underlying network connection so that the client is no longer connected to the broker.</p>
<p>MQTT libraries provide callbacks or handlers for these cases so that they can act when such an error occurs.</p>
<section data-type="sect2" id="__code_mqttkit_code_example_2">
<h2>MQTTKit Example</h2>
<p>The <code>MQTTClient</code> class has a <code>disconnectionHandler</code> property that can be set to handle any unexpected error leading to a disconnection.<a contenteditable="false" data-primary="error handling" data-secondary="in MQTT" data-tertiary="MQTTKit example" data-type="indexterm"> </a><a contenteditable="false" data-primary="MQTTKit" data-secondary="error handling example" data-type="indexterm"> </a> The disconnection can be the consequence of the MQTT broker closing the network exception (in case of abnormal errors or an administrative operation) or the network connection can be directly broken (e.g., if a mobile device is no longer able to receive any signal).</p>
<p>The <code>disconnectionHandler</code> is a block that takes an <code>NSUinteger code</code> parameter. There are no standard values for the code and you will have to consult your MQTT broker documentation if you need to act differently depending on the type of errors.</p>
<p>We can modify the <code>Motions</code> iOS application to handle such disconnection failures and aggressively try to reconnect the MQTT broker (assuming the root cause of the disconnection failures are transient):</p>
<pre data-code-language="objc" data-type="programlisting">
- (void)viewDidLoad
{
[super viewDidLoad];
...
self.client = [[MQTTClient alloc] initWithClientId:self.deviceID];
// use a weak reference to avoid a retain/release cycle in the block
__weak MWMViewController *weakSelf = self;
self.client.disconnectionHandler = ^(NSUInteger code) {
NSLog(@"client disconnected with code %lu", (unsigned long)code);
NSLog(@"trying to reconnect...");
// trying to reconnect
[weakSelf connect];
};
...
[self connect];
}</pre>
</section>
<section data-type="sect2" id="__code_mqttws31_js_code_example_2">
<h2>mqttws31.js Example</h2>
<p>In <a data-type="xref" href="#ch_mobile_mqtt_connect">#ch_mobile_mqtt_connect</a>, we already set an <code>onConnectionLost</code> callback on the <code>client</code> to be notified in case of a connection error.<a contenteditable="false" data-primary="error handling" data-secondary="in MQTT" data-tertiary="mqqtws31.js example" data-type="indexterm"> </a><a contenteditable="false" data-primary="mqttws31.js" data-secondary="error handling example" data-type="indexterm"> </a> The callback has a single <code>response</code> parameter object. This parameter is composed of two properties:</p>
<ul>
<li>
<p><code>errorCode</code>, a numerical representation of the type of connection error</p>
</li>
<li>
<p><code>errorMessage</code>, a textual description of the error</p>
</li>
</ul>
<p>We can modify the <code>Motions</code> web application to display an alert message when a connection loss is detected to inform the user:</p>
<pre data-code-language="js" data-type="programlisting">
client.onConnectionLost = function(response) {
alert(response.errorMessage + "\nclientID = " + client.clientID + " [" +
response.errorCode + "]\n");
};</pre>
</section>
</section>
<section data-type="sect1" id="_heart_beating_2">
<h1>Heart-Beating</h1>
<p>MQTT offers a simple mechanism to test the health of the network connection between a client and a broker<a contenteditable="false" data-primary="MQTT" data-secondary="advanced" data-tertiary="heart-beating" data-type="indexterm"> </a> using heart-beating.<a contenteditable="false" data-primary="heart-beating" data-secondary="in MQTT" data-type="indexterm"> </a></p>
<p>Heart-beating is enabled by specifying a <em>keep alive timer</em> when the client initially connects to the broker.<a contenteditable="false" data-primary="keep alive timer" data-type="indexterm"> </a> This timer, measured in seconds, defines the maximum time interval between messages received from a client. It allows the client and broker to detect whether the network connection is broken without waiting for the long TCP/IP timeout. A timer value of <code>0</code> disables heart-beating.</p>
<p>In the absence of regular messages exchanged between them, the client and the broker automatically send respective heart-beats (<code>PINGREQ</code> for the client and <code>PINGRESP</code> for the broker) based on the keep alive timer to check the health of the network connection.</p>
<p>If the client does not receive heartbeats from the broker, it will close the underlying network connection and report an error.</p>
<p>If the broker does not receive heartbeats from the client, it will consider the client to be disconnected.</p>
<div data-type="caution">
<p>Setting a good value for the keep alive timer is highly dependent on the application use cases and the platform it runs on.</p>
<p>For mobile devices that are subject to frequent intermittent network failures, using a value too small will report false failures and increase the instability of the application. It will also increase the bandwidth and battery usage as heartbeats would have to be sent over the network more frequently.</p>
</div>
<section data-type="sect2" id="__code_mqttkit_code_example_3">
<h2>MQTTKit Example</h2>
<p>By default, MQTTKit defines a keep-alive timer of 60 seconds.<a contenteditable="false" data-primary="heart-beating" data-secondary="in MQTT" data-tertiary="MQTTKit example" data-type="indexterm"> </a><a contenteditable="false" data-primary="MQTTKit" data-secondary="heart-beating example" data-type="indexterm"> </a></p>
<p>It is possible to change this value using the <code>keepAlive</code> property on the <code>MQTTClient</code> object. The property type is a <code>short</code> and its value must be changed prior to calling the client’s <code>connect</code> method in order to take effect:</p>
<pre data-code-language="objc" data-type="programlisting">
MQTTClient *client = [[MQTTClient alloc] initWithClientId:clientID];
client.keepAlive = 10; // seconds
[client connectToHost:host
completionHandler:^(MQTTConnectionReturnCode code) {
//...
}];</pre>
</section>
<section data-type="sect2" id="__code_mqttws31_js_code_example_3">
<h2>mqttws31.js Example</h2>
<p><code>mqttws31.js</code> also defines a keep-alive timer of 60 seconds by default.<a contenteditable="false" data-primary="heart-beating" data-secondary="in MQTT" data-tertiary="mqttws31.js example" data-type="indexterm"> </a><a contenteditable="false" data-primary="mqttws31.js" data-secondary="heart-beating example" data-type="indexterm"> </a></p>
<p>The <code>client</code>'s <code>connect</code> method can take an optional <code>keepAliveInterval</code> integer to specify another value (or <code>0</code> to disable heart-beating):</p>
<pre data-code-language="js" data-type="programlisting">
client.connect({onSuccess: function(frame) {
...
},
onFailure: function(failure) {
...
},
keepAliveInterval: 10 // seconds
});</pre>
</section>
</section>
<section data-type="sect1" id="_last_will">
<h1>Last Will</h1>
<p>One strength of messaging protocols is that producers and consumers are loosely coupled.<a contenteditable="false" data-primary="MQTT" data-secondary="advanced" data-tertiary="last will" data-type="indexterm"> </a> They do not have to be online at the same time to exchange messages. The producer can send a message to a destination and be terminated. The messaging broker will then deliver the message to a consumer when it subscribes to this destination.</p>
<p>However, there are cases in which an application may require more information on the liveness of messaging clients.</p>
<p>Let’s take the example of the Motions application that broadcasts the device position when it moves. A consumer of the device position topic will consume these messages. However, how could the consumer distinguish between receiving the messages because the device does not move or because the device is offline and has stopped broadcasting its position?</p>
<p>If the device is offline, the consumer may want to be notified to discard the device position from the map or show it differently from other <em>live</em> devices.</p>
<p>MQTT provides a <em>last will</em> feature that we could use to handle this use case.<a contenteditable="false" data-primary="last will (MQTT)" data-type="indexterm"> </a></p>
<p>When an MQTT client connects to the broker, it can specify a last will message that will be published to a last will topic by the broker <em>on behalf</em> of the client in case of unexpected disconnection. If the client disconnects normally, its last will message is not published. If the client uses heart-beating and the broker fails to receive its heartbeat in a timely fashion, this is considered an unexpected disconnection and the last will message will be published.</p>
<p>We could use this last will to let consumers know that the Motions iOS application has been terminated abnormally or its device is no longer reachable (in case of network disconnection).</p>
<section data-type="sect2" id="__code_mqttkit_code_example_4">
<h2>MQTTKit Example</h2>
<p>The <code>STOMPClient</code> object has <code>setWill:toTopic:withQos:retain</code> and <code>setWillData:toTopic:withQos:retain</code> methods to specify the client’s last will. <a contenteditable="false" data-primary="MQTTKit" data-secondary="last will example" data-type="indexterm"> </a><a contenteditable="false" data-primary="last will (MQTT)" data-secondary="MQTTKit example" data-type="indexterm"> </a>The difference between the two methods is that the first one takes an <code>NSString</code> for the will message payload and the second takes an <code>NSData</code>. These methods must be called before the client connects to the MQTT broker to take effect.</p>
<p>We could improve the <code>Motions</code> iOS application by specifying a last will to its <code>client</code> object in <em>MWMViewController.m</em> before it connects.</p>
<p>The last will topic can be any MQTT topic. We will use the <code>/mwm/lastWill</code> topic so that a consumer would have to subscribe to this topic to be notified of any device’s abnormal disconnection. The payload of the last will message is a simple JSON object with a <code>deviceID</code> property. We will encapsulate the setup of the last will in a <code>setLastWill</code> method:</p>
<pre data-code-language="objc" data-type="programlisting">
- (void)setLastWill
{
NSString *willTopic = @"/mwm/lastWill";
NSDictionary *dict = @{ @"deviceID": self.deviceID};
NSData *willData = [NSJSONSerialization dataWithJSONObject:dict
options:0
error:nil];
[self.client setWillData:willData
toTopic:willTopic
withQos:ExactlyOnce
retain:NO];
}
</pre>
<p>We just need to call this method before connecting to the MQTT broker in <code>connect</code>:</p>
<pre data-code-language="objc" data-type="programlisting">
- (void)connect
{
[self setLastWill];
NSLog(@"Connecting to %@...", kMqttHost);
[self.client connectToHost:kMqttHost
completionHandler:^(MQTTConnectionReturnCode code) {
...
}];
}</pre>
<p>Similar to a regular message, the last will message can specify its QoS and whether it must be retained. A last will message might be important but infrequent. Using a QoS of <code>Exactly Once</code> will ensure that a consumer of the last will topic will not receive false positives on the device’s disconnection. We will also not retain the last will message. If it were retained, a newly subscribed consumer could receive it and assume that a device has been disconnecting while it reconnected in the meantime.</p>
<p>Before we configure the web application’s own last will, we can first update it to discard data when it receives the last will message from a device.</p>
<p>To achieve this, we need to do the following:</p>
<ol>
<li>
<p>Subscribe to the last will topic <code>/mwm/lastWill</code>.</p>
</li>
<li>
<p>Update the subscription callback to handle last will messages.</p>
</li>
</ol>
<p>The first step is done in the <code>onSuccess</code> callback passed to <code>client</code>s <code>connect</code> method when we were already subscribing to the devices, motion topics:</p>
<pre data-code-language="js" data-type="programlisting">
var lastWillTopic = "/mwm/lastWill";
client.connect({onSuccess: function(frame) {
// after the client is successfully connected,
// subscribe to all the motions topics
client.subscribe("/mwm/+/motion");
// subscribe to the last will topic, too
client.subscribe(lastWillTopic);
},</pre>
<p>The second step requires us to modify the <code>client</code>'s <code>onMessageArrived</code> callback to check whether the message is coming from the last will topic and discard the device data if that the case. Because the last will message representation is a JSON object, we must first parse it by calling <code>JSON.parse</code> on the message’s <code>payloadString</code>:</p>
<pre data-code-language="js" data-type="programlisting">
client.onMessageArrived = function(message) {
if (message.destinationName === lastWillTopic) {
var payload = JSON.parse(message.payloadString);
discard(payload.deviceID);
return;
}
// the rest of the function is unchanged
...
};</pre>
<p>The <code>discard</code> function will delete the data from the <code>device</code>'s dictionary and remove the HTML elements that were created to display the device:</p>
<pre data-code-language="js" data-type="programlisting">
function discard(deviceID) {
console.log("discard data for " + deviceID);
delete devices[deviceID];
$('#'+ deviceID).remove();
}</pre>
</section>
<section data-type="sect2" id="__code_mqttws31_js_code_example_4">
<h2>mqttws31.js Example</h2>
<p>It is also possible to set a client’s last will using <code>mqttws31.js</code>. <a contenteditable="false" data-primary="last will (MQTT)" data-secondary="mqttws31.js example" data-type="indexterm"> </a><a contenteditable="false" data-primary="mqttws31.js" data-secondary="last will example" data-type="indexterm"> </a>The <code>client</code>'s <code>connect</code> method can take an optional <code>willMessage</code> object that represents the last will message to send if it disconnects unexpectedly. The value is a regular MQTT message created by calling a <code>new Messaging.Message</code> constructor and specifying its <code>destinationName</code> (the last will topic), and optionally its <code>qos</code> and <code>retained</code> value:</p>
<pre data-code-language="js" data-type="programlisting">
var willMessage = new Messaging.Message("Web client " + clientID +
"has unexpectedly died");
willMessage.destinationName = "/mwm/lastWill/web";
willMessage.qos = 2; // exactly once
willMessage.retained = false;
// specify the last will when the client connects to the broker
client.connect({onSuccess: function(frame) {
...
},
onFailure: function(failure) {
...
},
willMessage: willMessage
});</pre>
<p>Often, applications may not need to be notified of the last will of another MQTT client. However, we may still want to monitor the unexpected disconnection to be informed of the liveness of the whole system. If all MQTT clients have configured their last will, we can have a crude monitoring application by subscribing to their last will topics:</p>
<pre data-code-language="bash" data-type="programlisting">
$ mosquitto_sub -h iot.eclipse.org -t /mwm/lastWill/# -v
...
/mwm/lastWill {"deviceID":"C0962483-7DD9-43CC-B1A0-2E7FBFC05060"}
/mwm/lastWill/web Web client 0.90778b769105b876 has unexpectedly died</pre>
<div data-type="note">
<p>We subscribed to the wildcard topic <code>/mwm/lastWill/#</code> to receive messages from both <code>/mwm/lastWill</code> (that is used by the Motions iOS application) and any of its children including <code>/mwmw/lastWill/web</code> (that is used by the Motions web application).</p>
</div>
</section>
</section>
<section data-type="sect1" id="_clean_session">
<h1>Clean Session</h1>
<p>When an MQTT client connects to the broker, it can specify whether the broker must store its state after it disconnects and until it reconnects.<a contenteditable="false" data-primary="MQTT" data-secondary="advanced" data-tertiary="clean session" data-type="indexterm"> </a><a contenteditable="false" data-primary="clean session (MQTT)" data-type="indexterm"> </a> The client state that is stored includes its subscriptions and any in-flight message with a QoS greater or equal to <code>1</code>. Messages with a QoS of <code>0</code> (<code>AtMostOnce</code>) are not stored, because they are delivered on a best effort basis.<a contenteditable="false" data-primary="Quality of Service (QoS)" data-secondary="for stored messages" data-type="indexterm"> </a></p>
<p>The client uses a Clean Session flag for this. If the flag is set, the broker will not store any state and the connection opened by the client will be <em>clean</em>. If the flag is not set, the broker will store the client state.</p>
<p>A client with the Clean Session flag set will have to subscribe again to consume messages.</p>
<p>A client that does not set the Clean Session flag will consume memory on the broker side (to store its state) and the broker might also perform administrative operations to remove such state. Unless there is a strong incentive to use such a client, it is better practice to use a Clean Session client and subscribe again after it reconnects.</p>
<section data-type="sect2" id="__code_mqttkit_code_example_5">
<h2>MQTTKit Example</h2>
<p>By default, MQTT clients created using MQTTKit have the Clean Session flag set (their state is not stored by the broker after they disconnect).<a contenteditable="false" data-primary="MQTTKit" data-secondary="clean session example" data-type="indexterm"> </a><a contenteditable="false" data-primary="clean session (MQTT)" data-secondary="MQTTKit example" data-type="indexterm"> </a> It is also possible to change this behavior by using the <code>MQTTClient</code>'s <code>initWithClientID:cleanSession:</code> initializer and passing <code>NO</code> to its <code>cleanSession</code> parameter:</p>
<pre data-code-language="objc" data-type="programlisting">
- (void)viewDidLoad
{
[super viewDidLoad];
...
// do not clean the session in the broker when the client disconnects
self.client = [[MQTTClient alloc] initWithClientID:self.deviceID
cleanSession:NO];
...
[self connect];
}</pre>
<p>If the Motions iOS application is modified this way, we can test it by connecting to the broker (so that the broker knows that it must store its state) and closing the application.</p>
<p>While the application is closed, we will modify the <em>motions.html</em> web application to send an alert message to the device alert topic with a QoS of <code>1</code> (<code>At Least Once</code>):</p>
<pre data-code-language="js" data-type="programlisting">
function sendAlert(deviceID) {
var message = new Messaging.Message("red");
message.destinationName = "/mwm/" + deviceID + "/alert";
// send the alert with a QoS of At Least Once
message.qos = 1;
client.send(message);
}</pre>
<p>The client will not be available to receive the message so the broker must store it to deliver when the client reconnects.</p>
<p>If we open the <code>Motions</code> iOS application again, the broker will then deliver the message to the client.</p>
</section>
<section data-type="sect2" id="__code_mqttws31_js_code_example_5">
<h2>mqttws31.js Example</h2>
<p>The clients created by the mqttws31.js library also connect by default with the Clean Session flag set. <a contenteditable="false" data-primary="mqttws31.js" data-secondary="clean session example" data-type="indexterm"> </a><a contenteditable="false" data-primary="clean session (MQTT)" data-secondary="mqttws31.js example" data-type="indexterm"> </a>It is possible to change this behavior by adding a <code>cleanSession</code> property set to <code>false</code> in the properties passed to the client’s <code>connect</code> method:</p>
<pre data-code-language="js" data-type="programlisting">
// specify that the session must not be cleaned when the client connects to
// the broker
client.connect({onSuccess: function(frame) {
...
},
onFailure: function(failure) {
...
},
cleanSession: false
});</pre>
</section>
</section>
<section data-type="sect1" id="_beyond_mqtt">
<h1>Beyond MQTT?</h1>
<p>MQTT is a simple protocol well-suited to a limited set of applications that can be modeled using the publish/subscribe model.<a contenteditable="false" data-primary="MQTT" data-secondary="limitations of" data-type="indexterm"> </a></p>
<p>Its small set of features makes it simple to understand and use, but it lacks some flexibility and you have to carefully consider whether it meets your requirements.<a contenteditable="false" data-primary="headers" data-secondary="absence of, in MQTT messages" data-type="indexterm"> </a></p>
<p>One missing feature of MQTT is the lack of headers in the message representation.</p>
<p>MQTT defines a fixed set of headers for its command messages (used to connect to the broker, send a message, create a subscription, etc.), but there is no general notion of a header that the application or the broker could add to the messages.</p>
<p>This impacts the expressiveness of the messages that are delivered using MQTT. As a simple example, the absence of headers means that there is no way to know what type of data to expect from the payload. HTTP and STOMP define a <code>content-type</code> header that can be queried to know the MIME type of the payload and extract it accordingly. MQTT does not allow this and the consumer must have <em>a priori</em> knowledge of the payload type for a message before being able to read it.</p>
<p>The absence of headers also implies that the MQTT broker cannot provide additional features not covered by the protocol in an unobtrusive way. As we saw in <a data-type="xref" href="#ch_beyond_stomp">#ch_beyond_stomp</a>, STOMP brokers can provide additional features such as persistence, priority, and expiration, and the client can use them by adding headers to the messages. There is no such mechanism for MQTT in its current version. There are some ongoing discussions to add application-specific headers in a future version of the protocol, but no agreement has been reached at the time of this writing.</p>
<p>If you require features that are not provided by the protocol (or another messaging model than <em>publish/subscribe</em>), you may lose all the benefits of using this simple protocol and write brittle code that puts all the complexity in your applications instead of relying on the broker’s set of features. But if your requirements are fullfilled by MQTT features (as is the case for a lot of applications), you will be all set to use messaging in your applications.</p>
</section>
<section data-type="sect1" id="_summary_8">
<h1>Summary</h1>
<p>MQTT is a simple protocol that provides few advanced features. However, these features can be handy to solve common issues encountered by messaging applications.</p>
<p>In this chapter, we learned to use:</p>
<ul>
<li>
<p><em>Authentication</em> to ensure that only authenticated clients can communicate with the MQTT broker</p>
</li>
<li>
<p><em>Error handling</em> to face unexpected connection issues and eventually reconnect to the broker</p>
</li>
<li>
<p><em>Heart-beating</em> to ensure that the network connection between the client and broker is healthy (and to ensure that it will kill the connection if that is not the case)</p>
</li>
<li>
<p><em>Last will</em> to let the broker sent a message on behalf of the client in case of an unexpected disconnection</p>
</li>
<li>
<p>Clean Session to preserve client state on the broker between connections<a contenteditable="false" data-primary="MQTT" data-secondary="advanced" data-startref="MQTTadv" data-type="indexterm"> </a></p>
</li>
</ul>
</section>
</section>