1 /**
2 * Definition of MQTT protocol messages
3 *
4 * Author:
5 * Tomáš Chaloupka <chalucha@gmail.com>
6 *
7 * License:
8 * Boost Software License 1.0 (BSL-1.0)
9 *
10 * Permission is hereby granted, free of charge, to any person or organization obtaining a copy
11 * of the software and accompanying documentation covered by this license (the "Software") to use,
12 * reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative
13 * works of the Software, and to permit third-parties to whom the Software is furnished to do so,
14 * all subject to the following:
15 *
16 * The copyright notices in the Software and this entire statement, including the above license
17 * grant, this restriction and the following disclaimer, must be included in all copies of the Software,
18 * in whole or in part, and all derivative works of the Software, unless such copies or derivative works
19 * are solely in the form of machine-executable object code generated by a source language processor.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
22 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
23 * PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
24 * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT,
25 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 * OTHER DEALINGS IN THE SOFTWARE.
27 */28 modulemqttd.messages;
29 30 importstd.range;
31 importstd.exception : enforce;
32 importstd.traits : isIntegral;
33 importstd.typecons : Nullable;
34 debugimportstd.stdio;
35 36 importmqttd.traits;
37 38 @safe:
39 40 /**
41 * Exception thrown when package format is somehow malformed
42 */43 finalclassPacketFormatException : Exception44 {
45 this(stringmsg = null, Throwablenext = null)
46 {
47 super(msg, next);
48 }
49 }
50 51 enumubyteMQTT_PROTOCOL_LEVEL_3_1_1 = 0x04;
52 enumstringMQTT_PROTOCOL_NAME = "MQTT";
53 54 /**
55 * MQTT Control Packet type
56 *
57 * http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Table_2.1_-
58 */59 enumPacketType : ubyte60 {
61 /// Forbidden - Reserved62 RESERVED1 = 0,
63 /// Client -> Server - Client request to connect to Server64 CONNECT = 1,
65 /// Server -> Client - Connect acknowledgment66 CONNACK = 2,
67 /// Publish message68 PUBLISH = 3,
69 /// Publish acknowledgment70 PUBACK = 4,
71 /// Publish received (assured delivery part 1)72 PUBREC = 5,
73 /// Publish release (assured delivery part 2)74 PUBREL = 6,
75 /// Publish complete (assured delivery part 3)76 PUBCOMP = 7,
77 /// Client -> Server - Client subscribe request78 SUBSCRIBE = 8,
79 /// Server -> Client - Subscribe acknowledgment80 SUBACK = 9,
81 /// Client -> Server - Unsubscribe request82 UNSUBSCRIBE = 10,
83 /// Server -> Client - Unsubscribe acknowledgment84 UNSUBACK = 11,
85 /// Client -> Server - PING request86 PINGREQ = 12,
87 /// Server -> Client - PING response88 PINGRESP = 13,
89 /// Client -> Server - Client is disconnecting90 DISCONNECT = 14,
91 /// Forbidden - Reserved92 RESERVED2 = 1593 }
94 95 /**
96 * Indicates the level of assurance for delivery of an Application Message
97 * http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Table_3.11_-
98 */99 enumQoSLevel : ubyte100 {
101 /// At most once delivery102 QoS0 = 0x00,
103 /// At least once delivery104 QoS1 = 0x01,
105 /// Exactly once delivery106 QoS2 = 0x02,
107 /// Reserved – must not be used108 Reserved = 0x03,
109 /// Failure - used in SubAck packet only110 Failure = 0x80111 }
112 113 /// Connect Return code values - 0 = accepted, the rest means refused (6-255 are reserved)114 enumConnectReturnCode : ubyte115 {
116 /// Connection accepted117 ConnectionAccepted = 0x00,
118 /// The Server does not support the level of the MQTT protocol requested by the Client119 ProtocolVersion = 0x01,
120 /// The Client identifier is correct UTF-8 but not allowed by the Server121 Identifier = 0x02,
122 /// The Network Connection has been made but the MQTT service is unavailable123 ServerUnavailable = 0x03,
124 /// The data in the user name or password is malformed125 UserNameOrPassword = 0x04,
126 /// The Client is not authorized to connect127 NotAuthorized = 0x05128 }
129 130 /**
131 * Each MQTT Control Packet contains a fixed header.
132 *
133 * http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Figure_2.2_-
134 */135 structFixedHeader136 {
137 @safepure @nogcnothrow:
138 privateubyte_payload;
139 140 /// Represented as a 4-bit unsigned value141 @propertyPacketTypetype() const142 {
143 returncast(PacketType)(_payload >> 4);
144 }
145 146 /// ditto147 @propertyvoidtype(inPacketTypetype)
148 {
149 _payload = cast(ubyte)((_payload & ~0xf0) | (type << 4));
150 }
151 152 /// Duplicate delivery of a PUBLISH Control Packet153 @propertybooldup() const154 {
155 return (_payload & 0x08) == 0x08;
156 }
157 158 /// ditto159 @propertyvoiddup(inboolvalue)
160 {
161 _payload = cast(ubyte)((_payload & ~0x08) | (value ? 0x08 : 0x00));
162 }
163 164 /// Quality Of Service for a message165 @propertyQoSLevelqos() const166 {
167 returncast(QoSLevel)((_payload >> 1) & 0x03);
168 }
169 170 /// ditto171 @propertyvoidqos(inQoSLevelvalue)
172 {
173 _payload = cast(ubyte)((_payload & ~0x06) | (value << 1));
174 }
175 176 /// PUBLISH Retain flag177 @propertyboolretain() const178 {
179 return (_payload & 0x01) == 0x01;
180 }
181 182 /// ditto183 @propertyvoidretain(inboolvalue)
184 {
185 _payload = cast(ubyte)(_payload & ~0x01) | (value ? 0x01 : 0x00);
186 }
187 188 /// flags to ubyte189 @propertyubyteflags() const190 {
191 return_payload;
192 }
193 194 @propertyvoidflags(inubytevalue)
195 {
196 _payload = value;
197 }
198 199 /**
200 * The Remaining Length is the number of bytes remaining within the current packet,
201 * including data in the variable header and the payload.
202 * The Remaining Length does not include the bytes used to encode the Remaining Length.
203 */204 uintlength;
205 206 aliasflagsthis;
207 208 this(PacketTypetype, booldup, QoSLevelqos, boolretain, uintlength = 0)
209 {
210 this.type = type;
211 this.dup = dup;
212 this.retain = retain;
213 this.qos = qos;
214 this.length = length;
215 }
216 217 this(T)(PacketTypetype, Tflags, uintlength = 0) if(isIntegral!T)
218 {
219 this.flags = cast(ubyte)(type << 4 | flags);
220 this.type = type;
221 this.length = length;
222 }
223 224 this(T)(Tvalue) if(isIntegral!T)
225 {
226 this.flags = cast(ubyte)value;
227 }
228 }
229 230 /**
231 * The Connect Flags byte contains a number of parameters specifying the behavior of the MQTT connection.
232 * It also indicates the presence or absence of fields in the payload.
233 */234 structConnectFlags235 {
236 @safepure @nogcnothrow:
237 privateubyte_payload;
238 239 /**
240 * If the User Name Flag is set to 0, a user name MUST NOT be present in the payload.
241 * If the User Name Flag is set to 1, a user name MUST be present in the payload.
242 */243 @propertybooluserName() const244 {
245 return (_payload & 0x80) == 0x80;
246 }
247 248 /// ditto249 @propertyvoiduserName(inboolvalue)
250 {
251 _payload = cast(ubyte)((_payload & ~0x80) | (value ? 0x80 : 0x00));
252 }
253 254 /**
255 * If the Password Flag is set to 0, a password MUST NOT be present in the payload.
256 * If the Password Flag is set to 1, a password MUST be present in the payload.
257 * If the User Name Flag is set to 0, the Password Flag MUST be set to 0.
258 */259 @propertyboolpassword() const260 {
261 return (_payload & 0x40) == 0x40;
262 }
263 264 /// ditto265 @propertyvoidpassword(inboolvalue)
266 {
267 _payload = cast(ubyte)((_payload & ~0x40) | (value ? 0x40 : 0x00));
268 }
269 270 /**
271 * This bit specifies if the Will Message is to be Retained when it is published.
272 *
273 * If the Will Flag is set to 0, then the Will Retain Flag MUST be set to 0.
274 * If the Will Flag is set to 1:
275 * If Will Retain is set to 0, the Server MUST publish the Will Message as a non-retained message.
276 * If Will Retain is set to 1, the Server MUST publish the Will Message as a retained message
277 */278 @propertyboolwillRetain() const279 {
280 return (_payload & 0x20) == 0x20;
281 }
282 283 /// ditto284 @propertyvoidwillRetain(inboolvalue)
285 {
286 _payload = cast(ubyte)((_payload & ~0x20) | (value ? 0x20 : 0x00));
287 }
288 289 /**
290 * Specify the QoS level to be used when publishing the Will Message.
291 *
292 * If the Will Flag is set to 0, then the Will QoS MUST be set to 0 (0x00).
293 * If the Will Flag is set to 1, the value of Will QoS can be 0 (0x00), 1 (0x01), or 2 (0x02).
294 * It MUST NOT be 3 (0x03)
295 */296 @propertyQoSLevelwillQoS() const297 {
298 returncast(QoSLevel)((_payload >> 3) & 0x03);
299 }
300 301 /// ditto302 @propertyvoidwillQoS(inQoSLevelvalue)
303 {
304 _payload = cast(ubyte)((_payload & ~0x18) | (value << 3));
305 }
306 307 /**
308 * If the Will Flag is set to 1 this indicates that, if the Connect request is accepted, a Will Message MUST
309 * be stored on the Server and associated with the Network Connection. The Will Message MUST be published
310 * when the Network Connection is subsequently closed unless the Will Message has been deleted by the Server
311 * on receipt of a DISCONNECT Packet.
312 *
313 * Situations in which the Will Message is published include, but are not limited to:
314 * An I/O error or network failure detected by the Server.
315 * The Client fails to communicate within the Keep Alive time.
316 * The Client closes the Network Connection without first sending a DISCONNECT Packet.
317 * The Server closes the Network Connection because of a protocol error.
318 *
319 * If the Will Flag is set to 1, the Will QoS and Will Retain fields in the Connect Flags will be used by
320 * the Server, and the Will Topic and Will Message fields MUST be present in the payload.
321 *
322 * The Will Message MUST be removed from the stored Session state in the Server once it has been published
323 * or the Server has received a DISCONNECT packet from the Client.
324 *
325 * If the Will Flag is set to 0 the Will QoS and Will Retain fields in the Connect Flags MUST be set to zero
326 * and the Will Topic and Will Message fields MUST NOT be present in the payload.
327 *
328 * If the Will Flag is set to 0, a Will Message MUST NOT be published when this Network Connection ends
329 */330 @propertyboolwill() const331 {
332 return (_payload & 0x04) == 0x04;
333 }
334 335 /// ditto336 @propertyvoidwill(inboolvalue)
337 {
338 _payload = cast(ubyte)((_payload & ~0x04) | (value ? 0x04 : 0x00));
339 }
340 341 /**
342 * This bit specifies the handling of the Session state.
343 * The Client and Server can store Session state to enable reliable messaging to continue across a sequence
344 * of Network Connections. This bit is used to control the lifetime of the Session state.
345 *
346 * If CleanSession is set to 0, the Server MUST resume communications with the Client based on state from
347 * the current Session (as identified by the Client identifier).
348 * If there is no Session associated with the Client identifier the Server MUST create a new Session.
349 * The Client and Server MUST store the Session after the Client and Server are disconnected.
350 * After the disconnection of a Session that had CleanSession set to 0, the Server MUST store
351 * further QoS 1 and QoS 2 messages that match any subscriptions that the client had at the time of disconnection
352 * as part of the Session state.
353 * It MAY also store QoS 0 messages that meet the same criteria.
354 *
355 * If CleanSession is set to 1, the Client and Server MUST discard any previous Session and start a new one.
356 * This Session lasts as long as the Network Connection. State data associated with this Session MUST NOT be reused
357 * in any subsequent Session.
358 *
359 * The Session state in the Client consists of:
360 * QoS 1 and QoS 2 messages which have been sent to the Server, but have not been completely acknowledged.
361 * QoS 2 messages which have been received from the Server, but have not been completely acknowledged.
362 *
363 * To ensure consistent state in the event of a failure, the Client should repeat its attempts to connect with
364 * CleanSession set to 1, until it connects successfully.
365 *
366 * Typically, a Client will always connect using CleanSession set to 0 or CleanSession set to 1 and not swap
367 * between the two values. The choice will depend on the application. A Client using CleanSession set to 1 will
368 * not receive old Application Messages and has to subscribe afresh to any topics that it is interested in each
369 * time it connects. A Client using CleanSession set to 0 will receive all QoS 1 or QoS 2 messages that were
370 * published while it was disconnected. Hence, to ensure that you do not lose messages while disconnected,
371 * use QoS 1 or QoS 2 with CleanSession set to 0.
372 *
373 * When a Client connects with CleanSession set to 0, it is requesting that the Server maintain its MQTT session
374 * state after it disconnects. Clients should only connect with CleanSession set to 0, if they intend to reconnect
375 * to the Server at some later point in time. When a Client has determined that it has no further use for
376 * the session, it should do a final connect with CleanSession set to 1 and then disconnect.
377 */378 @propertyboolcleanSession() const379 {
380 return (_payload & 0x02) == 0x02;
381 }
382 383 /// ditto384 @propertyvoidcleanSession(inboolvalue)
385 {
386 _payload = cast(ubyte)((_payload & ~0x02) | (value ? 0x02 : 0x00));
387 }
388 389 @propertyubyteflags() const390 {
391 return_payload;
392 }
393 394 @propertyvoidflags(ubytevalue) pure395 {
396 _payload = value & ~0x01;
397 }
398 399 this(booluserName, boolpassword, boolwillRetain, QoSLevelwillQoS, boolwill, boolcleanSession)
400 {
401 this.userName = userName;
402 this.password = password;
403 this.willRetain = willRetain;
404 this.willQoS = willQoS;
405 this.will = will;
406 this.cleanSession = cleanSession;
407 }
408 409 this(T)(Tvalue) if(isIntegral!T)
410 {
411 this.flags = cast(ubyte)(value & ~0x01);
412 }
413 414 aliasflagsthis;
415 416 unittest417 {
418 importstd.array;
419 420 ConnectFlagsflags;
421 422 assert(flags == ConnectFlags(false, false, false, QoSLevel.QoS0, false, false));
423 assert(flags == 0);
424 425 flags.flags = 1; //reserved - no change426 assert(flags == ConnectFlags(false, false, false, QoSLevel.QoS0, false, false));
427 assert(flags == 0);
428 429 flags.flags = 2;
430 assert(flags == ConnectFlags(false, false, false, QoSLevel.QoS0, false, true));
431 432 flags.flags = 4;
433 assert(flags == ConnectFlags(false, false, false, QoSLevel.QoS0, true, false));
434 435 flags.flags = 24;
436 assert(flags == ConnectFlags(false, false, false, QoSLevel.Reserved, false, false));
437 438 flags.flags = 32;
439 assert(flags == ConnectFlags(false, false, true, QoSLevel.QoS0, false, false));
440 441 flags.flags = 64;
442 assert(flags == ConnectFlags(false, true, false, QoSLevel.QoS0, false, false));
443 444 flags.flags = 128;
445 assert(flags == ConnectFlags(true, false, false, QoSLevel.QoS0, false, false));
446 }
447 }
448 449 /// Connect Acknowledge Flags450 structConnAckFlags451 {
452 @safepure @nogcnothrow:
453 privateubyte_payload;
454 455 /**
456 * If the Server accepts a connection with CleanSession set to 1, the Server MUST set Session Present to 0
457 * in the CONNACK packet in addition to setting a zero return code in the CONNACK packet.
458 *
459 * If the Server accepts a connection with CleanSession set to 0, the value set in Session Present depends on
460 * whether the Server already has stored Session state for the supplied client ID. If the Server has stored
461 * Session state, it MUST set Session Present to 1 in the CONNACK packet.
462 * If the Server does not have stored Session state, it MUST set Session Present to 0 in the CONNACK packet.
463 * This is in addition to setting a zero return code in the CONNACK packet.
464 *
465 * The Session Present flag enables a Client to establish whether the Client and Server have a consistent view
466 * about whether there is already stored Session state.
467 *
468 * Once the initial setup of a Session is complete, a Client with stored Session state will expect the Server
469 * to maintain its stored Session state. In the event that the value of Session Present received by the Client
470 * from the Server is not as expected, the Client can choose whether to proceed with the Session or to disconnect.
471 * The Client can discard the Session state on both Client and Server by disconnecting, connecting with
472 * Clean Session set to 1 and then disconnecting again.
473 *
474 * If a server sends a CONNACK packet containing a non-zero return code it MUST set Session Present to 0
475 */476 @propertyboolsessionPresent() const477 {
478 return (_payload & 0x01) == 0x01;
479 }
480 481 /// ditto482 @propertyvoidsessionPresent(inboolvalue)
483 {
484 _payload = cast(ubyte)(value ? 0x01 : 0x00);
485 }
486 487 @propertyubyteflags() const488 {
489 return_payload;
490 }
491 492 @propertyvoidflags(ubytevalue) pure493 {
494 _payload = cast(ubyte)(value & 0x01); //clean reserved bits495 }
496 497 aliasflagsthis;
498 499 this(T)(Tvalue) if(isIntegral!T)
500 {
501 this.flags = cast(ubyte)value;
502 }
503 }
504 505 /// The payload of a SUBSCRIBE Packet506 staticstructTopic507 {
508 /**
509 * Topic Filter indicating the Topic to which the Client wants to subscribe.
510 * The Topic Filters in a SUBSCRIBE packet payload MUST be UTF-8 encoded strings.
511 * A Server SHOULD support Topic filters that contain the wildcard characters.
512 * If it chooses not to support topic filters that contain wildcard characters it MUST reject any Subscription request whose filter contains them
513 */514 stringfilter;
515 /// This gives the maximum QoS level at which the Server can send Application Messages to the Client.516 QoSLevelqos;
517 }
518 519 /**
520 * After a Network Connection is established by a Client to a Server,
521 * the first Packet sent from the Client to the Server MUST be a CONNECT Packet.
522 *
523 * A Client can only send the CONNECT Packet once over a Network Connection.
524 * The Server MUST process a second CONNECT Packet sent from a Client as a protocol violation and disconnect the Client.
525 *
526 * The payload contains one or more encoded fields.
527 * They specify a unique Client identifier for the Client, a Will topic, Will Message, User Name and Password.
528 * All but the Client identifier are optional and their presence is determined based on flags in the variable header.
529 */530 structConnect531 {
532 FixedHeaderheader = FixedHeader(0x10);
533 534 /// The Protocol Name is a UTF-8 encoded string that represents the protocol name “MQTT”535 stringprotocolName = MQTT_PROTOCOL_NAME;
536 537 /**
538 * The 8 bit unsigned value that represents the revision level of the protocol used by the Client.
539 * The value of the Protocol Level field for the version 3.1.1 of the protocol is 4 (0x04).
540 */541 ubyteprotocolLevel = MQTT_PROTOCOL_LEVEL_3_1_1;
542 543 /**
544 * The Connect Flags byte contains a number of parameters specifying the behavior of the MQTT connection.
545 * It also indicates the presence or absence of fields in the payload.
546 */547 ConnectFlagsflags;
548 549 /**
550 * The Keep Alive is a time interval measured in seconds. Expressed as a 16-bit word, it is the maximum time
551 * interval that is permitted to elapse between the point at which the Client finishes transmitting one Control
552 * Packet and the point it starts sending the next. It is the responsibility of the Client to ensure that the
553 * interval between Control Packets being sent does not exceed the Keep Alive value.
554 * In the absence of sending any other Control Packets, the Client MUST send a PINGREQ Packet.
555 *
556 * The Client can send PINGREQ at any time, irrespective of the Keep Alive value, and use the PINGRESP to determine
557 * that the network and the Server are working.
558 *
559 * If the Keep Alive value is non-zero and the Server does not receive a Control Packet from the Client within
560 * one and a half times the Keep Alive time period, it MUST disconnect the Network Connection to the Client as if
561 * the network had failed.
562 *
563 * If a Client does not receive a PINGRESP Packet within a reasonable amount of time after it has sent a PINGREQ,
564 * it SHOULD close the Network Connection to the Server.
565 *
566 * A Keep Alive value of zero (0) has the effect of turning off the keep alive mechanism.
567 * This means that, in this case, the Server is not required to disconnect the Client on the grounds of inactivity.
568 * Note that a Server is permitted to disconnect a Client that it determines to be inactive or non-responsive
569 * at any time, regardless of the Keep Alive value provided by that Client.
570 *
571 * The actual value of the Keep Alive is application specific; typically this is a few minutes.
572 * The maximum value is 18 hours 12 minutes and 15 seconds.
573 */574 ushortkeepAlive;
575 576 /// Client Identifier577 stringclientIdentifier;
578 579 /// Will Topic580 stringwillTopic;
581 582 /// Will Message583 stringwillMessage;
584 585 /// User Name586 stringuserName;
587 588 /// Password589 stringpassword;
590 }
591 592 /// Responce to Connect request593 structConnAck594 {
595 FixedHeaderheader = FixedHeader(PacketType.CONNACK, 0, 2);
596 597 ConnAckFlagsflags;
598 599 ConnectReturnCodereturnCode;
600 }
601 602 /// A PUBLISH Control Packet is sent from a Client to a Server or from Server to a Client to transport an Application Message.603 structPublish604 {
605 FixedHeaderheader = FixedHeader(0x30);
606 /**
607 * The Topic Name identifies the information channel to which payload data is published.
608 *
609 * The Topic Name MUST be present as the first field in the PUBLISH Packet Variable header.
610 * It MUST be a UTF-8 encoded string.
611 *
612 * The Topic Name in the PUBLISH Packet MUST NOT contain wildcard characters.
613 * The Topic Name in a PUBLISH Packet sent by a Server to a subscribing Client MUST match the Subscription’s
614 * Topic Filter according to the matching process.
615 * However, since the Server is permitted to override the Topic Name, it might not be the same as the Topic Name
616 * in the original PUBLISH Packet.
617 */618 stringtopic;
619 /// The Packet Identifier field is only present in PUBLISH Packets where the QoS level is 1 or 2620 ushortpacketId; // if QoS > 0621 /**
622 * The Payload contains the Application Message that is being published.
623 * The content and format of the data is application specific.
624 * The length of the payload can be calculated by subtracting the length of the variable header from
625 * the Remaining Length field that is in the Fixed Header.
626 * It is valid for a PUBLISH Packet to contain a zero length payload.
627 */628 ubyte[] payload;
629 }
630 631 /// A PUBACK Packet is the response to a PUBLISH Packet with QoS level 1.632 structPubAck633 {
634 FixedHeaderheader = FixedHeader(PacketType.PUBACK, 0, 2);
635 636 /// This contains the Packet Identifier from the PUBLISH Packet that is being acknowledged.637 ushortpacketId;
638 639 this(ushortpacketId)
640 {
641 this.packetId = packetId;
642 }
643 }
644 645 /// A PUBREC Packet is the response to a PUBLISH Packet with QoS 2. It is the second packet of the QoS 2 protocol exchange.646 structPubRec647 {
648 FixedHeaderheader = FixedHeader(PacketType.PUBREC, 0, 2);
649 650 /// This contains the Packet Identifier from the PUBLISH Packet that is being acknowledged.651 ushortpacketId;
652 653 this(ushortpacketId)
654 {
655 this.packetId = packetId;
656 }
657 }
658 659 /// A PUBREL Packet is the response to a PUBREC Packet. It is the third packet of the QoS 2 protocol exchange.660 structPubRel661 {
662 FixedHeaderheader = FixedHeader(PacketType.PUBREL, 0x02, 2);
663 664 /// This contains the same Packet Identifier as the PUBREC Packet that is being acknowledged.665 ushortpacketId;
666 667 this(ushortpacketId)
668 {
669 this.packetId = packetId;
670 }
671 }
672 673 /// The PUBCOMP Packet is the response to a PUBREL Packet. It is the fourth and final packet of the QoS 2 protocol exchange.674 structPubComp675 {
676 FixedHeaderheader = FixedHeader(PacketType.PUBCOMP, 0, 2);
677 678 /// This contains the same Packet Identifier as the PUBREC Packet that is being acknowledged.679 ushortpacketId;
680 681 this(ushortpacketId)
682 {
683 this.packetId = packetId;
684 }
685 }
686 687 /**
688 * The SUBSCRIBE Packet is sent from the Client to the Server to create one or more Subscriptions.
689 * Each Subscription registers a Client’s interest in one or more Topics.
690 * The Server sends PUBLISH Packets to the Client in order to forward Application Messages that were published to Topics that match these Subscriptions.
691 * The SUBSCRIBE Packet also specifies (for each Subscription) the maximum QoS with which the Server can send Application Messages to the Client.
692 *
693 * The payload of a SUBSCRIBE packet MUST contain at least one Topic Filter / QoS pair. A SUBSCRIBE packet with no payload is a protocol violation.
694 */695 structSubscribe696 {
697 FixedHeaderheader = FixedHeader(PacketType.SUBSCRIBE, 0x02);
698 699 /// This contains the Packet Identifier.700 ushortpacketId;
701 702 /// Topics to register to703 Topic[] topics;
704 }
705 706 /**
707 * A SUBACK Packet is sent by the Server to the Client to confirm receipt and processing of a SUBSCRIBE Packet.
708 * A SUBACK Packet contains a list of return codes, that specify the maximum QoS level that was granted in each
709 * Subscription that was requested by the SUBSCRIBE.
710 */711 structSubAck712 {
713 FixedHeaderheader = FixedHeader(PacketType.SUBACK, 0);
714 715 /// This contains the Packet Identifier from the SUBSCRIBE Packet that is being acknowledged.716 ushortpacketId;
717 /**
718 * The payload contains a list of return codes. Each return code corresponds to a Topic Filter in the
719 * SUBSCRIBE Packet being acknowledged.
720 * The order of return codes in the SUBACK Packet MUST match the order of Topic Filters in the SUBSCRIBE Packet.
721 */722 QoSLevel[] returnCodes;
723 }
724 725 /// An UNSUBSCRIBE Packet is sent by the Client to the Server, to unsubscribe from topics.726 structUnsubscribe727 {
728 FixedHeaderheader = FixedHeader(PacketType.UNSUBSCRIBE, 0x02);
729 730 /// This contains the Packet Identifier.731 ushortpacketId;
732 733 /**
734 * The list of Topic Filters that the Client wishes to unsubscribe from. The Topic Filters in an UNSUBSCRIBE packet MUST be UTF-8 encoded strings.
735 * The Payload of an UNSUBSCRIBE packet MUST contain at least one Topic Filter. An UNSUBSCRIBE packet with no payload is a protocol violation.
736 */737 string[] topics;
738 }
739 740 /// The UNSUBACK Packet is sent by the Server to the Client to confirm receipt of an UNSUBSCRIBE Packet.741 structUnsubAck742 {
743 FixedHeaderheader = FixedHeader(PacketType.UNSUBACK, 0, 2);
744 745 /// This contains the same Packet Identifier as the UNSUBSCRIBE Packet that is being acknowledged.746 ushortpacketId;
747 }
748 749 /**
750 * The PINGREQ Packet is sent from a Client to the Server. It can be used to:
751 *
752 * Indicate to the Server that the Client is alive in the absence of any other Control Packets being sent from the Client to the Server.
753 * Request that the Server responds to confirm that it is alive.
754 * Exercise the network to indicate that the Network Connection is active.
755 *
756 * This Packet is used in Keep Alive processing
757 */758 structPingReq759 {
760 FixedHeaderheader = FixedHeader(PacketType.PINGREQ, 0, 0);
761 }
762 763 /**
764 * A PINGRESP Packet is sent by the Server to the Client in response to a PINGREQ Packet. It indicates that the Server is alive.
765 * This Packet is used in Keep Alive processing.
766 */767 structPingResp768 {
769 FixedHeaderheader = FixedHeader(PacketType.PINGRESP, 0, 0);
770 }
771 772 /**
773 * The DISCONNECT Packet is the final Control Packet sent from the Client to the Server. It indicates that the Client is disconnecting cleanly.
774 *
775 * After sending a DISCONNECT Packet the Client:
776 * MUST close the Network Connection.
777 * MUST NOT send any more Control Packets on that Network Connection.
778 *
779 * On receipt of DISCONNECT the Server:
780 * MUST discard any Will Message associated with the current connection without publishing it.
781 * SHOULD close the Network Connection if the Client has not already done so.
782 */783 structDisconnect784 {
785 FixedHeaderheader = FixedHeader(PacketType.DISCONNECT, 0, 0);
786 }