From bd6472e5e4f5e0c5588b3003c0d966b2e2e07974 Mon Sep 17 00:00:00 2001 From: Philip Schell Date: Tue, 26 Nov 2019 15:34:16 +0100 Subject: [PATCH] add netcore and refactoring --- M2Mqtt/Exceptions/MqttClientException.cs | 205 +- .../Exceptions/MqttCommunicationException.cs | 38 +- M2Mqtt/Exceptions/MqttConnectionException.cs | 21 +- M2Mqtt/Exceptions/MqttTimeoutException.cs | 14 +- M2Mqtt/IMqttNetworkChannel.cs | 93 +- M2Mqtt/Internal/InternalEvent.cs | 14 +- M2Mqtt/Internal/MsgInternalEvent.cs | 50 +- M2Mqtt/Internal/MsgPublishedInternalEvent.cs | 55 +- M2Mqtt/M2Mqtt_Core.csproj | 24 + M2Mqtt/Messages/MqttMsgBase.cs | 463 +-- M2Mqtt/Messages/MqttMsgConnack.cs | 310 +- M2Mqtt/Messages/MqttMsgConnect.cs | 1028 +++-- M2Mqtt/Messages/MqttMsgConnectEventArgs.cs | 37 +- M2Mqtt/Messages/MqttMsgContext.cs | 262 +- M2Mqtt/Messages/MqttMsgDisconnect.cs | 117 +- M2Mqtt/Messages/MqttMsgPingReq.cs | 117 +- M2Mqtt/Messages/MqttMsgPingResp.cs | 116 +- M2Mqtt/Messages/MqttMsgPuback.cs | 192 +- M2Mqtt/Messages/MqttMsgPubcomp.cs | 192 +- M2Mqtt/Messages/MqttMsgPublish.cs | 83 +- M2Mqtt/Messages/MqttMsgPublishEventArgs.cs | 142 +- M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs | 92 +- M2Mqtt/Messages/MqttMsgPubrec.cs | 192 +- M2Mqtt/Messages/MqttMsgPubrel.cs | 226 +- M2Mqtt/Messages/MqttMsgSuback.cs | 255 +- M2Mqtt/Messages/MqttMsgSubscribe.cs | 451 +- M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs | 94 +- M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs | 74 +- M2Mqtt/Messages/MqttMsgUnsuback.cs | 191 +- M2Mqtt/Messages/MqttMsgUnsubscribe.cs | 391 +- .../Messages/MqttMsgUnsubscribeEventArgs.cs | 74 +- .../Messages/MqttMsgUnsubscribedEventArgs.cs | 52 +- M2Mqtt/MqttClient.cs | 3642 ++++++++--------- M2Mqtt/MqttSecurity.cs | 24 +- M2Mqtt/MqttSettings.cs | 169 +- M2Mqtt/Net/Fx.cs | 27 +- M2Mqtt/Net/MqttNetworkChannel.cs | 618 ++- M2Mqtt/Properties/AssemblyInfo.cs | 3 +- M2Mqtt/Session/MqttClientSession.cs | 26 +- M2Mqtt/Session/MqttSession.cs | 82 +- M2Mqtt/Utility/QueueExtension.cs | 53 +- M2Mqtt/Utility/Trace.cs | 118 +- M2Mqtt/WinRT/Fx.cs | 5 +- M2Mqtt/WinRT/Hashtable.cs | 3 +- M2Mqtt/WinRT/MqttNetworkChannel.cs | 3 +- M2Mqtt/WinRT/Queue.cs | 3 +- M2Mqtt_Core.sln | 25 + 47 files changed, 4897 insertions(+), 5569 deletions(-) create mode 100644 M2Mqtt/M2Mqtt_Core.csproj create mode 100644 M2Mqtt_Core.sln diff --git a/M2Mqtt/Exceptions/MqttClientException.cs b/M2Mqtt/Exceptions/MqttClientException.cs index cd8303f..d26ddfb 100644 --- a/M2Mqtt/Exceptions/MqttClientException.cs +++ b/M2Mqtt/Exceptions/MqttClientException.cs @@ -16,117 +16,104 @@ Contributors: using System; -namespace uPLibrary.Networking.M2Mqtt.Exceptions -{ +namespace uPLibrary.Networking.M2Mqtt.Exceptions { + /// + /// MQTT client exception + /// + public class MqttClientException : Exception { /// - /// MQTT client exception + /// Constructor /// - public class MqttClientException : Exception - { - /// - /// Constructor - /// - /// Error code - public MqttClientException(MqttClientErrorCode errorCode) - { - this.errorCode = errorCode; - } - - // error code - private MqttClientErrorCode errorCode; - - /// - /// Error code - /// - public MqttClientErrorCode ErrorCode - { - get { return this.errorCode; } - set { this.errorCode = value; } - } - } - + /// Error code + public MqttClientException(MqttClientErrorCode errorCode) => this.ErrorCode = errorCode; + /// - /// MQTT client erroro code + /// Error code /// - public enum MqttClientErrorCode - { - /// - /// Will error (topic, message or QoS level) - /// - WillWrong = 1, - - /// - /// Keep alive period too large - /// - KeepAliveWrong, - - /// - /// Topic contains wildcards - /// - TopicWildcard, - - /// - /// Topic length wrong - /// - TopicLength, - - /// - /// QoS level not allowed - /// - QosNotAllowed, - - /// - /// Topics list empty for subscribe - /// - TopicsEmpty, - - /// - /// Qos levels list empty for subscribe - /// - QosLevelsEmpty, - - /// - /// Topics / Qos Levels not match in subscribe - /// - TopicsQosLevelsNotMatch, - - /// - /// Wrong message from broker - /// - WrongBrokerMessage, - - /// - /// Wrong Message Id - /// - WrongMessageId, - - /// - /// Inflight queue is full - /// - InflightQueueFull, - - // [v3.1.1] - /// - /// Invalid flag bits received - /// - InvalidFlagBits, - - // [v3.1.1] - /// - /// Invalid connect flags received - /// - InvalidConnectFlags, - - // [v3.1.1] - /// - /// Invalid client id - /// - InvalidClientId, - - // [v3.1.1] - /// - /// Invalid protocol name - /// - InvalidProtocolName - } + public MqttClientErrorCode ErrorCode { get; set; } + } + + /// + /// MQTT client erroro code + /// + public enum MqttClientErrorCode { + /// + /// Will error (topic, message or QoS level) + /// + WillWrong = 1, + + /// + /// Keep alive period too large + /// + KeepAliveWrong, + + /// + /// Topic contains wildcards + /// + TopicWildcard, + + /// + /// Topic length wrong + /// + TopicLength, + + /// + /// QoS level not allowed + /// + QosNotAllowed, + + /// + /// Topics list empty for subscribe + /// + TopicsEmpty, + + /// + /// Qos levels list empty for subscribe + /// + QosLevelsEmpty, + + /// + /// Topics / Qos Levels not match in subscribe + /// + TopicsQosLevelsNotMatch, + + /// + /// Wrong message from broker + /// + WrongBrokerMessage, + + /// + /// Wrong Message Id + /// + WrongMessageId, + + /// + /// Inflight queue is full + /// + InflightQueueFull, + + // [v3.1.1] + /// + /// Invalid flag bits received + /// + InvalidFlagBits, + + // [v3.1.1] + /// + /// Invalid connect flags received + /// + InvalidConnectFlags, + + // [v3.1.1] + /// + /// Invalid client id + /// + InvalidClientId, + + // [v3.1.1] + /// + /// Invalid protocol name + /// + InvalidProtocolName + } } diff --git a/M2Mqtt/Exceptions/MqttCommunicationException.cs b/M2Mqtt/Exceptions/MqttCommunicationException.cs index 6b916cc..f995e94 100644 --- a/M2Mqtt/Exceptions/MqttCommunicationException.cs +++ b/M2Mqtt/Exceptions/MqttCommunicationException.cs @@ -16,27 +16,23 @@ Contributors: using System; -namespace uPLibrary.Networking.M2Mqtt.Exceptions -{ +namespace uPLibrary.Networking.M2Mqtt.Exceptions { + /// + /// Exception due to error communication with broker on socket + /// + public class MqttCommunicationException : Exception { /// - /// Exception due to error communication with broker on socket + /// Default constructor /// - public class MqttCommunicationException : Exception - { - /// - /// Default constructor - /// - public MqttCommunicationException() - { - } - - /// - /// Constructor - /// - /// Inner Exception - public MqttCommunicationException(Exception e) - : base(String.Empty, e) - { - } - } + public MqttCommunicationException() { + } + + /// + /// Constructor + /// + /// Inner Exception + public MqttCommunicationException(Exception e) + : base(String.Empty, e) { + } + } } diff --git a/M2Mqtt/Exceptions/MqttConnectionException.cs b/M2Mqtt/Exceptions/MqttConnectionException.cs index 3203c7e..2ad847f 100644 --- a/M2Mqtt/Exceptions/MqttConnectionException.cs +++ b/M2Mqtt/Exceptions/MqttConnectionException.cs @@ -16,16 +16,13 @@ Contributors: using System; -namespace uPLibrary.Networking.M2Mqtt.Exceptions -{ - /// - /// Connection to the broker exception - /// - public class MqttConnectionException : Exception - { - public MqttConnectionException(string message, Exception innerException) - : base(message, innerException) - { - } - } +namespace uPLibrary.Networking.M2Mqtt.Exceptions { + /// + /// Connection to the broker exception + /// + public class MqttConnectionException : Exception { + public MqttConnectionException(String message, Exception innerException) + : base(message, innerException) { + } + } } diff --git a/M2Mqtt/Exceptions/MqttTimeoutException.cs b/M2Mqtt/Exceptions/MqttTimeoutException.cs index b15e69e..bd25dcc 100644 --- a/M2Mqtt/Exceptions/MqttTimeoutException.cs +++ b/M2Mqtt/Exceptions/MqttTimeoutException.cs @@ -16,12 +16,10 @@ Contributors: using System; -namespace uPLibrary.Networking.M2Mqtt.Exceptions -{ - /// - /// Timeout on receiving from broker exception - /// - public class MqttTimeoutException : Exception - { - } +namespace uPLibrary.Networking.M2Mqtt.Exceptions { + /// + /// Timeout on receiving from broker exception + /// + public class MqttTimeoutException : Exception { + } } diff --git a/M2Mqtt/IMqttNetworkChannel.cs b/M2Mqtt/IMqttNetworkChannel.cs index f4ff90f..0a1e86e 100644 --- a/M2Mqtt/IMqttNetworkChannel.cs +++ b/M2Mqtt/IMqttNetworkChannel.cs @@ -15,55 +15,52 @@ Contributors: */ using System; -using System.Text; -namespace uPLibrary.Networking.M2Mqtt -{ +namespace uPLibrary.Networking.M2Mqtt { + /// + /// Interface for channel under MQTT library + /// + public interface IMqttNetworkChannel { /// - /// Interface for channel under MQTT library + /// Data available on channel /// - public interface IMqttNetworkChannel - { - /// - /// Data available on channel - /// - bool DataAvailable { get; } - - /// - /// Receive data from the network channel - /// - /// Data buffer for receiving data - /// Number of bytes received - int Receive(byte[] buffer); - - /// - /// Receive data from the network channel with a specified timeout - /// - /// Data buffer for receiving data - /// Timeout on receiving (in milliseconds) - /// Number of bytes received - int Receive(byte[] buffer, int timeout); - - /// - /// Send data on the network channel to the broker - /// - /// Data buffer to send - /// Number of byte sent - int Send(byte[] buffer); - - /// - /// Close the network channel - /// - void Close(); - - /// - /// Connect to remote server - /// - void Connect(); - - /// - /// Accept client connection - /// - void Accept(); - } + Boolean DataAvailable { get; } + + /// + /// Receive data from the network channel + /// + /// Data buffer for receiving data + /// Number of bytes received + Int32 Receive(Byte[] buffer); + + /// + /// Receive data from the network channel with a specified timeout + /// + /// Data buffer for receiving data + /// Timeout on receiving (in milliseconds) + /// Number of bytes received + Int32 Receive(Byte[] buffer, Int32 timeout); + + /// + /// Send data on the network channel to the broker + /// + /// Data buffer to send + /// Number of byte sent + Int32 Send(Byte[] buffer); + + /// + /// Close the network channel + /// + void Close(); + + /// + /// Connect to remote server + /// + void Connect(); + + /// + /// Accept client connection + /// + void Accept(); + } } diff --git a/M2Mqtt/Internal/InternalEvent.cs b/M2Mqtt/Internal/InternalEvent.cs index 7f298c7..ec196c6 100644 --- a/M2Mqtt/Internal/InternalEvent.cs +++ b/M2Mqtt/Internal/InternalEvent.cs @@ -14,12 +14,10 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ -namespace uPLibrary.Networking.M2Mqtt.Internal -{ - /// - /// Generic internal event for dispatching - /// - public abstract class InternalEvent - { - } +namespace uPLibrary.Networking.M2Mqtt.Internal { + /// + /// Generic internal event for dispatching + /// + public abstract class InternalEvent { + } } diff --git a/M2Mqtt/Internal/MsgInternalEvent.cs b/M2Mqtt/Internal/MsgInternalEvent.cs index 8cf46a4..8be6658 100644 --- a/M2Mqtt/Internal/MsgInternalEvent.cs +++ b/M2Mqtt/Internal/MsgInternalEvent.cs @@ -16,36 +16,26 @@ Contributors: using uPLibrary.Networking.M2Mqtt.Messages; -namespace uPLibrary.Networking.M2Mqtt.Internal -{ +namespace uPLibrary.Networking.M2Mqtt.Internal { + /// + /// Internal event with a message + /// + public class MsgInternalEvent : InternalEvent { + #region Properties ... + /// - /// Internal event with a message + /// Related message /// - public class MsgInternalEvent : InternalEvent - { - #region Properties ... - - /// - /// Related message - /// - public MqttMsgBase Message - { - get { return this.msg; } - set { this.msg = value; } - } - - #endregion - - // related message - protected MqttMsgBase msg; - - /// - /// Constructor - /// - /// Related message - public MsgInternalEvent(MqttMsgBase msg) - { - this.msg = msg; - } - } + public MqttMsgBase Message { get; set; } + + #endregion + + // related message + + /// + /// Constructor + /// + /// Related message + public MsgInternalEvent(MqttMsgBase msg) => this.Message = msg; + } } diff --git a/M2Mqtt/Internal/MsgPublishedInternalEvent.cs b/M2Mqtt/Internal/MsgPublishedInternalEvent.cs index 5a7f182..062a8d2 100644 --- a/M2Mqtt/Internal/MsgPublishedInternalEvent.cs +++ b/M2Mqtt/Internal/MsgPublishedInternalEvent.cs @@ -14,40 +14,31 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ +using System; using uPLibrary.Networking.M2Mqtt.Messages; -namespace uPLibrary.Networking.M2Mqtt.Internal -{ +namespace uPLibrary.Networking.M2Mqtt.Internal { + /// + /// Internal event for a published message + /// + public class MsgPublishedInternalEvent : MsgInternalEvent { + #region Properties... + /// - /// Internal event for a published message + /// Message published (or failed due to retries) /// - public class MsgPublishedInternalEvent : MsgInternalEvent - { - #region Properties... - - /// - /// Message published (or failed due to retries) - /// - public bool IsPublished - { - get { return this.isPublished; } - internal set { this.isPublished = value; } - } - - #endregion - - // published flag - bool isPublished; - - /// - /// Constructor - /// - /// Message published - /// Publish flag - public MsgPublishedInternalEvent(MqttMsgBase msg, bool isPublished) - : base(msg) - { - this.isPublished = isPublished; - } - } + public Boolean IsPublished { get; internal set; } + + #endregion + + // published flag + + /// + /// Constructor + /// + /// Message published + /// Publish flag + public MsgPublishedInternalEvent(MqttMsgBase msg, Boolean isPublished) + : base(msg) => this.IsPublished = isPublished; + } } diff --git a/M2Mqtt/M2Mqtt_Core.csproj b/M2Mqtt/M2Mqtt_Core.csproj new file mode 100644 index 0000000..f18ab8e --- /dev/null +++ b/M2Mqtt/M2Mqtt_Core.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp3.0 + M2Mqtt + uPLibrary.Networking.M2Mqtt + MQTT Client Library for M2M communication + Paolo Patierno + Paolo Patierno + Copyright © Paolo Patierno 2014 + 4.3.0 + 4.3.0 + 4.3.0 + + + + TRACE;SSL + + + + TRACE;SSL + + + diff --git a/M2Mqtt/Messages/MqttMsgBase.cs b/M2Mqtt/Messages/MqttMsgBase.cs index 870fe5f..407adbd 100644 --- a/M2Mqtt/Messages/MqttMsgBase.cs +++ b/M2Mqtt/Messages/MqttMsgBase.cs @@ -17,259 +17,220 @@ Contributors: using System; using System.Text; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Base class for all MQTT messages + /// + public abstract class MqttMsgBase { + #region Constants... + + // mask, offset and size for fixed header fields + internal const Byte MSG_TYPE_MASK = 0xF0; + internal const Byte MSG_TYPE_OFFSET = 0x04; + internal const Byte MSG_TYPE_SIZE = 0x04; + internal const Byte MSG_FLAG_BITS_MASK = 0x0F; // [v3.1.1] + internal const Byte MSG_FLAG_BITS_OFFSET = 0x00; // [v3.1.1] + internal const Byte MSG_FLAG_BITS_SIZE = 0x04; // [v3.1.1] + internal const Byte DUP_FLAG_MASK = 0x08; + internal const Byte DUP_FLAG_OFFSET = 0x03; + internal const Byte DUP_FLAG_SIZE = 0x01; + internal const Byte QOS_LEVEL_MASK = 0x06; + internal const Byte QOS_LEVEL_OFFSET = 0x01; + internal const Byte QOS_LEVEL_SIZE = 0x02; + internal const Byte RETAIN_FLAG_MASK = 0x01; + internal const Byte RETAIN_FLAG_OFFSET = 0x00; + internal const Byte RETAIN_FLAG_SIZE = 0x01; + + // MQTT message types + internal const Byte MQTT_MSG_CONNECT_TYPE = 0x01; + internal const Byte MQTT_MSG_CONNACK_TYPE = 0x02; + internal const Byte MQTT_MSG_PUBLISH_TYPE = 0x03; + internal const Byte MQTT_MSG_PUBACK_TYPE = 0x04; + internal const Byte MQTT_MSG_PUBREC_TYPE = 0x05; + internal const Byte MQTT_MSG_PUBREL_TYPE = 0x06; + internal const Byte MQTT_MSG_PUBCOMP_TYPE = 0x07; + internal const Byte MQTT_MSG_SUBSCRIBE_TYPE = 0x08; + internal const Byte MQTT_MSG_SUBACK_TYPE = 0x09; + internal const Byte MQTT_MSG_UNSUBSCRIBE_TYPE = 0x0A; + internal const Byte MQTT_MSG_UNSUBACK_TYPE = 0x0B; + internal const Byte MQTT_MSG_PINGREQ_TYPE = 0x0C; + internal const Byte MQTT_MSG_PINGRESP_TYPE = 0x0D; + internal const Byte MQTT_MSG_DISCONNECT_TYPE = 0x0E; + + // [v3.1.1] MQTT flag bits + internal const Byte MQTT_MSG_CONNECT_FLAG_BITS = 0x00; + internal const Byte MQTT_MSG_CONNACK_FLAG_BITS = 0x00; + internal const Byte MQTT_MSG_PUBLISH_FLAG_BITS = 0x00; // just defined as 0x00 but depends on publish props (dup, qos, retain) + internal const Byte MQTT_MSG_PUBACK_FLAG_BITS = 0x00; + internal const Byte MQTT_MSG_PUBREC_FLAG_BITS = 0x00; + internal const Byte MQTT_MSG_PUBREL_FLAG_BITS = 0x02; + internal const Byte MQTT_MSG_PUBCOMP_FLAG_BITS = 0x00; + internal const Byte MQTT_MSG_SUBSCRIBE_FLAG_BITS = 0x02; + internal const Byte MQTT_MSG_SUBACK_FLAG_BITS = 0x00; + internal const Byte MQTT_MSG_UNSUBSCRIBE_FLAG_BITS = 0x02; + internal const Byte MQTT_MSG_UNSUBACK_FLAG_BITS = 0x00; + internal const Byte MQTT_MSG_PINGREQ_FLAG_BITS = 0x00; + internal const Byte MQTT_MSG_PINGRESP_FLAG_BITS = 0x00; + internal const Byte MQTT_MSG_DISCONNECT_FLAG_BITS = 0x00; + + // QOS levels + public const Byte QOS_LEVEL_AT_MOST_ONCE = 0x00; + public const Byte QOS_LEVEL_AT_LEAST_ONCE = 0x01; + public const Byte QOS_LEVEL_EXACTLY_ONCE = 0x02; + + // SUBSCRIBE QoS level granted failure [v3.1.1] + public const Byte QOS_LEVEL_GRANTED_FAILURE = 0x80; + + internal const UInt16 MAX_TOPIC_LENGTH = 65535; + internal const UInt16 MIN_TOPIC_LENGTH = 1; + internal const Byte MESSAGE_ID_SIZE = 2; + + #endregion + + #region Properties... + /// - /// Base class for all MQTT messages + /// Message type /// - public abstract class MqttMsgBase - { - #region Constants... - - // mask, offset and size for fixed header fields - internal const byte MSG_TYPE_MASK = 0xF0; - internal const byte MSG_TYPE_OFFSET = 0x04; - internal const byte MSG_TYPE_SIZE = 0x04; - internal const byte MSG_FLAG_BITS_MASK = 0x0F; // [v3.1.1] - internal const byte MSG_FLAG_BITS_OFFSET = 0x00; // [v3.1.1] - internal const byte MSG_FLAG_BITS_SIZE = 0x04; // [v3.1.1] - internal const byte DUP_FLAG_MASK = 0x08; - internal const byte DUP_FLAG_OFFSET = 0x03; - internal const byte DUP_FLAG_SIZE = 0x01; - internal const byte QOS_LEVEL_MASK = 0x06; - internal const byte QOS_LEVEL_OFFSET = 0x01; - internal const byte QOS_LEVEL_SIZE = 0x02; - internal const byte RETAIN_FLAG_MASK = 0x01; - internal const byte RETAIN_FLAG_OFFSET = 0x00; - internal const byte RETAIN_FLAG_SIZE = 0x01; - - // MQTT message types - internal const byte MQTT_MSG_CONNECT_TYPE = 0x01; - internal const byte MQTT_MSG_CONNACK_TYPE = 0x02; - internal const byte MQTT_MSG_PUBLISH_TYPE = 0x03; - internal const byte MQTT_MSG_PUBACK_TYPE = 0x04; - internal const byte MQTT_MSG_PUBREC_TYPE = 0x05; - internal const byte MQTT_MSG_PUBREL_TYPE = 0x06; - internal const byte MQTT_MSG_PUBCOMP_TYPE = 0x07; - internal const byte MQTT_MSG_SUBSCRIBE_TYPE = 0x08; - internal const byte MQTT_MSG_SUBACK_TYPE = 0x09; - internal const byte MQTT_MSG_UNSUBSCRIBE_TYPE = 0x0A; - internal const byte MQTT_MSG_UNSUBACK_TYPE = 0x0B; - internal const byte MQTT_MSG_PINGREQ_TYPE = 0x0C; - internal const byte MQTT_MSG_PINGRESP_TYPE = 0x0D; - internal const byte MQTT_MSG_DISCONNECT_TYPE = 0x0E; - - // [v3.1.1] MQTT flag bits - internal const byte MQTT_MSG_CONNECT_FLAG_BITS = 0x00; - internal const byte MQTT_MSG_CONNACK_FLAG_BITS = 0x00; - internal const byte MQTT_MSG_PUBLISH_FLAG_BITS = 0x00; // just defined as 0x00 but depends on publish props (dup, qos, retain) - internal const byte MQTT_MSG_PUBACK_FLAG_BITS = 0x00; - internal const byte MQTT_MSG_PUBREC_FLAG_BITS = 0x00; - internal const byte MQTT_MSG_PUBREL_FLAG_BITS = 0x02; - internal const byte MQTT_MSG_PUBCOMP_FLAG_BITS = 0x00; - internal const byte MQTT_MSG_SUBSCRIBE_FLAG_BITS = 0x02; - internal const byte MQTT_MSG_SUBACK_FLAG_BITS = 0x00; - internal const byte MQTT_MSG_UNSUBSCRIBE_FLAG_BITS = 0x02; - internal const byte MQTT_MSG_UNSUBACK_FLAG_BITS = 0x00; - internal const byte MQTT_MSG_PINGREQ_FLAG_BITS = 0x00; - internal const byte MQTT_MSG_PINGRESP_FLAG_BITS = 0x00; - internal const byte MQTT_MSG_DISCONNECT_FLAG_BITS = 0x00; - - // QOS levels - public const byte QOS_LEVEL_AT_MOST_ONCE = 0x00; - public const byte QOS_LEVEL_AT_LEAST_ONCE = 0x01; - public const byte QOS_LEVEL_EXACTLY_ONCE = 0x02; - - // SUBSCRIBE QoS level granted failure [v3.1.1] - public const byte QOS_LEVEL_GRANTED_FAILURE = 0x80; - - internal const ushort MAX_TOPIC_LENGTH = 65535; - internal const ushort MIN_TOPIC_LENGTH = 1; - internal const byte MESSAGE_ID_SIZE = 2; - - #endregion - - #region Properties... - - /// - /// Message type - /// - public byte Type - { - get { return this.type; } - set { this.type = value; } - } - - /// - /// Duplicate message flag - /// - public bool DupFlag - { - get { return this.dupFlag; } - set { this.dupFlag = value; } - } - - /// - /// Quality of Service level - /// - public byte QosLevel - { - get { return this.qosLevel; } - set { this.qosLevel = value; } - } - - /// - /// Retain message flag - /// - public bool Retain - { - get { return this.retain; } - set { this.retain = value; } - } - - /// - /// Message identifier for the message - /// - public ushort MessageId - { - get { return this.messageId; } - set { this.messageId = value; } - } - - #endregion - - // message type - protected byte type; - // duplicate delivery - protected bool dupFlag; - // quality of service level - protected byte qosLevel; - // retain flag - protected bool retain; - // message identifier - protected ushort messageId; - - /// - /// Returns message bytes rapresentation - /// - /// Protocol version - /// Bytes rapresentation - public abstract byte[] GetBytes(byte protocolVersion); - - /// - /// Encode remaining length and insert it into message buffer - /// - /// Remaining length value to encode - /// Message buffer for inserting encoded value - /// Index from which insert encoded value into buffer - /// Index updated - protected int encodeRemainingLength(int remainingLength, byte[] buffer, int index) - { - int digit = 0; - do - { - digit = remainingLength % 128; - remainingLength /= 128; - if (remainingLength > 0) - digit = digit | 0x80; - buffer[index++] = (byte)digit; - } while (remainingLength > 0); - return index; - } - - /// - /// Decode remaining length reading bytes from socket - /// - /// Channel from reading bytes - /// Decoded remaining length - protected static int decodeRemainingLength(IMqttNetworkChannel channel) - { - int multiplier = 1; - int value = 0; - int digit = 0; - byte[] nextByte = new byte[1]; - do - { - // next digit from stream - channel.Receive(nextByte); - digit = nextByte[0]; - value += ((digit & 127) * multiplier); - multiplier *= 128; - } while ((digit & 128) != 0); - return value; - } - + public Byte Type { get; set; } + + /// + /// Duplicate message flag + /// + public Boolean DupFlag { get; set; } + + /// + /// Quality of Service level + /// + public Byte QosLevel { get; set; } + + /// + /// Retain message flag + /// + public Boolean Retain { get; set; } + + /// + /// Message identifier for the message + /// + public UInt16 MessageId { get; set; } + + #endregion + + // message type + // duplicate delivery + // quality of service level + // retain flag + // message identifier + + /// + /// Returns message bytes rapresentation + /// + /// Protocol version + /// Bytes rapresentation + public abstract Byte[] GetBytes(Byte protocolVersion); + + /// + /// Encode remaining length and insert it into message buffer + /// + /// Remaining length value to encode + /// Message buffer for inserting encoded value + /// Index from which insert encoded value into buffer + /// Index updated + protected Int32 EncodeRemainingLength(Int32 remainingLength, Byte[] buffer, Int32 index) { + do { + Int32 digit = remainingLength % 128; + remainingLength /= 128; + if (remainingLength > 0) { + digit |= 0x80; + } + + buffer[index++] = (Byte)digit; + } while (remainingLength > 0); + return index; + } + + /// + /// Decode remaining length reading bytes from socket + /// + /// Channel from reading bytes + /// Decoded remaining length + protected static Int32 DecodeRemainingLength(IMqttNetworkChannel channel) { + Int32 multiplier = 1; + Int32 value = 0; + Byte[] nextByte = new Byte[1]; + Int32 digit; + do { + // next digit from stream + _ = channel.Receive(nextByte); + digit = nextByte[0]; + value += (digit & 127) * multiplier; + multiplier *= 128; + } while ((digit & 128) != 0); + return value; + } + #if TRACE - /// - /// Returns a string representation of the message for tracing - /// - /// Message name - /// Message fields name - /// Message fields value - /// String representation of the message - protected string GetTraceString(string name, object[] fieldNames, object[] fieldValues) - { - StringBuilder sb = new StringBuilder(); - sb.Append(name); - - if ((fieldNames != null) && (fieldValues != null)) - { - sb.Append("("); - bool addComma = false; - for (int i = 0; i < fieldValues.Length; i++) - { - if (fieldValues[i] != null) - { - if (addComma) - { - sb.Append(","); - } - - sb.Append(fieldNames[i]); - sb.Append(":"); - sb.Append(GetStringObject(fieldValues[i])); - addComma = true; - } - } - sb.Append(")"); - } - - return sb.ToString(); - } - - object GetStringObject(object value) - { - byte[] binary = value as byte[]; - if (binary != null) - { - string hexChars = "0123456789ABCDEF"; - StringBuilder sb = new StringBuilder(binary.Length * 2); - for (int i = 0; i < binary.Length; ++i) - { - sb.Append(hexChars[binary[i] >> 4]); - sb.Append(hexChars[binary[i] & 0x0F]); - } - - return sb.ToString(); - } - - object[] list = value as object[]; - if (list != null) - { - StringBuilder sb = new StringBuilder(); - sb.Append('['); - for (int i = 0; i < list.Length; ++i) - { - if (i > 0) sb.Append(','); - sb.Append(list[i]); - } - sb.Append(']'); - - return sb.ToString(); - } - - return value; - } + /// + /// Returns a string representation of the message for tracing + /// + /// Message name + /// Message fields name + /// Message fields value + /// String representation of the message + protected String GetTraceString(String name, Object[] fieldNames, Object[] fieldValues) { + StringBuilder sb = new StringBuilder(); + _ = sb.Append(name); + + if (fieldNames != null && fieldValues != null) { + _ = sb.Append("("); + Boolean addComma = false; + for (Int32 i = 0; i < fieldValues.Length; i++) { + if (fieldValues[i] != null) { + if (addComma) { + _ = sb.Append(","); + } + + _ = sb.Append(fieldNames[i]); + _ = sb.Append(":"); + _ = sb.Append(this.GetStringObject(fieldValues[i])); + addComma = true; + } + } + _ = sb.Append(")"); + } + + return sb.ToString(); + } + + Object GetStringObject(Object value) { + if (value is Byte[] binary) { + String hexChars = "0123456789ABCDEF"; + StringBuilder sb = new StringBuilder(binary.Length * 2); + for (Int32 i = 0; i < binary.Length; ++i) { + _ = sb.Append(hexChars[binary[i] >> 4]); + _ = sb.Append(hexChars[binary[i] & 0x0F]); + } + + return sb.ToString(); + } + + if (value is Object[] list) { + StringBuilder sb = new StringBuilder(); + _ = sb.Append('['); + for (Int32 i = 0; i < list.Length; ++i) { + if (i > 0) { + _ = sb.Append(','); + } + + _ = sb.Append(list[i]); + } + _ = sb.Append(']'); + + return sb.ToString(); + } + + return value; + } #endif - } + } } diff --git a/M2Mqtt/Messages/MqttMsgConnack.cs b/M2Mqtt/Messages/MqttMsgConnack.cs index ae5a342..f0e70df 100644 --- a/M2Mqtt/Messages/MqttMsgConnack.cs +++ b/M2Mqtt/Messages/MqttMsgConnack.cs @@ -17,175 +17,153 @@ Contributors: using System; using uPLibrary.Networking.M2Mqtt.Exceptions; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Class for CONNACK message from broker to client + /// + public class MqttMsgConnack : MqttMsgBase { + #region Constants... + + // return codes for CONNACK message + public const Byte CONN_ACCEPTED = 0x00; + public const Byte CONN_REFUSED_PROT_VERS = 0x01; + public const Byte CONN_REFUSED_IDENT_REJECTED = 0x02; + public const Byte CONN_REFUSED_SERVER_UNAVAILABLE = 0x03; + public const Byte CONN_REFUSED_USERNAME_PASSWORD = 0x04; + public const Byte CONN_REFUSED_NOT_AUTHORIZED = 0x05; + + //private const Byte TOPIC_NAME_COMP_RESP_BYTE_OFFSET = 0; + private const Byte TOPIC_NAME_COMP_RESP_BYTE_SIZE = 1; + // [v3.1.1] connect acknowledge flags replace "old" topic name compression respone (not used in 3.1) + private const Byte CONN_ACK_FLAGS_BYTE_OFFSET = 0; + private const Byte CONN_ACK_FLAGS_BYTE_SIZE = 1; + // [v3.1.1] session present flag + private const Byte SESSION_PRESENT_FLAG_MASK = 0x01; + private const Byte SESSION_PRESENT_FLAG_OFFSET = 0x00; + //private const Byte SESSION_PRESENT_FLAG_SIZE = 0x01; + private const Byte CONN_RETURN_CODE_BYTE_OFFSET = 1; + private const Byte CONN_RETURN_CODE_BYTE_SIZE = 1; + + #endregion + + #region Properties... + + // [v3.1.1] session present flag /// - /// Class for CONNACK message from broker to client + /// Session present flag /// - public class MqttMsgConnack : MqttMsgBase - { - #region Constants... - - // return codes for CONNACK message - public const byte CONN_ACCEPTED = 0x00; - public const byte CONN_REFUSED_PROT_VERS = 0x01; - public const byte CONN_REFUSED_IDENT_REJECTED = 0x02; - public const byte CONN_REFUSED_SERVER_UNAVAILABLE = 0x03; - public const byte CONN_REFUSED_USERNAME_PASSWORD = 0x04; - public const byte CONN_REFUSED_NOT_AUTHORIZED = 0x05; - - private const byte TOPIC_NAME_COMP_RESP_BYTE_OFFSET = 0; - private const byte TOPIC_NAME_COMP_RESP_BYTE_SIZE = 1; - // [v3.1.1] connect acknowledge flags replace "old" topic name compression respone (not used in 3.1) - private const byte CONN_ACK_FLAGS_BYTE_OFFSET = 0; - private const byte CONN_ACK_FLAGS_BYTE_SIZE = 1; - // [v3.1.1] session present flag - private const byte SESSION_PRESENT_FLAG_MASK = 0x01; - private const byte SESSION_PRESENT_FLAG_OFFSET = 0x00; - private const byte SESSION_PRESENT_FLAG_SIZE = 0x01; - private const byte CONN_RETURN_CODE_BYTE_OFFSET = 1; - private const byte CONN_RETURN_CODE_BYTE_SIZE = 1; - - #endregion - - #region Properties... - - // [v3.1.1] session present flag - /// - /// Session present flag - /// - public bool SessionPresent - { - get { return this.sessionPresent; } - set { this.sessionPresent = value; } - } - - /// - /// Return Code - /// - public byte ReturnCode - { - get { return this.returnCode; } - set { this.returnCode = value; } - } - - #endregion - - // [v3.1.1] session present flag - private bool sessionPresent; - - // return code for CONNACK message - private byte returnCode; - - /// - /// Constructor - /// - public MqttMsgConnack() - { - this.type = MQTT_MSG_CONNACK_TYPE; - } - - /// - /// Parse bytes for a CONNACK message - /// - /// First fixed header byte - /// Protocol Version - /// Channel connected to the broker - /// CONNACK message instance - public static MqttMsgConnack Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel) - { - byte[] buffer; - MqttMsgConnack msg = new MqttMsgConnack(); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - { - // [v3.1.1] check flag bits - if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_CONNACK_FLAG_BITS) - throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); - } - - // get remaining length and allocate buffer - int remainingLength = MqttMsgBase.decodeRemainingLength(channel); - buffer = new byte[remainingLength]; - - // read bytes from socket... - channel.Receive(buffer); - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - { - // [v3.1.1] ... set session present flag ... - msg.sessionPresent = (buffer[CONN_ACK_FLAGS_BYTE_OFFSET] & SESSION_PRESENT_FLAG_MASK) != 0x00; - } - // ...and set return code from broker - msg.returnCode = buffer[CONN_RETURN_CODE_BYTE_OFFSET]; - - return msg; - } - - public override byte[] GetBytes(byte ProtocolVersion) - { - int fixedHeaderSize = 0; - int varHeaderSize = 0; - int payloadSize = 0; - int remainingLength = 0; - byte[] buffer; - int index = 0; - - if (ProtocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - // flags byte and connect return code - varHeaderSize += (CONN_ACK_FLAGS_BYTE_SIZE + CONN_RETURN_CODE_BYTE_SIZE); - else - // topic name compression response and connect return code - varHeaderSize += (TOPIC_NAME_COMP_RESP_BYTE_SIZE + CONN_RETURN_CODE_BYTE_SIZE); - - remainingLength += (varHeaderSize + payloadSize); - - // first byte of fixed header - fixedHeaderSize = 1; - - int temp = remainingLength; - // increase fixed header size based on remaining length - // (each remaining length byte can encode until 128) - do - { - fixedHeaderSize++; - temp = temp / 128; - } while (temp > 0); - - // allocate buffer for message - buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize]; - - // first fixed header byte - if (ProtocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - buffer[index++] = (MQTT_MSG_CONNACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_CONNACK_FLAG_BITS; // [v.3.1.1] - else - buffer[index++] = (byte)(MQTT_MSG_CONNACK_TYPE << MSG_TYPE_OFFSET); - - // encode remaining length - index = this.encodeRemainingLength(remainingLength, buffer, index); - - if (ProtocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - // [v3.1.1] session present flag - buffer[index++] = this.sessionPresent ? (byte)(1 << SESSION_PRESENT_FLAG_OFFSET) : (byte)0x00; - else - // topic name compression response (reserved values. not used); - buffer[index++] = 0x00; - - // connect return code - buffer[index++] = this.returnCode; - - return buffer; - } - - public override string ToString() - { + public Boolean SessionPresent { get; set; } + + /// + /// Return Code + /// + public Byte ReturnCode { get; set; } + + #endregion + + // [v3.1.1] session present flag + + /// + /// Constructor + /// + public MqttMsgConnack() => this.Type = MQTT_MSG_CONNACK_TYPE; + + /// + /// Parse bytes for a CONNACK message + /// + /// First fixed header byte + /// Protocol Version + /// Channel connected to the broker + /// CONNACK message instance + public static MqttMsgConnack Parse(Byte fixedHeaderFirstByte, Byte protocolVersion, IMqttNetworkChannel channel) { + Byte[] buffer; + MqttMsgConnack msg = new MqttMsgConnack(); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] check flag bits + if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_CONNACK_FLAG_BITS) { + throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); + } + } + + // get remaining length and allocate buffer + Int32 remainingLength = MqttMsgBase.DecodeRemainingLength(channel); + buffer = new Byte[remainingLength]; + + // read bytes from socket... + _ = channel.Receive(buffer); + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] ... set session present flag ... + msg.SessionPresent = (buffer[CONN_ACK_FLAGS_BYTE_OFFSET] & SESSION_PRESENT_FLAG_MASK) != 0x00; + } + // ...and set return code from broker + msg.ReturnCode = buffer[CONN_RETURN_CODE_BYTE_OFFSET]; + + return msg; + } + + public override Byte[] GetBytes(Byte ProtocolVersion) { + Int32 varHeaderSize = 0; + Int32 payloadSize = 0; + Int32 remainingLength = 0; + Byte[] buffer; + Int32 index = 0; + + if (ProtocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // flags byte and connect return code + varHeaderSize += CONN_ACK_FLAGS_BYTE_SIZE + CONN_RETURN_CODE_BYTE_SIZE; + } else { + // topic name compression response and connect return code + varHeaderSize += TOPIC_NAME_COMP_RESP_BYTE_SIZE + CONN_RETURN_CODE_BYTE_SIZE; + } + + remainingLength += varHeaderSize + payloadSize; + + // first byte of fixed header + Int32 fixedHeaderSize = 1; + + Int32 temp = remainingLength; + // increase fixed header size based on remaining length + // (each remaining length byte can encode until 128) + do { + fixedHeaderSize++; + temp /= 128; + } while (temp > 0); + + // allocate buffer for message + buffer = new Byte[fixedHeaderSize + varHeaderSize + payloadSize]; + + // first fixed header byte + buffer[index++] = ProtocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1 + ? (Byte)((MQTT_MSG_CONNACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_CONNACK_FLAG_BITS) + : (Byte)(MQTT_MSG_CONNACK_TYPE << MSG_TYPE_OFFSET); + + // encode remaining length + index = this.EncodeRemainingLength(remainingLength, buffer, index); + + if (ProtocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] session present flag + buffer[index++] = this.SessionPresent ? (Byte)(1 << SESSION_PRESENT_FLAG_OFFSET) : (Byte)0x00; + } else { + // topic name compression response (reserved values. not used); + buffer[index++] = 0x00; + } + + // connect return code + buffer[index++] = this.ReturnCode; + + return buffer; + } + + public override String ToString() => #if TRACE - return this.GetTraceString( - "CONNACK", - new object[] { "returnCode" }, - new object[] { this.returnCode }); + this.GetTraceString( + "CONNACK", + new Object[] { "returnCode" }, + new Object[] { this.ReturnCode }); #else - return base.ToString(); + base.ToString(); #endif - } - } + + } } diff --git a/M2Mqtt/Messages/MqttMsgConnect.cs b/M2Mqtt/Messages/MqttMsgConnect.cs index 382cf83..8a11bb6 100644 --- a/M2Mqtt/Messages/MqttMsgConnect.cs +++ b/M2Mqtt/Messages/MqttMsgConnect.cs @@ -18,565 +18,481 @@ using System; using System.Text; using uPLibrary.Networking.M2Mqtt.Exceptions; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Class for CONNECT message from client to broker + /// + public class MqttMsgConnect : MqttMsgBase { + #region Constants... + + // protocol name supported + internal const String PROTOCOL_NAME_V3_1 = "MQIsdp"; + internal const String PROTOCOL_NAME_V3_1_1 = "MQTT"; // [v.3.1.1] + + // max length for client id (removed in 3.1.1) + internal const Int32 CLIENT_ID_MAX_LENGTH = 23; + + // variable header fields + internal const Byte PROTOCOL_NAME_LEN_SIZE = 2; + internal const Byte PROTOCOL_NAME_V3_1_SIZE = 6; + internal const Byte PROTOCOL_NAME_V3_1_1_SIZE = 4; // [v.3.1.1] + internal const Byte PROTOCOL_VERSION_SIZE = 1; + internal const Byte CONNECT_FLAGS_SIZE = 1; + internal const Byte KEEP_ALIVE_TIME_SIZE = 2; + + internal const Byte PROTOCOL_VERSION_V3_1 = 0x03; + internal const Byte PROTOCOL_VERSION_V3_1_1 = 0x04; // [v.3.1.1] + internal const UInt16 KEEP_ALIVE_PERIOD_DEFAULT = 60; // seconds + internal const UInt16 MAX_KEEP_ALIVE = 65535; // 16 bit + + // connect flags + internal const Byte USERNAME_FLAG_MASK = 0x80; + internal const Byte USERNAME_FLAG_OFFSET = 0x07; + internal const Byte USERNAME_FLAG_SIZE = 0x01; + internal const Byte PASSWORD_FLAG_MASK = 0x40; + internal const Byte PASSWORD_FLAG_OFFSET = 0x06; + internal const Byte PASSWORD_FLAG_SIZE = 0x01; + internal const Byte WILL_RETAIN_FLAG_MASK = 0x20; + internal const Byte WILL_RETAIN_FLAG_OFFSET = 0x05; + internal const Byte WILL_RETAIN_FLAG_SIZE = 0x01; + internal const Byte WILL_QOS_FLAG_MASK = 0x18; + internal const Byte WILL_QOS_FLAG_OFFSET = 0x03; + internal const Byte WILL_QOS_FLAG_SIZE = 0x02; + internal const Byte WILL_FLAG_MASK = 0x04; + internal const Byte WILL_FLAG_OFFSET = 0x02; + internal const Byte WILL_FLAG_SIZE = 0x01; + internal const Byte CLEAN_SESSION_FLAG_MASK = 0x02; + internal const Byte CLEAN_SESSION_FLAG_OFFSET = 0x01; + internal const Byte CLEAN_SESSION_FLAG_SIZE = 0x01; + // [v.3.1.1] lsb (reserved) must be now 0 + internal const Byte RESERVED_FLAG_MASK = 0x01; + internal const Byte RESERVED_FLAG_OFFSET = 0x00; + internal const Byte RESERVED_FLAG_SIZE = 0x01; + + #endregion + + #region Properties... + /// - /// Class for CONNECT message from client to broker + /// Protocol name /// - public class MqttMsgConnect : MqttMsgBase - { - #region Constants... - - // protocol name supported - internal const string PROTOCOL_NAME_V3_1 = "MQIsdp"; - internal const string PROTOCOL_NAME_V3_1_1 = "MQTT"; // [v.3.1.1] - - // max length for client id (removed in 3.1.1) - internal const int CLIENT_ID_MAX_LENGTH = 23; - - // variable header fields - internal const byte PROTOCOL_NAME_LEN_SIZE = 2; - internal const byte PROTOCOL_NAME_V3_1_SIZE = 6; - internal const byte PROTOCOL_NAME_V3_1_1_SIZE = 4; // [v.3.1.1] - internal const byte PROTOCOL_VERSION_SIZE = 1; - internal const byte CONNECT_FLAGS_SIZE = 1; - internal const byte KEEP_ALIVE_TIME_SIZE = 2; - - internal const byte PROTOCOL_VERSION_V3_1 = 0x03; - internal const byte PROTOCOL_VERSION_V3_1_1 = 0x04; // [v.3.1.1] - internal const ushort KEEP_ALIVE_PERIOD_DEFAULT = 60; // seconds - internal const ushort MAX_KEEP_ALIVE = 65535; // 16 bit - - // connect flags - internal const byte USERNAME_FLAG_MASK = 0x80; - internal const byte USERNAME_FLAG_OFFSET = 0x07; - internal const byte USERNAME_FLAG_SIZE = 0x01; - internal const byte PASSWORD_FLAG_MASK = 0x40; - internal const byte PASSWORD_FLAG_OFFSET = 0x06; - internal const byte PASSWORD_FLAG_SIZE = 0x01; - internal const byte WILL_RETAIN_FLAG_MASK = 0x20; - internal const byte WILL_RETAIN_FLAG_OFFSET = 0x05; - internal const byte WILL_RETAIN_FLAG_SIZE = 0x01; - internal const byte WILL_QOS_FLAG_MASK = 0x18; - internal const byte WILL_QOS_FLAG_OFFSET = 0x03; - internal const byte WILL_QOS_FLAG_SIZE = 0x02; - internal const byte WILL_FLAG_MASK = 0x04; - internal const byte WILL_FLAG_OFFSET = 0x02; - internal const byte WILL_FLAG_SIZE = 0x01; - internal const byte CLEAN_SESSION_FLAG_MASK = 0x02; - internal const byte CLEAN_SESSION_FLAG_OFFSET = 0x01; - internal const byte CLEAN_SESSION_FLAG_SIZE = 0x01; - // [v.3.1.1] lsb (reserved) must be now 0 - internal const byte RESERVED_FLAG_MASK = 0x01; - internal const byte RESERVED_FLAG_OFFSET = 0x00; - internal const byte RESERVED_FLAG_SIZE = 0x01; - - #endregion - - #region Properties... - - /// - /// Protocol name - /// - public string ProtocolName - { - get { return this.protocolName; } - set { this.protocolName = value; } - } - - /// - /// Protocol version - /// - public byte ProtocolVersion - { - get { return this.protocolVersion; } - set { this.protocolVersion = value; } - } - - /// - /// Client identifier - /// - public string ClientId - { - get { return this.clientId; } - set { this.clientId = value; } - } - - /// - /// Will retain flag - /// - public bool WillRetain - { - get { return this.willRetain; } - set { this.willRetain = value; } - } - - /// - /// Will QOS level - /// - public byte WillQosLevel - { - get { return this.willQosLevel; } - set { this.willQosLevel = value; } - } - - /// - /// Will flag - /// - public bool WillFlag - { - get { return this.willFlag; } - set { this.willFlag = value; } - } - - /// - /// Will topic - /// - public string WillTopic - { - get { return this.willTopic; } - set { this.willTopic = value; } - } - - /// - /// Will message - /// - public string WillMessage - { - get { return this.willMessage; } - set { this.willMessage = value; } - } - - /// - /// Username - /// - public string Username - { - get { return this.username; } - set { this.username = value; } - } - - /// - /// Password - /// - public string Password - { - get { return this.password; } - set { this.password = value; } - } - - /// - /// Clean session flag - /// - public bool CleanSession - { - get { return this.cleanSession; } - set { this.cleanSession = value; } - } - - /// - /// Keep alive period - /// - public ushort KeepAlivePeriod - { - get { return this.keepAlivePeriod; } - set { this.keepAlivePeriod = value; } - } - - #endregion - - // protocol name - private string protocolName; - // protocol version - private byte protocolVersion; - // client identifier - private string clientId; - // will retain flag - protected bool willRetain; - // will quality of service level - protected byte willQosLevel; - // will flag - private bool willFlag; - // will topic - private string willTopic; - // will message - private string willMessage; - // username - private string username; - // password - private string password; - // clean session flag - private bool cleanSession; - // keep alive period (in sec) - private ushort keepAlivePeriod; - - /// - /// Constructor - /// - public MqttMsgConnect() - { - this.type = MQTT_MSG_CONNECT_TYPE; - } - - /// - /// Constructor - /// - /// Client identifier - public MqttMsgConnect(string clientId) : - this(clientId, null, null, false, QOS_LEVEL_AT_LEAST_ONCE, false, null, null, true, KEEP_ALIVE_PERIOD_DEFAULT, PROTOCOL_VERSION_V3_1_1) - { - } - - /// - /// Constructor - /// - /// Client identifier - /// Username - /// Password - /// Will retain flag - /// Will QOS level - /// Will flag - /// Will topic - /// Will message - /// Clean sessione flag - /// Keep alive period - /// Protocol version - public MqttMsgConnect(string clientId, - string username, - string password, - bool willRetain, - byte willQosLevel, - bool willFlag, - string willTopic, - string willMessage, - bool cleanSession, - ushort keepAlivePeriod, - byte protocolVersion - ) - { - this.type = MQTT_MSG_CONNECT_TYPE; - - this.clientId = clientId; - this.username = username; - this.password = password; - this.willRetain = willRetain; - this.willQosLevel = willQosLevel; - this.willFlag = willFlag; - this.willTopic = willTopic; - this.willMessage = willMessage; - this.cleanSession = cleanSession; - this.keepAlivePeriod = keepAlivePeriod; - // [v.3.1.1] added new protocol name and version - this.protocolVersion = protocolVersion; - this.protocolName = (this.protocolVersion == PROTOCOL_VERSION_V3_1_1) ? PROTOCOL_NAME_V3_1_1 : PROTOCOL_NAME_V3_1; - } - - /// - /// Parse bytes for a CONNECT message - /// - /// First fixed header byte - /// Protocol Version - /// Channel connected to the broker - /// CONNECT message instance - public static MqttMsgConnect Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel) - { - byte[] buffer; - int index = 0; - int protNameUtf8Length; - byte[] protNameUtf8; - bool isUsernameFlag; - bool isPasswordFlag; - int clientIdUtf8Length; - byte[] clientIdUtf8; - int willTopicUtf8Length; - byte[] willTopicUtf8; - int willMessageUtf8Length; - byte[] willMessageUtf8; - int usernameUtf8Length; - byte[] usernameUtf8; - int passwordUtf8Length; - byte[] passwordUtf8; - MqttMsgConnect msg = new MqttMsgConnect(); - - // get remaining length and allocate buffer - int remainingLength = MqttMsgBase.decodeRemainingLength(channel); - buffer = new byte[remainingLength]; - - // read bytes from socket... - channel.Receive(buffer); - - // protocol name - protNameUtf8Length = ((buffer[index++] << 8) & 0xFF00); - protNameUtf8Length |= buffer[index++]; - protNameUtf8 = new byte[protNameUtf8Length]; - Array.Copy(buffer, index, protNameUtf8, 0, protNameUtf8Length); - index += protNameUtf8Length; - msg.protocolName = new String(Encoding.UTF8.GetChars(protNameUtf8)); - - // [v3.1.1] wrong protocol name - if (!msg.protocolName.Equals(PROTOCOL_NAME_V3_1) && !msg.protocolName.Equals(PROTOCOL_NAME_V3_1_1)) - throw new MqttClientException(MqttClientErrorCode.InvalidProtocolName); - - // protocol version - msg.protocolVersion = buffer[index]; - index += PROTOCOL_VERSION_SIZE; - - // connect flags - // [v3.1.1] check lsb (reserved) must be 0 - if ((msg.protocolVersion == PROTOCOL_VERSION_V3_1_1) && - ((buffer[index] & RESERVED_FLAG_MASK) != 0x00)) - throw new MqttClientException(MqttClientErrorCode.InvalidConnectFlags); - - isUsernameFlag = (buffer[index] & USERNAME_FLAG_MASK) != 0x00; - isPasswordFlag = (buffer[index] & PASSWORD_FLAG_MASK) != 0x00; - msg.willRetain = (buffer[index] & WILL_RETAIN_FLAG_MASK) != 0x00; - msg.willQosLevel = (byte)((buffer[index] & WILL_QOS_FLAG_MASK) >> WILL_QOS_FLAG_OFFSET); - msg.willFlag = (buffer[index] & WILL_FLAG_MASK) != 0x00; - msg.cleanSession = (buffer[index] & CLEAN_SESSION_FLAG_MASK) != 0x00; - index += CONNECT_FLAGS_SIZE; - - // keep alive timer - msg.keepAlivePeriod = (ushort)((buffer[index++] << 8) & 0xFF00); - msg.keepAlivePeriod |= buffer[index++]; - - // client identifier [v3.1.1] it may be zero bytes long (empty string) - clientIdUtf8Length = ((buffer[index++] << 8) & 0xFF00); - clientIdUtf8Length |= buffer[index++]; - clientIdUtf8 = new byte[clientIdUtf8Length]; - Array.Copy(buffer, index, clientIdUtf8, 0, clientIdUtf8Length); - index += clientIdUtf8Length; - msg.clientId = new String(Encoding.UTF8.GetChars(clientIdUtf8)); - // [v3.1.1] if client identifier is zero bytes long, clean session must be true - if ((msg.protocolVersion == PROTOCOL_VERSION_V3_1_1) && (clientIdUtf8Length == 0) && (!msg.cleanSession)) - throw new MqttClientException(MqttClientErrorCode.InvalidClientId); - - // will topic and will message - if (msg.willFlag) - { - willTopicUtf8Length = ((buffer[index++] << 8) & 0xFF00); - willTopicUtf8Length |= buffer[index++]; - willTopicUtf8 = new byte[willTopicUtf8Length]; - Array.Copy(buffer, index, willTopicUtf8, 0, willTopicUtf8Length); - index += willTopicUtf8Length; - msg.willTopic = new String(Encoding.UTF8.GetChars(willTopicUtf8)); - - willMessageUtf8Length = ((buffer[index++] << 8) & 0xFF00); - willMessageUtf8Length |= buffer[index++]; - willMessageUtf8 = new byte[willMessageUtf8Length]; - Array.Copy(buffer, index, willMessageUtf8, 0, willMessageUtf8Length); - index += willMessageUtf8Length; - msg.willMessage = new String(Encoding.UTF8.GetChars(willMessageUtf8)); - } - - // username - if (isUsernameFlag) - { - usernameUtf8Length = ((buffer[index++] << 8) & 0xFF00); - usernameUtf8Length |= buffer[index++]; - usernameUtf8 = new byte[usernameUtf8Length]; - Array.Copy(buffer, index, usernameUtf8, 0, usernameUtf8Length); - index += usernameUtf8Length; - msg.username = new String(Encoding.UTF8.GetChars(usernameUtf8)); - } - - // password - if (isPasswordFlag) - { - passwordUtf8Length = ((buffer[index++] << 8) & 0xFF00); - passwordUtf8Length |= buffer[index++]; - passwordUtf8 = new byte[passwordUtf8Length]; - Array.Copy(buffer, index, passwordUtf8, 0, passwordUtf8Length); - index += passwordUtf8Length; - msg.password = new String(Encoding.UTF8.GetChars(passwordUtf8)); - } - - return msg; - } - - public override byte[] GetBytes(byte protocolVersion) - { - int fixedHeaderSize = 0; - int varHeaderSize = 0; - int payloadSize = 0; - int remainingLength = 0; - byte[] buffer; - int index = 0; - - byte[] clientIdUtf8 = Encoding.UTF8.GetBytes(this.clientId); - byte[] willTopicUtf8 = (this.willFlag && (this.willTopic != null)) ? Encoding.UTF8.GetBytes(this.willTopic) : null; - byte[] willMessageUtf8 = (this.willFlag && (this.willMessage != null)) ? Encoding.UTF8.GetBytes(this.willMessage) : null; - byte[] usernameUtf8 = ((this.username != null) && (this.username.Length > 0)) ? Encoding.UTF8.GetBytes(this.username) : null; - byte[] passwordUtf8 = ((this.password != null) && (this.password.Length > 0)) ? Encoding.UTF8.GetBytes(this.password) : null; - - // [v3.1.1] - if (this.protocolVersion == PROTOCOL_VERSION_V3_1_1) - { - // will flag set, will topic and will message MUST be present - if (this.willFlag && ((this.willQosLevel >= 0x03) || - (willTopicUtf8 == null) || (willMessageUtf8 == null) || - ((willTopicUtf8 != null) && (willTopicUtf8.Length == 0)) || - ((willMessageUtf8 != null) && (willMessageUtf8.Length == 0)))) - throw new MqttClientException(MqttClientErrorCode.WillWrong); - // willflag not set, retain must be 0 and will topic and message MUST NOT be present - else if (!this.willFlag && ((this.willRetain) || - (willTopicUtf8 != null) || (willMessageUtf8 != null) || - ((willTopicUtf8 != null) && (willTopicUtf8.Length != 0)) || - ((willMessageUtf8 != null) && (willMessageUtf8.Length != 0)))) - throw new MqttClientException(MqttClientErrorCode.WillWrong); - } - - if (this.keepAlivePeriod > MAX_KEEP_ALIVE) - throw new MqttClientException(MqttClientErrorCode.KeepAliveWrong); - - // check on will QoS Level - if ((this.willQosLevel < MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE) || - (this.willQosLevel > MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE)) - throw new MqttClientException(MqttClientErrorCode.WillWrong); - - // protocol name field size - // MQTT version 3.1 - if (this.protocolVersion == PROTOCOL_VERSION_V3_1) - { - varHeaderSize += (PROTOCOL_NAME_LEN_SIZE + PROTOCOL_NAME_V3_1_SIZE); - } - // MQTT version 3.1.1 - else - { - varHeaderSize += (PROTOCOL_NAME_LEN_SIZE + PROTOCOL_NAME_V3_1_1_SIZE); - } - // protocol level field size - varHeaderSize += PROTOCOL_VERSION_SIZE; - // connect flags field size - varHeaderSize += CONNECT_FLAGS_SIZE; - // keep alive timer field size - varHeaderSize += KEEP_ALIVE_TIME_SIZE; - - // client identifier field size - payloadSize += clientIdUtf8.Length + 2; - // will topic field size - payloadSize += (willTopicUtf8 != null) ? (willTopicUtf8.Length + 2) : 0; - // will message field size - payloadSize += (willMessageUtf8 != null) ? (willMessageUtf8.Length + 2) : 0; - // username field size - payloadSize += (usernameUtf8 != null) ? (usernameUtf8.Length + 2) : 0; - // password field size - payloadSize += (passwordUtf8 != null) ? (passwordUtf8.Length + 2) : 0; - - remainingLength += (varHeaderSize + payloadSize); - - // first byte of fixed header - fixedHeaderSize = 1; - - int temp = remainingLength; - // increase fixed header size based on remaining length - // (each remaining length byte can encode until 128) - do - { - fixedHeaderSize++; - temp = temp / 128; - } while (temp > 0); - - // allocate buffer for message - buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize]; - - // first fixed header byte - buffer[index++] = (MQTT_MSG_CONNECT_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_CONNECT_FLAG_BITS; // [v.3.1.1] - - // encode remaining length - index = this.encodeRemainingLength(remainingLength, buffer, index); - - // protocol name - buffer[index++] = 0; // MSB protocol name size - // MQTT version 3.1 - if (this.protocolVersion == PROTOCOL_VERSION_V3_1) - { - buffer[index++] = PROTOCOL_NAME_V3_1_SIZE; // LSB protocol name size - Array.Copy(Encoding.UTF8.GetBytes(PROTOCOL_NAME_V3_1), 0, buffer, index, PROTOCOL_NAME_V3_1_SIZE); - index += PROTOCOL_NAME_V3_1_SIZE; - // protocol version - buffer[index++] = PROTOCOL_VERSION_V3_1; - } - // MQTT version 3.1.1 - else - { - buffer[index++] = PROTOCOL_NAME_V3_1_1_SIZE; // LSB protocol name size - Array.Copy(Encoding.UTF8.GetBytes(PROTOCOL_NAME_V3_1_1), 0, buffer, index, PROTOCOL_NAME_V3_1_1_SIZE); - index += PROTOCOL_NAME_V3_1_1_SIZE; - // protocol version - buffer[index++] = PROTOCOL_VERSION_V3_1_1; - } - - // connect flags - byte connectFlags = 0x00; - connectFlags |= (usernameUtf8 != null) ? (byte)(1 << USERNAME_FLAG_OFFSET) : (byte)0x00; - connectFlags |= (passwordUtf8 != null) ? (byte)(1 << PASSWORD_FLAG_OFFSET) : (byte)0x00; - connectFlags |= (this.willRetain) ? (byte)(1 << WILL_RETAIN_FLAG_OFFSET) : (byte)0x00; - // only if will flag is set, we have to use will QoS level (otherwise is MUST be 0) - if (this.willFlag) - connectFlags |= (byte)(this.willQosLevel << WILL_QOS_FLAG_OFFSET); - connectFlags |= (this.willFlag) ? (byte)(1 << WILL_FLAG_OFFSET) : (byte)0x00; - connectFlags |= (this.cleanSession) ? (byte)(1 << CLEAN_SESSION_FLAG_OFFSET) : (byte)0x00; - buffer[index++] = connectFlags; - - // keep alive period - buffer[index++] = (byte)((this.keepAlivePeriod >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(this.keepAlivePeriod & 0x00FF); // LSB - - // client identifier - buffer[index++] = (byte)((clientIdUtf8.Length >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(clientIdUtf8.Length & 0x00FF); // LSB - Array.Copy(clientIdUtf8, 0, buffer, index, clientIdUtf8.Length); - index += clientIdUtf8.Length; - - // will topic - if (this.willFlag && (willTopicUtf8 != null)) - { - buffer[index++] = (byte)((willTopicUtf8.Length >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(willTopicUtf8.Length & 0x00FF); // LSB - Array.Copy(willTopicUtf8, 0, buffer, index, willTopicUtf8.Length); - index += willTopicUtf8.Length; - } - - // will message - if (this.willFlag && (willMessageUtf8 != null)) - { - buffer[index++] = (byte)((willMessageUtf8.Length >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(willMessageUtf8.Length & 0x00FF); // LSB - Array.Copy(willMessageUtf8, 0, buffer, index, willMessageUtf8.Length); - index += willMessageUtf8.Length; - } - - // username - if (usernameUtf8 != null) - { - buffer[index++] = (byte)((usernameUtf8.Length >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(usernameUtf8.Length & 0x00FF); // LSB - Array.Copy(usernameUtf8, 0, buffer, index, usernameUtf8.Length); - index += usernameUtf8.Length; - } - - // password - if (passwordUtf8 != null) - { - buffer[index++] = (byte)((passwordUtf8.Length >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(passwordUtf8.Length & 0x00FF); // LSB - Array.Copy(passwordUtf8, 0, buffer, index, passwordUtf8.Length); - index += passwordUtf8.Length; - } - - return buffer; - } - - public override string ToString() - { + public String ProtocolName { get; set; } + + /// + /// Protocol version + /// + public Byte ProtocolVersion { get; set; } + + /// + /// Client identifier + /// + public String ClientId { get; set; } + + /// + /// Will retain flag + /// + public Boolean WillRetain { get; set; } + + /// + /// Will QOS level + /// + public Byte WillQosLevel { get; set; } + + /// + /// Will flag + /// + public Boolean WillFlag { get; set; } + + /// + /// Will topic + /// + public String WillTopic { get; set; } + + /// + /// Will message + /// + public String WillMessage { get; set; } + + /// + /// Username + /// + public String Username { get; set; } + + /// + /// Password + /// + public String Password { get; set; } + + /// + /// Clean session flag + /// + public Boolean CleanSession { get; set; } + + /// + /// Keep alive period + /// + public UInt16 KeepAlivePeriod { get; set; } + + #endregion + + // protocol name + + // will retain flag + // will quality of service level + + /// + /// Constructor + /// + public MqttMsgConnect() => this.Type = MQTT_MSG_CONNECT_TYPE; + + /// + /// Constructor + /// + /// Client identifier + public MqttMsgConnect(String clientId) : + this(clientId, null, null, false, QOS_LEVEL_AT_LEAST_ONCE, false, null, null, true, KEEP_ALIVE_PERIOD_DEFAULT, PROTOCOL_VERSION_V3_1_1) { + } + + /// + /// Constructor + /// + /// Client identifier + /// Username + /// Password + /// Will retain flag + /// Will QOS level + /// Will flag + /// Will topic + /// Will message + /// Clean sessione flag + /// Keep alive period + /// Protocol version + public MqttMsgConnect(String clientId, + String username, + String password, + Boolean willRetain, + Byte willQosLevel, + Boolean willFlag, + String willTopic, + String willMessage, + Boolean cleanSession, + UInt16 keepAlivePeriod, + Byte protocolVersion + ) { + this.Type = MQTT_MSG_CONNECT_TYPE; + + this.ClientId = clientId; + this.Username = username; + this.Password = password; + this.WillRetain = willRetain; + this.WillQosLevel = willQosLevel; + this.WillFlag = willFlag; + this.WillTopic = willTopic; + this.WillMessage = willMessage; + this.CleanSession = cleanSession; + this.KeepAlivePeriod = keepAlivePeriod; + // [v.3.1.1] added new protocol name and version + this.ProtocolVersion = protocolVersion; + this.ProtocolName = (this.ProtocolVersion == PROTOCOL_VERSION_V3_1_1) ? PROTOCOL_NAME_V3_1_1 : PROTOCOL_NAME_V3_1; + } + + /// + /// Parse bytes for a CONNECT message + /// + /// First fixed header byte + /// Protocol Version + /// Channel connected to the broker + /// CONNECT message instance + public static MqttMsgConnect Parse(Byte fixedHeaderFirstByte, Byte protocolVersion, IMqttNetworkChannel channel) { + Byte[] buffer; + Int32 index = 0; + Int32 protNameUtf8Length; + Byte[] protNameUtf8; + Boolean isUsernameFlag; + Boolean isPasswordFlag; + Int32 clientIdUtf8Length; + Byte[] clientIdUtf8; + Int32 willTopicUtf8Length; + Byte[] willTopicUtf8; + Int32 willMessageUtf8Length; + Byte[] willMessageUtf8; + Int32 usernameUtf8Length; + Byte[] usernameUtf8; + Int32 passwordUtf8Length; + Byte[] passwordUtf8; + MqttMsgConnect msg = new MqttMsgConnect(); + + // get remaining length and allocate buffer + Int32 remainingLength = MqttMsgBase.DecodeRemainingLength(channel); + buffer = new Byte[remainingLength]; + + // read bytes from socket... + _ = channel.Receive(buffer); + + // protocol name + protNameUtf8Length = (buffer[index++] << 8) & 0xFF00; + protNameUtf8Length |= buffer[index++]; + protNameUtf8 = new Byte[protNameUtf8Length]; + Array.Copy(buffer, index, protNameUtf8, 0, protNameUtf8Length); + index += protNameUtf8Length; + msg.ProtocolName = new String(Encoding.UTF8.GetChars(protNameUtf8)); + + // [v3.1.1] wrong protocol name + if (!msg.ProtocolName.Equals(PROTOCOL_NAME_V3_1) && !msg.ProtocolName.Equals(PROTOCOL_NAME_V3_1_1)) { + throw new MqttClientException(MqttClientErrorCode.InvalidProtocolName); + } + + // protocol version + msg.ProtocolVersion = buffer[index]; + index += PROTOCOL_VERSION_SIZE; + + // connect flags + // [v3.1.1] check lsb (reserved) must be 0 + if (msg.ProtocolVersion == PROTOCOL_VERSION_V3_1_1 && + (buffer[index] & RESERVED_FLAG_MASK) != 0x00) { + throw new MqttClientException(MqttClientErrorCode.InvalidConnectFlags); + } + + isUsernameFlag = (buffer[index] & USERNAME_FLAG_MASK) != 0x00; + isPasswordFlag = (buffer[index] & PASSWORD_FLAG_MASK) != 0x00; + msg.WillRetain = (buffer[index] & WILL_RETAIN_FLAG_MASK) != 0x00; + msg.WillQosLevel = (Byte)((buffer[index] & WILL_QOS_FLAG_MASK) >> WILL_QOS_FLAG_OFFSET); + msg.WillFlag = (buffer[index] & WILL_FLAG_MASK) != 0x00; + msg.CleanSession = (buffer[index] & CLEAN_SESSION_FLAG_MASK) != 0x00; + index += CONNECT_FLAGS_SIZE; + + // keep alive timer + msg.KeepAlivePeriod = (UInt16)((buffer[index++] << 8) & 0xFF00); + msg.KeepAlivePeriod |= buffer[index++]; + + // client identifier [v3.1.1] it may be zero bytes long (empty string) + clientIdUtf8Length = (buffer[index++] << 8) & 0xFF00; + clientIdUtf8Length |= buffer[index++]; + clientIdUtf8 = new Byte[clientIdUtf8Length]; + Array.Copy(buffer, index, clientIdUtf8, 0, clientIdUtf8Length); + index += clientIdUtf8Length; + msg.ClientId = new String(Encoding.UTF8.GetChars(clientIdUtf8)); + // [v3.1.1] if client identifier is zero bytes long, clean session must be true + if (msg.ProtocolVersion == PROTOCOL_VERSION_V3_1_1 && clientIdUtf8Length == 0 && !msg.CleanSession) { + throw new MqttClientException(MqttClientErrorCode.InvalidClientId); + } + + // will topic and will message + if (msg.WillFlag) { + willTopicUtf8Length = (buffer[index++] << 8) & 0xFF00; + willTopicUtf8Length |= buffer[index++]; + willTopicUtf8 = new Byte[willTopicUtf8Length]; + Array.Copy(buffer, index, willTopicUtf8, 0, willTopicUtf8Length); + index += willTopicUtf8Length; + msg.WillTopic = new String(Encoding.UTF8.GetChars(willTopicUtf8)); + + willMessageUtf8Length = (buffer[index++] << 8) & 0xFF00; + willMessageUtf8Length |= buffer[index++]; + willMessageUtf8 = new Byte[willMessageUtf8Length]; + Array.Copy(buffer, index, willMessageUtf8, 0, willMessageUtf8Length); + index += willMessageUtf8Length; + msg.WillMessage = new String(Encoding.UTF8.GetChars(willMessageUtf8)); + } + + // username + if (isUsernameFlag) { + usernameUtf8Length = (buffer[index++] << 8) & 0xFF00; + usernameUtf8Length |= buffer[index++]; + usernameUtf8 = new Byte[usernameUtf8Length]; + Array.Copy(buffer, index, usernameUtf8, 0, usernameUtf8Length); + index += usernameUtf8Length; + msg.Username = new String(Encoding.UTF8.GetChars(usernameUtf8)); + } + + // password + if (isPasswordFlag) { + passwordUtf8Length = (buffer[index++] << 8) & 0xFF00; + passwordUtf8Length |= buffer[index++]; + passwordUtf8 = new Byte[passwordUtf8Length]; + Array.Copy(buffer, index, passwordUtf8, 0, passwordUtf8Length); + msg.Password = new String(Encoding.UTF8.GetChars(passwordUtf8)); + } + + return msg; + } + + public override Byte[] GetBytes(Byte protocolVersion) { + Int32 varHeaderSize = 0; + Int32 payloadSize = 0; + Int32 remainingLength = 0; + Byte[] buffer; + Int32 index = 0; + + Byte[] clientIdUtf8 = Encoding.UTF8.GetBytes(this.ClientId); + Byte[] willTopicUtf8 = (this.WillFlag && this.WillTopic != null) ? Encoding.UTF8.GetBytes(this.WillTopic) : null; + Byte[] willMessageUtf8 = (this.WillFlag && this.WillMessage != null) ? Encoding.UTF8.GetBytes(this.WillMessage) : null; + Byte[] usernameUtf8 = (this.Username != null && this.Username.Length > 0) ? Encoding.UTF8.GetBytes(this.Username) : null; + Byte[] passwordUtf8 = (this.Password != null && this.Password.Length > 0) ? Encoding.UTF8.GetBytes(this.Password) : null; + + // [v3.1.1] + if (this.ProtocolVersion == PROTOCOL_VERSION_V3_1_1) { + // will flag set, will topic and will message MUST be present + if (this.WillFlag && (this.WillQosLevel >= 0x03 || + willTopicUtf8 == null || willMessageUtf8 == null || + willTopicUtf8 != null && willTopicUtf8.Length == 0 || + willMessageUtf8 != null && willMessageUtf8.Length == 0)) { + throw new MqttClientException(MqttClientErrorCode.WillWrong); + } + // willflag not set, retain must be 0 and will topic and message MUST NOT be present + else if (!this.WillFlag && (this.WillRetain || + willTopicUtf8 != null || willMessageUtf8 != null || + willTopicUtf8 != null && willTopicUtf8.Length != 0 || + willMessageUtf8 != null && willMessageUtf8.Length != 0)) { + throw new MqttClientException(MqttClientErrorCode.WillWrong); + } + } + + if (this.KeepAlivePeriod > MAX_KEEP_ALIVE) { + throw new MqttClientException(MqttClientErrorCode.KeepAliveWrong); + } + + // check on will QoS Level + if (this.WillQosLevel < MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE || + this.WillQosLevel > MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE) { + throw new MqttClientException(MqttClientErrorCode.WillWrong); + } + + // protocol name field size + // MQTT version 3.1 + if (this.ProtocolVersion == PROTOCOL_VERSION_V3_1) { + varHeaderSize += PROTOCOL_NAME_LEN_SIZE + PROTOCOL_NAME_V3_1_SIZE; + } + // MQTT version 3.1.1 + else { + varHeaderSize += PROTOCOL_NAME_LEN_SIZE + PROTOCOL_NAME_V3_1_1_SIZE; + } + // protocol level field size + varHeaderSize += PROTOCOL_VERSION_SIZE; + // connect flags field size + varHeaderSize += CONNECT_FLAGS_SIZE; + // keep alive timer field size + varHeaderSize += KEEP_ALIVE_TIME_SIZE; + + // client identifier field size + payloadSize += clientIdUtf8.Length + 2; + // will topic field size + payloadSize += (willTopicUtf8 != null) ? (willTopicUtf8.Length + 2) : 0; + // will message field size + payloadSize += (willMessageUtf8 != null) ? (willMessageUtf8.Length + 2) : 0; + // username field size + payloadSize += (usernameUtf8 != null) ? (usernameUtf8.Length + 2) : 0; + // password field size + payloadSize += (passwordUtf8 != null) ? (passwordUtf8.Length + 2) : 0; + + remainingLength += varHeaderSize + payloadSize; + + // first byte of fixed header + Int32 fixedHeaderSize = 1; + + Int32 temp = remainingLength; + // increase fixed header size based on remaining length + // (each remaining length byte can encode until 128) + do { + fixedHeaderSize++; + temp /= 128; + } while (temp > 0); + + // allocate buffer for message + buffer = new Byte[fixedHeaderSize + varHeaderSize + payloadSize]; + + // first fixed header byte + buffer[index++] = (MQTT_MSG_CONNECT_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_CONNECT_FLAG_BITS; // [v.3.1.1] + + // encode remaining length + index = this.EncodeRemainingLength(remainingLength, buffer, index); + + // protocol name + buffer[index++] = 0; // MSB protocol name size + // MQTT version 3.1 + if (this.ProtocolVersion == PROTOCOL_VERSION_V3_1) { + buffer[index++] = PROTOCOL_NAME_V3_1_SIZE; // LSB protocol name size + Array.Copy(Encoding.UTF8.GetBytes(PROTOCOL_NAME_V3_1), 0, buffer, index, PROTOCOL_NAME_V3_1_SIZE); + index += PROTOCOL_NAME_V3_1_SIZE; + // protocol version + buffer[index++] = PROTOCOL_VERSION_V3_1; + } + // MQTT version 3.1.1 + else { + buffer[index++] = PROTOCOL_NAME_V3_1_1_SIZE; // LSB protocol name size + Array.Copy(Encoding.UTF8.GetBytes(PROTOCOL_NAME_V3_1_1), 0, buffer, index, PROTOCOL_NAME_V3_1_1_SIZE); + index += PROTOCOL_NAME_V3_1_1_SIZE; + // protocol version + buffer[index++] = PROTOCOL_VERSION_V3_1_1; + } + + // connect flags + Byte connectFlags = 0x00; + connectFlags |= (usernameUtf8 != null) ? (Byte)(1 << USERNAME_FLAG_OFFSET) : (Byte)0x00; + connectFlags |= (passwordUtf8 != null) ? (Byte)(1 << PASSWORD_FLAG_OFFSET) : (Byte)0x00; + connectFlags |= this.WillRetain ? (Byte)(1 << WILL_RETAIN_FLAG_OFFSET) : (Byte)0x00; + // only if will flag is set, we have to use will QoS level (otherwise is MUST be 0) + if (this.WillFlag) { + connectFlags |= (Byte)(this.WillQosLevel << WILL_QOS_FLAG_OFFSET); + } + + connectFlags |= this.WillFlag ? (Byte)(1 << WILL_FLAG_OFFSET) : (Byte)0x00; + connectFlags |= this.CleanSession ? (Byte)(1 << CLEAN_SESSION_FLAG_OFFSET) : (Byte)0x00; + buffer[index++] = connectFlags; + + // keep alive period + buffer[index++] = (Byte)((this.KeepAlivePeriod >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(this.KeepAlivePeriod & 0x00FF); // LSB + + // client identifier + buffer[index++] = (Byte)((clientIdUtf8.Length >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(clientIdUtf8.Length & 0x00FF); // LSB + Array.Copy(clientIdUtf8, 0, buffer, index, clientIdUtf8.Length); + index += clientIdUtf8.Length; + + // will topic + if (this.WillFlag && willTopicUtf8 != null) { + buffer[index++] = (Byte)((willTopicUtf8.Length >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(willTopicUtf8.Length & 0x00FF); // LSB + Array.Copy(willTopicUtf8, 0, buffer, index, willTopicUtf8.Length); + index += willTopicUtf8.Length; + } + + // will message + if (this.WillFlag && willMessageUtf8 != null) { + buffer[index++] = (Byte)((willMessageUtf8.Length >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(willMessageUtf8.Length & 0x00FF); // LSB + Array.Copy(willMessageUtf8, 0, buffer, index, willMessageUtf8.Length); + index += willMessageUtf8.Length; + } + + // username + if (usernameUtf8 != null) { + buffer[index++] = (Byte)((usernameUtf8.Length >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(usernameUtf8.Length & 0x00FF); // LSB + Array.Copy(usernameUtf8, 0, buffer, index, usernameUtf8.Length); + index += usernameUtf8.Length; + } + + // password + if (passwordUtf8 != null) { + buffer[index++] = (Byte)((passwordUtf8.Length >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(passwordUtf8.Length & 0x00FF); // LSB + Array.Copy(passwordUtf8, 0, buffer, index, passwordUtf8.Length); + _ = passwordUtf8.Length; + } + + return buffer; + } + + public override String ToString() => #if TRACE - return this.GetTraceString( - "CONNECT", - new object[] { "protocolName", "protocolVersion", "clientId", "willFlag", "willRetain", "willQosLevel", "willTopic", "willMessage", "username", "password", "cleanSession", "keepAlivePeriod" }, - new object[] { this.protocolName, this.protocolVersion, this.clientId, this.willFlag, this.willRetain, this.willQosLevel, this.willTopic, this.willMessage, this.username, this.password, this.cleanSession, this.keepAlivePeriod }); + this.GetTraceString( + "CONNECT", + new Object[] { "protocolName", "protocolVersion", "clientId", "willFlag", "willRetain", "willQosLevel", "willTopic", "willMessage", "username", "password", "cleanSession", "keepAlivePeriod" }, + new Object[] { this.ProtocolName, this.ProtocolVersion, this.ClientId, this.WillFlag, this.WillRetain, this.WillQosLevel, this.WillTopic, this.WillMessage, this.Username, this.Password, this.CleanSession, this.KeepAlivePeriod }); #else - return base.ToString(); + base.ToString(); #endif - } - } + + } } diff --git a/M2Mqtt/Messages/MqttMsgConnectEventArgs.cs b/M2Mqtt/Messages/MqttMsgConnectEventArgs.cs index ce0b3fe..8995f66 100644 --- a/M2Mqtt/Messages/MqttMsgConnectEventArgs.cs +++ b/M2Mqtt/Messages/MqttMsgConnectEventArgs.cs @@ -14,31 +14,26 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ -#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +#if !MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 using System; #else using Microsoft.SPOT; #endif -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Event Args class for CONNECT message received from client + /// + public class MqttMsgConnectEventArgs : EventArgs { /// - /// Event Args class for CONNECT message received from client + /// Message received from client /// - public class MqttMsgConnectEventArgs : EventArgs - { - /// - /// Message received from client - /// - public MqttMsgConnect Message { get; private set; } - - /// - /// Constructor - /// - /// CONNECT message received from client - public MqttMsgConnectEventArgs(MqttMsgConnect connect) - { - this.Message = connect; - } - } -} + public MqttMsgConnect Message { get; private set; } + + /// + /// Constructor + /// + /// CONNECT message received from client + public MqttMsgConnectEventArgs(MqttMsgConnect connect) => this.Message = connect; + } +} diff --git a/M2Mqtt/Messages/MqttMsgContext.cs b/M2Mqtt/Messages/MqttMsgContext.cs index a2850d7..0b7891a 100644 --- a/M2Mqtt/Messages/MqttMsgContext.cs +++ b/M2Mqtt/Messages/MqttMsgContext.cs @@ -15,145 +15,137 @@ Contributors: */ using System; -using System.Text; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Context for MQTT message + /// + public class MqttMsgContext { /// - /// Context for MQTT message + /// MQTT message /// - public class MqttMsgContext - { - /// - /// MQTT message - /// - public MqttMsgBase Message { get; set; } - - /// - /// MQTT message state - /// - public MqttMsgState State { get; set; } - - /// - /// Flow of the message - /// - public MqttMsgFlow Flow { get; set; } - - /// - /// Timestamp in ticks (for retry) - /// - public int Timestamp { get; set; } - - /// - /// Attempt (for retry) - /// - public int Attempt { get; set; } - - /// - /// Unique key - /// - public string Key - { - get { return this.Flow + "_" + this.Message.MessageId; } - } - } - - /// - /// Flow of the message - /// - public enum MqttMsgFlow - { - /// - /// To publish to subscribers - /// - ToPublish, - - /// - /// To acknowledge to publisher - /// - ToAcknowledge - } - + public MqttMsgBase Message { get; set; } + /// /// MQTT message state /// - public enum MqttMsgState - { - /// - /// QOS = 0, Message queued - /// - QueuedQos0, - - /// - /// QOS = 1, Message queued - /// - QueuedQos1, - - /// - /// QOS = 2, Message queued - /// - QueuedQos2, - - /// - /// QOS = 1, PUBLISH sent, wait for PUBACK - /// - WaitForPuback, - - /// - /// QOS = 2, PUBLISH sent, wait for PUBREC - /// - WaitForPubrec, - - /// - /// QOS = 2, PUBREC sent, wait for PUBREL - /// - WaitForPubrel, - - /// - /// QOS = 2, PUBREL sent, wait for PUBCOMP - /// - WaitForPubcomp, - - /// - /// QOS = 2, start first phase handshake send PUBREC - /// - SendPubrec, - - /// - /// QOS = 2, start second phase handshake send PUBREL - /// - SendPubrel, - - /// - /// QOS = 2, end second phase handshake send PUBCOMP - /// - SendPubcomp, - - /// - /// QOS = 1, PUBLISH received, send PUBACK - /// - SendPuback, - - // [v3.1.1] SUBSCRIBE isn't "officially" QOS = 1 - /// - /// Send SUBSCRIBE message - /// - SendSubscribe, - - // [v3.1.1] UNSUBSCRIBE isn't "officially" QOS = 1 - /// - /// Send UNSUBSCRIBE message - /// - SendUnsubscribe, - - /// - /// (QOS = 1), SUBSCRIBE sent, wait for SUBACK - /// - WaitForSuback, - - /// - /// (QOS = 1), UNSUBSCRIBE sent, wait for UNSUBACK - /// - WaitForUnsuback - } + public MqttMsgState State { get; set; } + + /// + /// Flow of the message + /// + public MqttMsgFlow Flow { get; set; } + + /// + /// Timestamp in ticks (for retry) + /// + public Int32 Timestamp { get; set; } + + /// + /// Attempt (for retry) + /// + public Int32 Attempt { get; set; } + + /// + /// Unique key + /// + public String Key => this.Flow + "_" + this.Message.MessageId; + } + + /// + /// Flow of the message + /// + public enum MqttMsgFlow { + /// + /// To publish to subscribers + /// + ToPublish, + + /// + /// To acknowledge to publisher + /// + ToAcknowledge + } + + /// + /// MQTT message state + /// + public enum MqttMsgState { + /// + /// QOS = 0, Message queued + /// + QueuedQos0, + + /// + /// QOS = 1, Message queued + /// + QueuedQos1, + + /// + /// QOS = 2, Message queued + /// + QueuedQos2, + + /// + /// QOS = 1, PUBLISH sent, wait for PUBACK + /// + WaitForPuback, + + /// + /// QOS = 2, PUBLISH sent, wait for PUBREC + /// + WaitForPubrec, + + /// + /// QOS = 2, PUBREC sent, wait for PUBREL + /// + WaitForPubrel, + + /// + /// QOS = 2, PUBREL sent, wait for PUBCOMP + /// + WaitForPubcomp, + + /// + /// QOS = 2, start first phase handshake send PUBREC + /// + SendPubrec, + + /// + /// QOS = 2, start second phase handshake send PUBREL + /// + SendPubrel, + + /// + /// QOS = 2, end second phase handshake send PUBCOMP + /// + SendPubcomp, + + /// + /// QOS = 1, PUBLISH received, send PUBACK + /// + SendPuback, + + // [v3.1.1] SUBSCRIBE isn't "officially" QOS = 1 + /// + /// Send SUBSCRIBE message + /// + SendSubscribe, + + // [v3.1.1] UNSUBSCRIBE isn't "officially" QOS = 1 + /// + /// Send UNSUBSCRIBE message + /// + SendUnsubscribe, + + /// + /// (QOS = 1), SUBSCRIBE sent, wait for SUBACK + /// + WaitForSuback, + + /// + /// (QOS = 1), UNSUBSCRIBE sent, wait for UNSUBACK + /// + WaitForUnsuback + } } diff --git a/M2Mqtt/Messages/MqttMsgDisconnect.cs b/M2Mqtt/Messages/MqttMsgDisconnect.cs index d918473..beb4476 100644 --- a/M2Mqtt/Messages/MqttMsgDisconnect.cs +++ b/M2Mqtt/Messages/MqttMsgDisconnect.cs @@ -14,73 +14,66 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ +using System; using uPLibrary.Networking.M2Mqtt.Exceptions; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Class for DISCONNECT message from client to broker + /// + public class MqttMsgDisconnect : MqttMsgBase { /// - /// Class for DISCONNECT message from client to broker + /// Constructor /// - public class MqttMsgDisconnect : MqttMsgBase - { - /// - /// Constructor - /// - public MqttMsgDisconnect() - { - this.type = MQTT_MSG_DISCONNECT_TYPE; - } - - /// - /// Parse bytes for a DISCONNECT message - /// - /// First fixed header byte - /// Protocol Version - /// Channel connected to the broker - /// DISCONNECT message instance - public static MqttMsgDisconnect Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel) - { - MqttMsgDisconnect msg = new MqttMsgDisconnect(); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - { - // [v3.1.1] check flag bits - if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_DISCONNECT_FLAG_BITS) - throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); - } - - // get remaining length and allocate buffer - int remainingLength = MqttMsgBase.decodeRemainingLength(channel); - // NOTE : remainingLength must be 0 - - return msg; - } - - public override byte[] GetBytes(byte protocolVersion) - { - byte[] buffer = new byte[2]; - int index = 0; - - // first fixed header byte - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - buffer[index++] = (MQTT_MSG_DISCONNECT_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_DISCONNECT_FLAG_BITS; // [v.3.1.1] - else - buffer[index++] = (MQTT_MSG_DISCONNECT_TYPE << MSG_TYPE_OFFSET); - buffer[index++] = 0x00; - - return buffer; - } - - public override string ToString() - { + public MqttMsgDisconnect() => this.Type = MQTT_MSG_DISCONNECT_TYPE; + + /// + /// Parse bytes for a DISCONNECT message + /// + /// First fixed header byte + /// Protocol Version + /// Channel connected to the broker + /// DISCONNECT message instance + public static MqttMsgDisconnect Parse(Byte fixedHeaderFirstByte, Byte protocolVersion, IMqttNetworkChannel channel) { + MqttMsgDisconnect msg = new MqttMsgDisconnect(); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] check flag bits + if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_DISCONNECT_FLAG_BITS) { + throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); + } + } + + // get remaining length and allocate buffer + _ = DecodeRemainingLength(channel); + // NOTE : remainingLength must be 0 + + return msg; + } + + public override Byte[] GetBytes(Byte protocolVersion) { + Byte[] buffer = new Byte[2]; + Int32 index = 0; + + // first fixed header byte + buffer[index++] = protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1 + ? (Byte)((MQTT_MSG_DISCONNECT_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_DISCONNECT_FLAG_BITS) + : (Byte)(MQTT_MSG_DISCONNECT_TYPE << MSG_TYPE_OFFSET); + + buffer[index++] = 0x00; + + return buffer; + } + + public override String ToString() => #if TRACE - return this.GetTraceString( - "DISCONNECT", - null, - null); + this.GetTraceString( + "DISCONNECT", + null, + null); #else - return base.ToString(); + base.ToString(); #endif - } - } + + } } diff --git a/M2Mqtt/Messages/MqttMsgPingReq.cs b/M2Mqtt/Messages/MqttMsgPingReq.cs index b46bdb6..0695e53 100644 --- a/M2Mqtt/Messages/MqttMsgPingReq.cs +++ b/M2Mqtt/Messages/MqttMsgPingReq.cs @@ -14,73 +14,66 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ +using System; using uPLibrary.Networking.M2Mqtt.Exceptions; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Class for PINGREQ message from client to broker + /// + public class MqttMsgPingReq : MqttMsgBase { /// - /// Class for PINGREQ message from client to broker + /// Constructor /// - public class MqttMsgPingReq : MqttMsgBase - { - /// - /// Constructor - /// - public MqttMsgPingReq() - { - this.type = MQTT_MSG_PINGREQ_TYPE; - } - - public override byte[] GetBytes(byte protocolVersion) - { - byte[] buffer = new byte[2]; - int index = 0; - - // first fixed header byte - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - buffer[index++] = (MQTT_MSG_PINGREQ_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PINGREQ_FLAG_BITS; // [v.3.1.1] - else - buffer[index++] = (MQTT_MSG_PINGREQ_TYPE << MSG_TYPE_OFFSET); - buffer[index++] = 0x00; - - return buffer; - } - - /// - /// Parse bytes for a PINGREQ message - /// - /// First fixed header byte - /// Protocol Version - /// Channel connected to the broker - /// PINGREQ message instance - public static MqttMsgPingReq Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel) - { - MqttMsgPingReq msg = new MqttMsgPingReq(); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - { - // [v3.1.1] check flag bits - if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PINGREQ_FLAG_BITS) - throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); - } - - // already know remaininglength is zero (MQTT specification), - // so it isn't necessary to read other data from socket - int remainingLength = MqttMsgBase.decodeRemainingLength(channel); - - return msg; - } - - public override string ToString() - { + public MqttMsgPingReq() => this.Type = MQTT_MSG_PINGREQ_TYPE; + + public override Byte[] GetBytes(Byte protocolVersion) { + Byte[] buffer = new Byte[2]; + Int32 index = 0; + + // first fixed header byte + buffer[index++] = protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1 + ? (Byte)((MQTT_MSG_PINGREQ_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PINGREQ_FLAG_BITS) + : (Byte)(MQTT_MSG_PINGREQ_TYPE << MSG_TYPE_OFFSET); + + buffer[index++] = 0x00; + + return buffer; + } + + /// + /// Parse bytes for a PINGREQ message + /// + /// First fixed header byte + /// Protocol Version + /// Channel connected to the broker + /// PINGREQ message instance + public static MqttMsgPingReq Parse(Byte fixedHeaderFirstByte, Byte protocolVersion, IMqttNetworkChannel channel) { + MqttMsgPingReq msg = new MqttMsgPingReq(); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] check flag bits + if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PINGREQ_FLAG_BITS) { + throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); + } + } + + // already know remaininglength is zero (MQTT specification), + // so it isn't necessary to read other data from socket + _ = DecodeRemainingLength(channel); + + return msg; + } + + public override String ToString() => #if TRACE - return this.GetTraceString( - "PINGREQ", - null, - null); + this.GetTraceString( + "PINGREQ", + null, + null); #else - return base.ToString(); + base.ToString(); #endif - } - } + + } } diff --git a/M2Mqtt/Messages/MqttMsgPingResp.cs b/M2Mqtt/Messages/MqttMsgPingResp.cs index 2b028b9..bfae63f 100644 --- a/M2Mqtt/Messages/MqttMsgPingResp.cs +++ b/M2Mqtt/Messages/MqttMsgPingResp.cs @@ -17,71 +17,63 @@ Contributors: using System; using uPLibrary.Networking.M2Mqtt.Exceptions; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Class for PINGRESP message from client to broker + /// + public class MqttMsgPingResp : MqttMsgBase { /// - /// Class for PINGRESP message from client to broker + /// Constructor /// - public class MqttMsgPingResp : MqttMsgBase - { - /// - /// Constructor - /// - public MqttMsgPingResp() - { - this.type = MQTT_MSG_PINGRESP_TYPE; - } - - /// - /// Parse bytes for a PINGRESP message - /// - /// First fixed header byte - /// Protocol Version - /// Channel connected to the broker - /// PINGRESP message instance - public static MqttMsgPingResp Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel) - { - MqttMsgPingResp msg = new MqttMsgPingResp(); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - { - // [v3.1.1] check flag bits - if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PINGRESP_FLAG_BITS) - throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); - } - - // already know remaininglength is zero (MQTT specification), - // so it isn't necessary to read other data from socket - int remainingLength = MqttMsgBase.decodeRemainingLength(channel); - - return msg; - } - - public override byte[] GetBytes(byte protocolVersion) - { - byte[] buffer = new byte[2]; - int index = 0; - - // first fixed header byte - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - buffer[index++] = (MQTT_MSG_PINGRESP_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PINGRESP_FLAG_BITS; // [v.3.1.1] - else - buffer[index++] = (MQTT_MSG_PINGRESP_TYPE << MSG_TYPE_OFFSET); - buffer[index++] = 0x00; - - return buffer; - } - - public override string ToString() - { + public MqttMsgPingResp() => this.Type = MQTT_MSG_PINGRESP_TYPE; + + /// + /// Parse bytes for a PINGRESP message + /// + /// First fixed header byte + /// Protocol Version + /// Channel connected to the broker + /// PINGRESP message instance + public static MqttMsgPingResp Parse(Byte fixedHeaderFirstByte, Byte protocolVersion, IMqttNetworkChannel channel) { + MqttMsgPingResp msg = new MqttMsgPingResp(); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] check flag bits + if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PINGRESP_FLAG_BITS) { + throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); + } + } + + // already know remaininglength is zero (MQTT specification), + // so it isn't necessary to read other data from socket + _ = DecodeRemainingLength(channel); + + return msg; + } + + public override Byte[] GetBytes(Byte protocolVersion) { + Byte[] buffer = new Byte[2]; + Int32 index = 0; + + // first fixed header byte + buffer[index++] = protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1 + ? (Byte)((MQTT_MSG_PINGRESP_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PINGRESP_FLAG_BITS) + : (Byte)(MQTT_MSG_PINGRESP_TYPE << MSG_TYPE_OFFSET); + + buffer[index++] = 0x00; + + return buffer; + } + + public override String ToString() => #if TRACE - return this.GetTraceString( - "PINGRESP", - null, - null); + this.GetTraceString( + "PINGRESP", + null, + null); #else - return base.ToString(); + base.ToString(); #endif - } - } + + } } diff --git a/M2Mqtt/Messages/MqttMsgPuback.cs b/M2Mqtt/Messages/MqttMsgPuback.cs index e00e37f..9d59b74 100644 --- a/M2Mqtt/Messages/MqttMsgPuback.cs +++ b/M2Mqtt/Messages/MqttMsgPuback.cs @@ -14,112 +14,102 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ +using System; using uPLibrary.Networking.M2Mqtt.Exceptions; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Class for PUBACK message from broker to client + /// + public class MqttMsgPuback : MqttMsgBase { /// - /// Class for PUBACK message from broker to client + /// Constructor /// - public class MqttMsgPuback : MqttMsgBase - { - /// - /// Constructor - /// - public MqttMsgPuback() - { - this.type = MQTT_MSG_PUBACK_TYPE; - } - - public override byte[] GetBytes(byte protocolVersion) - { - int fixedHeaderSize = 0; - int varHeaderSize = 0; - int payloadSize = 0; - int remainingLength = 0; - byte[] buffer; - int index = 0; - - // message identifier - varHeaderSize += MESSAGE_ID_SIZE; - - remainingLength += (varHeaderSize + payloadSize); - - // first byte of fixed header - fixedHeaderSize = 1; - - int temp = remainingLength; - // increase fixed header size based on remaining length - // (each remaining length byte can encode until 128) - do - { - fixedHeaderSize++; - temp = temp / 128; - } while (temp > 0); - - // allocate buffer for message - buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize]; - - // first fixed header byte - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - buffer[index++] = (MQTT_MSG_PUBACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBACK_FLAG_BITS; // [v.3.1.1] - else - buffer[index++] = (MQTT_MSG_PUBACK_TYPE << MSG_TYPE_OFFSET); - - // encode remaining length - index = this.encodeRemainingLength(remainingLength, buffer, index); - - // get message identifier - buffer[index++] = (byte)((this.messageId >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(this.messageId & 0x00FF); // LSB - - return buffer; - } - - /// - /// Parse bytes for a PUBACK message - /// - /// First fixed header byte - /// Protocol Version - /// Channel connected to the broker - /// PUBACK message instance - public static MqttMsgPuback Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel) - { - byte[] buffer; - int index = 0; - MqttMsgPuback msg = new MqttMsgPuback(); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - { - // [v3.1.1] check flag bits - if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBACK_FLAG_BITS) - throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); - } - - // get remaining length and allocate buffer - int remainingLength = MqttMsgBase.decodeRemainingLength(channel); - buffer = new byte[remainingLength]; - - // read bytes from socket... - channel.Receive(buffer); - - // message id - msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00); - msg.messageId |= (buffer[index++]); - - return msg; - } - - public override string ToString() - { + public MqttMsgPuback() => this.Type = MQTT_MSG_PUBACK_TYPE; + + public override Byte[] GetBytes(Byte protocolVersion) { + Int32 varHeaderSize = 0; + Int32 payloadSize = 0; + Int32 remainingLength = 0; + Byte[] buffer; + Int32 index = 0; + + // message identifier + varHeaderSize += MESSAGE_ID_SIZE; + + remainingLength += varHeaderSize + payloadSize; + + // first byte of fixed header + Int32 fixedHeaderSize = 1; + + Int32 temp = remainingLength; + // increase fixed header size based on remaining length + // (each remaining length byte can encode until 128) + do { + fixedHeaderSize++; + temp /= 128; + } while (temp > 0); + + // allocate buffer for message + buffer = new Byte[fixedHeaderSize + varHeaderSize + payloadSize]; + + // first fixed header byte + buffer[index++] = protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1 + ? (Byte)((MQTT_MSG_PUBACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBACK_FLAG_BITS) + : (Byte)(MQTT_MSG_PUBACK_TYPE << MSG_TYPE_OFFSET); + + // encode remaining length + index = this.EncodeRemainingLength(remainingLength, buffer, index); + + // get message identifier + buffer[index++] = (Byte)((this.MessageId >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(this.MessageId & 0x00FF); // LSB + + return buffer; + } + + /// + /// Parse bytes for a PUBACK message + /// + /// First fixed header byte + /// Protocol Version + /// Channel connected to the broker + /// PUBACK message instance + public static MqttMsgPuback Parse(Byte fixedHeaderFirstByte, Byte protocolVersion, IMqttNetworkChannel channel) { + Byte[] buffer; + Int32 index = 0; + MqttMsgPuback msg = new MqttMsgPuback(); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] check flag bits + if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBACK_FLAG_BITS) { + throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); + } + } + + // get remaining length and allocate buffer + Int32 remainingLength = MqttMsgBase.DecodeRemainingLength(channel); + buffer = new Byte[remainingLength]; + + // read bytes from socket... + _ = channel.Receive(buffer); + + // message id + msg.MessageId = (UInt16)((buffer[index++] << 8) & 0xFF00); + msg.MessageId |= buffer[index++]; + + return msg; + } + + public override String ToString() => #if TRACE - return this.GetTraceString( - "PUBACK", - new object[] { "messageId" }, - new object[] { this.messageId }); + this.GetTraceString( + "PUBACK", + new Object[] { "messageId" }, + new Object[] { this.MessageId }); #else - return base.ToString(); + base.ToString(); #endif - } - } + + } } diff --git a/M2Mqtt/Messages/MqttMsgPubcomp.cs b/M2Mqtt/Messages/MqttMsgPubcomp.cs index eeabd3b..a88a687 100644 --- a/M2Mqtt/Messages/MqttMsgPubcomp.cs +++ b/M2Mqtt/Messages/MqttMsgPubcomp.cs @@ -14,112 +14,102 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ +using System; using uPLibrary.Networking.M2Mqtt.Exceptions; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Class for PUBCOMP message from broker to client + /// + public class MqttMsgPubcomp : MqttMsgBase { /// - /// Class for PUBCOMP message from broker to client + /// Constructor /// - public class MqttMsgPubcomp : MqttMsgBase - { - /// - /// Constructor - /// - public MqttMsgPubcomp() - { - this.type = MQTT_MSG_PUBCOMP_TYPE; - } - - public override byte[] GetBytes(byte protocolVersion) - { - int fixedHeaderSize = 0; - int varHeaderSize = 0; - int payloadSize = 0; - int remainingLength = 0; - byte[] buffer; - int index = 0; - - // message identifier - varHeaderSize += MESSAGE_ID_SIZE; - - remainingLength += (varHeaderSize + payloadSize); - - // first byte of fixed header - fixedHeaderSize = 1; - - int temp = remainingLength; - // increase fixed header size based on remaining length - // (each remaining length byte can encode until 128) - do - { - fixedHeaderSize++; - temp = temp / 128; - } while (temp > 0); - - // allocate buffer for message - buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize]; - - // first fixed header byte - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - buffer[index++] = (MQTT_MSG_PUBCOMP_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBCOMP_FLAG_BITS; // [v.3.1.1] - else - buffer[index++] = (MQTT_MSG_PUBCOMP_TYPE << MSG_TYPE_OFFSET); - - // encode remaining length - index = this.encodeRemainingLength(remainingLength, buffer, index); - - // get message identifier - buffer[index++] = (byte)((this.messageId >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(this.messageId & 0x00FF); // LSB - - return buffer; - } - - /// - /// Parse bytes for a PUBCOMP message - /// - /// First fixed header byte - /// Protocol Version - /// Channel connected to the broker - /// PUBCOMP message instance - public static MqttMsgPubcomp Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel) - { - byte[] buffer; - int index = 0; - MqttMsgPubcomp msg = new MqttMsgPubcomp(); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - { - // [v3.1.1] check flag bits - if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBCOMP_FLAG_BITS) - throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); - } - - // get remaining length and allocate buffer - int remainingLength = MqttMsgBase.decodeRemainingLength(channel); - buffer = new byte[remainingLength]; - - // read bytes from socket... - channel.Receive(buffer); - - // message id - msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00); - msg.messageId |= (buffer[index++]); - - return msg; - } - - public override string ToString() - { + public MqttMsgPubcomp() => this.Type = MQTT_MSG_PUBCOMP_TYPE; + + public override Byte[] GetBytes(Byte protocolVersion) { + Int32 varHeaderSize = 0; + Int32 payloadSize = 0; + Int32 remainingLength = 0; + Byte[] buffer; + Int32 index = 0; + + // message identifier + varHeaderSize += MESSAGE_ID_SIZE; + + remainingLength += varHeaderSize + payloadSize; + + // first byte of fixed header + Int32 fixedHeaderSize = 1; + + Int32 temp = remainingLength; + // increase fixed header size based on remaining length + // (each remaining length byte can encode until 128) + do { + fixedHeaderSize++; + temp /= 128; + } while (temp > 0); + + // allocate buffer for message + buffer = new Byte[fixedHeaderSize + varHeaderSize + payloadSize]; + + // first fixed header byte + buffer[index++] = protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1 + ? (Byte)((MQTT_MSG_PUBCOMP_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBCOMP_FLAG_BITS) + : (Byte)(MQTT_MSG_PUBCOMP_TYPE << MSG_TYPE_OFFSET); + + // encode remaining length + index = this.EncodeRemainingLength(remainingLength, buffer, index); + + // get message identifier + buffer[index++] = (Byte)((this.MessageId >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(this.MessageId & 0x00FF); // LSB + + return buffer; + } + + /// + /// Parse bytes for a PUBCOMP message + /// + /// First fixed header byte + /// Protocol Version + /// Channel connected to the broker + /// PUBCOMP message instance + public static MqttMsgPubcomp Parse(Byte fixedHeaderFirstByte, Byte protocolVersion, IMqttNetworkChannel channel) { + Byte[] buffer; + Int32 index = 0; + MqttMsgPubcomp msg = new MqttMsgPubcomp(); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] check flag bits + if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBCOMP_FLAG_BITS) { + throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); + } + } + + // get remaining length and allocate buffer + Int32 remainingLength = MqttMsgBase.DecodeRemainingLength(channel); + buffer = new Byte[remainingLength]; + + // read bytes from socket... + _ = channel.Receive(buffer); + + // message id + msg.MessageId = (UInt16)((buffer[index++] << 8) & 0xFF00); + msg.MessageId |= buffer[index++]; + + return msg; + } + + public override String ToString() => #if TRACE - return this.GetTraceString( - "PUBCOMP", - new object[] { "messageId" }, - new object[] { this.messageId }); + this.GetTraceString( + "PUBCOMP", + new Object[] { "messageId" }, + new Object[] { this.MessageId }); #else - return base.ToString(); + base.ToString(); #endif - } - } + + } } diff --git a/M2Mqtt/Messages/MqttMsgPublish.cs b/M2Mqtt/Messages/MqttMsgPublish.cs index 0809af9..0f2d0e6 100644 --- a/M2Mqtt/Messages/MqttMsgPublish.cs +++ b/M2Mqtt/Messages/MqttMsgPublish.cs @@ -40,9 +40,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages { /// /// Constructor /// - public MqttMsgPublish() { - this.type = MQTT_MSG_PUBLISH_TYPE; - } + public MqttMsgPublish() => this.Type = MQTT_MSG_PUBLISH_TYPE; /// /// Constructor @@ -61,17 +59,16 @@ namespace uPLibrary.Networking.M2Mqtt.Messages { /// Quality of Service level /// Retain flag public MqttMsgPublish(String topic, Byte[] message, Boolean dupFlag, Byte qosLevel, Boolean retain) : base() { - this.type = MQTT_MSG_PUBLISH_TYPE; + this.Type = MQTT_MSG_PUBLISH_TYPE; this.Topic = topic; this.Message = message; - this.dupFlag = dupFlag; - this.qosLevel = qosLevel; - this.retain = retain; - this.messageId = 0; + this.DupFlag = dupFlag; + this.QosLevel = qosLevel; + this.Retain = retain; + this.MessageId = 0; } public override Byte[] GetBytes(Byte protocolVersion) { - Int32 fixedHeaderSize = 0; Int32 varHeaderSize = 0; Int32 payloadSize = 0; Int32 remainingLength = 0; @@ -79,17 +76,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages { Int32 index = 0; // topic can't contain wildcards - if ((this.Topic.IndexOf('#') != -1) || (this.Topic.IndexOf('+') != -1)) { + if (this.Topic.IndexOf('#') != -1 || this.Topic.IndexOf('+') != -1) { throw new MqttClientException(MqttClientErrorCode.TopicWildcard); } // check topic length - if ((this.Topic.Length < MIN_TOPIC_LENGTH) || (this.Topic.Length > MAX_TOPIC_LENGTH)) { + if (this.Topic.Length < MIN_TOPIC_LENGTH || this.Topic.Length > MAX_TOPIC_LENGTH) { throw new MqttClientException(MqttClientErrorCode.TopicLength); } // check wrong QoS level (both bits can't be set 1) - if (this.qosLevel > QOS_LEVEL_EXACTLY_ONCE) { + if (this.QosLevel > QOS_LEVEL_EXACTLY_ONCE) { throw new MqttClientException(MqttClientErrorCode.QosNotAllowed); } @@ -99,7 +96,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages { varHeaderSize += topicUtf8.Length + 2; // message id is valid only with QOS level 1 or QOS level 2 - if ((this.qosLevel == QOS_LEVEL_AT_LEAST_ONCE) || (this.qosLevel == QOS_LEVEL_EXACTLY_ONCE)) { + if (this.QosLevel == QOS_LEVEL_AT_LEAST_ONCE || this.QosLevel == QOS_LEVEL_EXACTLY_ONCE) { varHeaderSize += MESSAGE_ID_SIZE; } @@ -109,30 +106,30 @@ namespace uPLibrary.Networking.M2Mqtt.Messages { payloadSize += this.Message.Length; } - remainingLength += (varHeaderSize + payloadSize); + remainingLength += varHeaderSize + payloadSize; // first byte of fixed header - fixedHeaderSize = 1; + Int32 fixedHeaderSize = 1; Int32 temp = remainingLength; // increase fixed header size based on remaining length // (each remaining length byte can encode until 128) do { fixedHeaderSize++; - temp = temp / 128; + temp /= 128; } while (temp > 0); // allocate buffer for message buffer = new Byte[fixedHeaderSize + varHeaderSize + payloadSize]; // first fixed header byte - buffer[index] = (Byte)((MQTT_MSG_PUBLISH_TYPE << MSG_TYPE_OFFSET) | (this.qosLevel << QOS_LEVEL_OFFSET)); - buffer[index] |= this.dupFlag ? (Byte)(1 << DUP_FLAG_OFFSET) : (Byte)0x00; - buffer[index] |= this.retain ? (Byte)(1 << RETAIN_FLAG_OFFSET) : (Byte)0x00; + buffer[index] = (Byte)((MQTT_MSG_PUBLISH_TYPE << MSG_TYPE_OFFSET) | (this.QosLevel << QOS_LEVEL_OFFSET)); + buffer[index] |= this.DupFlag ? (Byte)(1 << DUP_FLAG_OFFSET) : (Byte)0x00; + buffer[index] |= this.Retain ? (Byte)(1 << RETAIN_FLAG_OFFSET) : (Byte)0x00; index++; // encode remaining length - index = this.encodeRemainingLength(remainingLength, buffer, index); + index = this.EncodeRemainingLength(remainingLength, buffer, index); // topic name buffer[index++] = (Byte)((topicUtf8.Length >> 8) & 0x00FF); // MSB @@ -141,22 +138,22 @@ namespace uPLibrary.Networking.M2Mqtt.Messages { index += topicUtf8.Length; // message id is valid only with QOS level 1 or QOS level 2 - if ((this.qosLevel == QOS_LEVEL_AT_LEAST_ONCE) || - (this.qosLevel == QOS_LEVEL_EXACTLY_ONCE)) { + if (this.QosLevel == QOS_LEVEL_AT_LEAST_ONCE || + this.QosLevel == QOS_LEVEL_EXACTLY_ONCE) { // check message identifier assigned - if (this.messageId == 0) { + if (this.MessageId == 0) { throw new MqttClientException(MqttClientErrorCode.WrongMessageId); } - buffer[index++] = (Byte)((this.messageId >> 8) & 0x00FF); // MSB - buffer[index++] = (Byte)(this.messageId & 0x00FF); // LSB + buffer[index++] = (Byte)((this.MessageId >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(this.MessageId & 0x00FF); // LSB } // check on message with zero length if (this.Message != null) { // message data Array.Copy(this.Message, 0, buffer, index, this.Message.Length); - index += this.Message.Length; + _ = this.Message.Length; } return buffer; @@ -177,14 +174,14 @@ namespace uPLibrary.Networking.M2Mqtt.Messages { MqttMsgPublish msg = new MqttMsgPublish(); // get remaining length and allocate buffer - Int32 remainingLength = decodeRemainingLength(channel); + Int32 remainingLength = DecodeRemainingLength(channel); buffer = new Byte[remainingLength]; // read bytes from socket... Int32 received = channel.Receive(buffer); // topic name - topicUtf8Length = ((buffer[index++] << 8) & 0xFF00); + topicUtf8Length = (buffer[index++] << 8) & 0xFF00; topicUtf8Length |= buffer[index++]; topicUtf8 = new Byte[topicUtf8Length]; Array.Copy(buffer, index, topicUtf8, 0, topicUtf8Length); @@ -192,22 +189,22 @@ namespace uPLibrary.Networking.M2Mqtt.Messages { msg.Topic = new String(Encoding.UTF8.GetChars(topicUtf8)); // read QoS level from fixed header - msg.qosLevel = (Byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET); + msg.QosLevel = (Byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET); // check wrong QoS level (both bits can't be set 1) - if (msg.qosLevel > QOS_LEVEL_EXACTLY_ONCE) { + if (msg.QosLevel > QOS_LEVEL_EXACTLY_ONCE) { throw new MqttClientException(MqttClientErrorCode.QosNotAllowed); } // read DUP flag from fixed header - msg.dupFlag = (((fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET) == 0x01); + msg.DupFlag = (fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET == 0x01; // read retain flag from fixed header - msg.retain = (((fixedHeaderFirstByte & RETAIN_FLAG_MASK) >> RETAIN_FLAG_OFFSET) == 0x01); + msg.Retain = (fixedHeaderFirstByte & RETAIN_FLAG_MASK) >> RETAIN_FLAG_OFFSET == 0x01; // message id is valid only with QOS level 1 or QOS level 2 - if ((msg.qosLevel == QOS_LEVEL_AT_LEAST_ONCE) || - (msg.qosLevel == QOS_LEVEL_EXACTLY_ONCE)) { + if (msg.QosLevel == QOS_LEVEL_AT_LEAST_ONCE || + msg.QosLevel == QOS_LEVEL_EXACTLY_ONCE) { // message id - msg.messageId = (UInt16)((buffer[index++] << 8) & 0xFF00); - msg.messageId |= (buffer[index++]); + msg.MessageId = (UInt16)((buffer[index++] << 8) & 0xFF00); + msg.MessageId |= buffer[index++]; } // get payload with message data @@ -220,8 +217,8 @@ namespace uPLibrary.Networking.M2Mqtt.Messages { // copy first part of payload data received Array.Copy(buffer, index, msg.Message, messageOffset, received - index); - remaining -= (received - index); - messageOffset += (received - index); + remaining -= received - index; + messageOffset += received - index; // if payload isn't finished while (remaining > 0) { @@ -235,15 +232,15 @@ namespace uPLibrary.Networking.M2Mqtt.Messages { return msg; } - public override String ToString() { + public override String ToString() => #if TRACE - return this.GetTraceString( + this.GetTraceString( "PUBLISH", new Object[] { "messageId", "topic", "message" }, - new Object[] { this.messageId, this.Topic, this.Message }); + new Object[] { this.MessageId, this.Topic, this.Message }); #else - return base.ToString(); + base.ToString(); #endif - } + } } diff --git a/M2Mqtt/Messages/MqttMsgPublishEventArgs.cs b/M2Mqtt/Messages/MqttMsgPublishEventArgs.cs index efb2333..56af34b 100644 --- a/M2Mqtt/Messages/MqttMsgPublishEventArgs.cs +++ b/M2Mqtt/Messages/MqttMsgPublishEventArgs.cs @@ -14,98 +14,66 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ -#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +#if !MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 using System; #else using Microsoft.SPOT; #endif -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Event Args class for PUBLISH message received from broker + /// + public class MqttMsgPublishEventArgs : EventArgs { + #region Properties... + /// - /// Event Args class for PUBLISH message received from broker + /// Message topic /// - public class MqttMsgPublishEventArgs : EventArgs - { - #region Properties... - - /// - /// Message topic - /// - public string Topic - { - get { return this.topic; } - internal set { this.topic = value; } - } - - /// - /// Message data - /// - public byte[] Message - { - get { return this.message; } - internal set { this.message = value; } - } - - /// - /// Duplicate message flag - /// - public bool DupFlag - { - get { return this.dupFlag; } - set { this.dupFlag = value; } - } - - /// - /// Quality of Service level - /// - public byte QosLevel - { - get { return this.qosLevel; } - internal set { this.qosLevel = value; } - } - - /// - /// Retain message flag - /// - public bool Retain - { - get { return this.retain; } - internal set { this.retain = value; } - } - - #endregion - - // message topic - private string topic; - // message data - private byte[] message; - // duplicate delivery - private bool dupFlag; - // quality of service level - private byte qosLevel; - // retain flag - private bool retain; - - /// - /// Constructor - /// - /// Message topic - /// Message data - /// Duplicate delivery flag - /// Quality of Service level - /// Retain flag - public MqttMsgPublishEventArgs(string topic, - byte[] message, - bool dupFlag, - byte qosLevel, - bool retain) - { - this.topic = topic; - this.message = message; - this.dupFlag = dupFlag; - this.qosLevel = qosLevel; - this.retain = retain; - } - } + public String Topic { get; internal set; } + + /// + /// Message data + /// + public Byte[] Message { get; internal set; } + + /// + /// Duplicate message flag + /// + public Boolean DupFlag { get; set; } + + /// + /// Quality of Service level + /// + public Byte QosLevel { get; internal set; } + + /// + /// Retain message flag + /// + public Boolean Retain { get; internal set; } + + #endregion + + // message topic + + /// + /// Constructor + /// + /// Message topic + /// Message data + /// Duplicate delivery flag + /// Quality of Service level + /// Retain flag + public MqttMsgPublishEventArgs(String topic, + Byte[] message, + Boolean dupFlag, + Byte qosLevel, + Boolean retain) { + this.Topic = topic; + this.Message = message; + this.DupFlag = dupFlag; + this.QosLevel = qosLevel; + this.Retain = retain; + } + } } diff --git a/M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs b/M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs index e507c38..ef040f0 100644 --- a/M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs +++ b/M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs @@ -14,65 +14,49 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ -#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +#if !MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 using System; #else using Microsoft.SPOT; #endif -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Event Args class for published message + /// + public class MqttMsgPublishedEventArgs : EventArgs { + #region Properties... + /// - /// Event Args class for published message + /// Message identifier /// - public class MqttMsgPublishedEventArgs : EventArgs - { - #region Properties... - - /// - /// Message identifier - /// - public ushort MessageId - { - get { return this.messageId; } - internal set { this.messageId = value; } - } - - /// - /// Message published (or failed due to retries) - /// - public bool IsPublished - { - get { return this.isPublished; } - internal set { this.isPublished = value; } - } - - #endregion - - // message identifier - ushort messageId; - - // published flag - bool isPublished; - - /// - /// Constructor (published message) - /// - /// Message identifier published - public MqttMsgPublishedEventArgs(ushort messageId) - : this(messageId, true) - { - } - - /// - /// Constructor - /// - /// Message identifier - /// Publish flag - public MqttMsgPublishedEventArgs(ushort messageId, bool isPublished) - { - this.messageId = messageId; - this.isPublished = isPublished; - } - } + public UInt16 MessageId { get; internal set; } + + /// + /// Message published (or failed due to retries) + /// + public Boolean IsPublished { get; internal set; } + + #endregion + + // message identifier + + /// + /// Constructor (published message) + /// + /// Message identifier published + public MqttMsgPublishedEventArgs(UInt16 messageId) + : this(messageId, true) { + } + + /// + /// Constructor + /// + /// Message identifier + /// Publish flag + public MqttMsgPublishedEventArgs(UInt16 messageId, Boolean isPublished) { + this.MessageId = messageId; + this.IsPublished = isPublished; + } + } } diff --git a/M2Mqtt/Messages/MqttMsgPubrec.cs b/M2Mqtt/Messages/MqttMsgPubrec.cs index 1855bf9..a37d604 100644 --- a/M2Mqtt/Messages/MqttMsgPubrec.cs +++ b/M2Mqtt/Messages/MqttMsgPubrec.cs @@ -14,112 +14,102 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ +using System; using uPLibrary.Networking.M2Mqtt.Exceptions; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Class for PUBREC message from broker to client + /// + public class MqttMsgPubrec : MqttMsgBase { /// - /// Class for PUBREC message from broker to client + /// Constructor /// - public class MqttMsgPubrec : MqttMsgBase - { - /// - /// Constructor - /// - public MqttMsgPubrec() - { - this.type = MQTT_MSG_PUBREC_TYPE; - } - - public override byte[] GetBytes(byte protocolVersion) - { - int fixedHeaderSize = 0; - int varHeaderSize = 0; - int payloadSize = 0; - int remainingLength = 0; - byte[] buffer; - int index = 0; - - // message identifier - varHeaderSize += MESSAGE_ID_SIZE; - - remainingLength += (varHeaderSize + payloadSize); - - // first byte of fixed header - fixedHeaderSize = 1; - - int temp = remainingLength; - // increase fixed header size based on remaining length - // (each remaining length byte can encode until 128) - do - { - fixedHeaderSize++; - temp = temp / 128; - } while (temp > 0); - - // allocate buffer for message - buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize]; - - // first fixed header byte - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - buffer[index++] = (MQTT_MSG_PUBREC_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBREC_FLAG_BITS; // [v.3.1.1] - else - buffer[index++] = (MQTT_MSG_PUBREC_TYPE << MSG_TYPE_OFFSET); - - // encode remaining length - index = this.encodeRemainingLength(remainingLength, buffer, index); - - // get message identifier - buffer[index++] = (byte)((this.messageId >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(this.messageId & 0x00FF); // LSB - - return buffer; - } - - /// - /// Parse bytes for a PUBREC message - /// - /// First fixed header byte - /// Protocol Version - /// Channel connected to the broker - /// PUBREC message instance - public static MqttMsgPubrec Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel) - { - byte[] buffer; - int index = 0; - MqttMsgPubrec msg = new MqttMsgPubrec(); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - { - // [v3.1.1] check flag bits - if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBREC_FLAG_BITS) - throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); - } - - // get remaining length and allocate buffer - int remainingLength = MqttMsgBase.decodeRemainingLength(channel); - buffer = new byte[remainingLength]; - - // read bytes from socket... - channel.Receive(buffer); - - // message id - msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00); - msg.messageId |= (buffer[index++]); - - return msg; - } - - public override string ToString() - { + public MqttMsgPubrec() => this.Type = MQTT_MSG_PUBREC_TYPE; + + public override Byte[] GetBytes(Byte protocolVersion) { + Int32 varHeaderSize = 0; + Int32 payloadSize = 0; + Int32 remainingLength = 0; + Byte[] buffer; + Int32 index = 0; + + // message identifier + varHeaderSize += MESSAGE_ID_SIZE; + + remainingLength += varHeaderSize + payloadSize; + + // first byte of fixed header + Int32 fixedHeaderSize = 1; + + Int32 temp = remainingLength; + // increase fixed header size based on remaining length + // (each remaining length byte can encode until 128) + do { + fixedHeaderSize++; + temp /= 128; + } while (temp > 0); + + // allocate buffer for message + buffer = new Byte[fixedHeaderSize + varHeaderSize + payloadSize]; + + // first fixed header byte + buffer[index++] = protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1 + ? (Byte)((MQTT_MSG_PUBREC_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBREC_FLAG_BITS) + : (Byte)(MQTT_MSG_PUBREC_TYPE << MSG_TYPE_OFFSET); + + // encode remaining length + index = this.EncodeRemainingLength(remainingLength, buffer, index); + + // get message identifier + buffer[index++] = (Byte)((this.MessageId >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(this.MessageId & 0x00FF); // LSB + + return buffer; + } + + /// + /// Parse bytes for a PUBREC message + /// + /// First fixed header byte + /// Protocol Version + /// Channel connected to the broker + /// PUBREC message instance + public static MqttMsgPubrec Parse(Byte fixedHeaderFirstByte, Byte protocolVersion, IMqttNetworkChannel channel) { + Byte[] buffer; + Int32 index = 0; + MqttMsgPubrec msg = new MqttMsgPubrec(); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] check flag bits + if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBREC_FLAG_BITS) { + throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); + } + } + + // get remaining length and allocate buffer + Int32 remainingLength = MqttMsgBase.DecodeRemainingLength(channel); + buffer = new Byte[remainingLength]; + + // read bytes from socket... + _ = channel.Receive(buffer); + + // message id + msg.MessageId = (UInt16)((buffer[index++] << 8) & 0xFF00); + msg.MessageId |= buffer[index++]; + + return msg; + } + + public override String ToString() => #if TRACE - return this.GetTraceString( - "PUBREC", - new object[] { "messageId" }, - new object[] { this.messageId }); + this.GetTraceString( + "PUBREC", + new Object[] { "messageId" }, + new Object[] { this.MessageId }); #else - return base.ToString(); + base.ToString(); #endif - } - } + + } } diff --git a/M2Mqtt/Messages/MqttMsgPubrel.cs b/M2Mqtt/Messages/MqttMsgPubrel.cs index e43ca91..c2cc8b1 100644 --- a/M2Mqtt/Messages/MqttMsgPubrel.cs +++ b/M2Mqtt/Messages/MqttMsgPubrel.cs @@ -16,127 +16,117 @@ Contributors: using uPLibrary.Networking.M2Mqtt.Exceptions; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Class for PUBREL message from client top broker + /// + public class MqttMsgPubrel : MqttMsgBase { /// - /// Class for PUBREL message from client top broker + /// Constructor /// - public class MqttMsgPubrel : MqttMsgBase - { - /// - /// Constructor - /// - public MqttMsgPubrel() - { - this.type = MQTT_MSG_PUBREL_TYPE; - // PUBREL message use QoS Level 1 (not "officially" in 3.1.1) - this.qosLevel = QOS_LEVEL_AT_LEAST_ONCE; - } - - public override byte[] GetBytes(byte protocolVersion) - { - int fixedHeaderSize = 0; - int varHeaderSize = 0; - int payloadSize = 0; - int remainingLength = 0; - byte[] buffer; - int index = 0; - - // message identifier - varHeaderSize += MESSAGE_ID_SIZE; - - remainingLength += (varHeaderSize + payloadSize); - - // first byte of fixed header - fixedHeaderSize = 1; - - int temp = remainingLength; - // increase fixed header size based on remaining length - // (each remaining length byte can encode until 128) - do - { - fixedHeaderSize++; - temp = temp / 128; - } while (temp > 0); - - // allocate buffer for message - buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize]; - - // first fixed header byte - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - buffer[index++] = (MQTT_MSG_PUBREL_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBREL_FLAG_BITS; // [v.3.1.1] - else - { - buffer[index] = (byte)((MQTT_MSG_PUBREL_TYPE << MSG_TYPE_OFFSET) | - (this.qosLevel << QOS_LEVEL_OFFSET)); - buffer[index] |= this.dupFlag ? (byte)(1 << DUP_FLAG_OFFSET) : (byte)0x00; - index++; - } - - // encode remaining length - index = this.encodeRemainingLength(remainingLength, buffer, index); - - // get next message identifier - buffer[index++] = (byte)((this.messageId >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(this.messageId & 0x00FF); // LSB - - return buffer; - } - - /// - /// Parse bytes for a PUBREL message - /// - /// First fixed header byte - /// Protocol Version - /// Channel connected to the broker - /// PUBREL message instance - public static MqttMsgPubrel Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel) - { - byte[] buffer; - int index = 0; - MqttMsgPubrel msg = new MqttMsgPubrel(); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - { - // [v3.1.1] check flag bits - if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBREL_FLAG_BITS) - throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); - } - - // get remaining length and allocate buffer - int remainingLength = MqttMsgBase.decodeRemainingLength(channel); - buffer = new byte[remainingLength]; - - // read bytes from socket... - channel.Receive(buffer); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1) - { - // only 3.1.0 - - // read QoS level from fixed header (would be QoS Level 1) - msg.qosLevel = (byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET); - // read DUP flag from fixed header - msg.dupFlag = (((fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET) == 0x01); - } - - // message id - msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00); - msg.messageId |= (buffer[index++]); - - return msg; - } - - public override string ToString() - { + public MqttMsgPubrel() { + this.Type = MQTT_MSG_PUBREL_TYPE; + // PUBREL message use QoS Level 1 (not "officially" in 3.1.1) + this.QosLevel = QOS_LEVEL_AT_LEAST_ONCE; + } + + public override System.Byte[] GetBytes(System.Byte protocolVersion) { + System.Int32 varHeaderSize = 0; + System.Int32 payloadSize = 0; + System.Int32 remainingLength = 0; + System.Byte[] buffer; + System.Int32 index = 0; + + // message identifier + varHeaderSize += MESSAGE_ID_SIZE; + + remainingLength += varHeaderSize + payloadSize; + + // first byte of fixed header + System.Int32 fixedHeaderSize = 1; + + System.Int32 temp = remainingLength; + // increase fixed header size based on remaining length + // (each remaining length byte can encode until 128) + do { + fixedHeaderSize++; + temp /= 128; + } while (temp > 0); + + // allocate buffer for message + buffer = new System.Byte[fixedHeaderSize + varHeaderSize + payloadSize]; + + // first fixed header byte + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + buffer[index++] = (MQTT_MSG_PUBREL_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBREL_FLAG_BITS; // [v.3.1.1] + } else { + buffer[index] = (System.Byte)((MQTT_MSG_PUBREL_TYPE << MSG_TYPE_OFFSET) | + (this.QosLevel << QOS_LEVEL_OFFSET)); + buffer[index] |= this.DupFlag ? (System.Byte)(1 << DUP_FLAG_OFFSET) : (System.Byte)0x00; + index++; + } + + // encode remaining length + index = this.EncodeRemainingLength(remainingLength, buffer, index); + + // get next message identifier + buffer[index++] = (System.Byte)((this.MessageId >> 8) & 0x00FF); // MSB + buffer[index++] = (System.Byte)(this.MessageId & 0x00FF); // LSB + + return buffer; + } + + /// + /// Parse bytes for a PUBREL message + /// + /// First fixed header byte + /// Protocol Version + /// Channel connected to the broker + /// PUBREL message instance + public static MqttMsgPubrel Parse(System.Byte fixedHeaderFirstByte, System.Byte protocolVersion, IMqttNetworkChannel channel) { + System.Byte[] buffer; + System.Int32 index = 0; + MqttMsgPubrel msg = new MqttMsgPubrel(); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] check flag bits + if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBREL_FLAG_BITS) { + throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); + } + } + + // get remaining length and allocate buffer + System.Int32 remainingLength = MqttMsgBase.DecodeRemainingLength(channel); + buffer = new System.Byte[remainingLength]; + + // read bytes from socket... + _ = channel.Receive(buffer); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1) { + // only 3.1.0 + + // read QoS level from fixed header (would be QoS Level 1) + msg.QosLevel = (System.Byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET); + // read DUP flag from fixed header + msg.DupFlag = (fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET == 0x01; + } + + // message id + msg.MessageId = (System.UInt16)((buffer[index++] << 8) & 0xFF00); + msg.MessageId |= buffer[index++]; + + return msg; + } + + public override System.String ToString() => #if TRACE - return this.GetTraceString( - "PUBREL", - new object[] { "messageId" }, - new object[] { this.messageId }); + this.GetTraceString( + "PUBREL", + new System.Object[] { "messageId" }, + new System.Object[] { this.MessageId }); #else - return base.ToString(); + base.ToString(); #endif - } - } + + } } diff --git a/M2Mqtt/Messages/MqttMsgSuback.cs b/M2Mqtt/Messages/MqttMsgSuback.cs index 6ecfd0d..724e291 100644 --- a/M2Mqtt/Messages/MqttMsgSuback.cs +++ b/M2Mqtt/Messages/MqttMsgSuback.cs @@ -17,146 +17,127 @@ Contributors: using System; using uPLibrary.Networking.M2Mqtt.Exceptions; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Class for SUBACK message from broker to client + /// + public class MqttMsgSuback : MqttMsgBase { + #region Properties... + /// - /// Class for SUBACK message from broker to client + /// List of granted QOS Levels /// - public class MqttMsgSuback : MqttMsgBase - { - #region Properties... - - /// - /// List of granted QOS Levels - /// - public byte[] GrantedQoSLevels - { - get { return this.grantedQosLevels; } - set { this.grantedQosLevels = value; } - } - - #endregion - - // granted QOS levels - byte[] grantedQosLevels; - - /// - /// Constructor - /// - public MqttMsgSuback() - { - this.type = MQTT_MSG_SUBACK_TYPE; - } - - /// - /// Parse bytes for a SUBACK message - /// - /// First fixed header byte - /// Protocol Version - /// Channel connected to the broker - /// SUBACK message instance - public static MqttMsgSuback Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel) - { - byte[] buffer; - int index = 0; - MqttMsgSuback msg = new MqttMsgSuback(); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - { - // [v3.1.1] check flag bits - if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_SUBACK_FLAG_BITS) - throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); - } - - // get remaining length and allocate buffer - int remainingLength = MqttMsgBase.decodeRemainingLength(channel); - buffer = new byte[remainingLength]; - - // read bytes from socket... - channel.Receive(buffer); - - // message id - msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00); - msg.messageId |= (buffer[index++]); - - // payload contains QoS levels granted - msg.grantedQosLevels = new byte[remainingLength - MESSAGE_ID_SIZE]; - int qosIdx = 0; - do - { - msg.grantedQosLevels[qosIdx++] = buffer[index++]; - } while (index < remainingLength); - - return msg; - } - - public override byte[] GetBytes(byte protocolVersion) - { - int fixedHeaderSize = 0; - int varHeaderSize = 0; - int payloadSize = 0; - int remainingLength = 0; - byte[] buffer; - int index = 0; - - // message identifier - varHeaderSize += MESSAGE_ID_SIZE; - - int grantedQosIdx = 0; - for (grantedQosIdx = 0; grantedQosIdx < this.grantedQosLevels.Length; grantedQosIdx++) - { - payloadSize++; - } - - remainingLength += (varHeaderSize + payloadSize); - - // first byte of fixed header - fixedHeaderSize = 1; - - int temp = remainingLength; - // increase fixed header size based on remaining length - // (each remaining length byte can encode until 128) - do - { - fixedHeaderSize++; - temp = temp / 128; - } while (temp > 0); - - // allocate buffer for message - buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize]; - - // first fixed header byte - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - buffer[index++] = (MQTT_MSG_SUBACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_SUBACK_FLAG_BITS; // [v.3.1.1] - else - buffer[index++] = (byte)(MQTT_MSG_SUBACK_TYPE << MSG_TYPE_OFFSET); - - // encode remaining length - index = this.encodeRemainingLength(remainingLength, buffer, index); - - // message id - buffer[index++] = (byte)((this.messageId >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(this.messageId & 0x00FF); // LSB - - // payload contains QoS levels granted - for (grantedQosIdx = 0; grantedQosIdx < this.grantedQosLevels.Length; grantedQosIdx++) - { - buffer[index++] = this.grantedQosLevels[grantedQosIdx]; - } - - return buffer; - } - - public override string ToString() - { + public Byte[] GrantedQoSLevels { get; set; } + + #endregion + + // granted QOS levels + + /// + /// Constructor + /// + public MqttMsgSuback() => this.Type = MQTT_MSG_SUBACK_TYPE; + + /// + /// Parse bytes for a SUBACK message + /// + /// First fixed header byte + /// Protocol Version + /// Channel connected to the broker + /// SUBACK message instance + public static MqttMsgSuback Parse(Byte fixedHeaderFirstByte, Byte protocolVersion, IMqttNetworkChannel channel) { + Byte[] buffer; + Int32 index = 0; + MqttMsgSuback msg = new MqttMsgSuback(); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] check flag bits + if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_SUBACK_FLAG_BITS) { + throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); + } + } + + // get remaining length and allocate buffer + Int32 remainingLength = MqttMsgBase.DecodeRemainingLength(channel); + buffer = new Byte[remainingLength]; + + // read bytes from socket... + _ = channel.Receive(buffer); + + // message id + msg.MessageId = (UInt16)((buffer[index++] << 8) & 0xFF00); + msg.MessageId |= buffer[index++]; + + // payload contains QoS levels granted + msg.GrantedQoSLevels = new Byte[remainingLength - MESSAGE_ID_SIZE]; + Int32 qosIdx = 0; + do { + msg.GrantedQoSLevels[qosIdx++] = buffer[index++]; + } while (index < remainingLength); + + return msg; + } + + public override Byte[] GetBytes(Byte protocolVersion) { + Int32 varHeaderSize = 0; + Int32 payloadSize = 0; + Int32 remainingLength = 0; + Byte[] buffer; + Int32 index = 0; + + // message identifier + varHeaderSize += MESSAGE_ID_SIZE; + + Int32 grantedQosIdx; + for (grantedQosIdx = 0; grantedQosIdx < this.GrantedQoSLevels.Length; grantedQosIdx++) { + payloadSize++; + } + + remainingLength += varHeaderSize + payloadSize; + + // first byte of fixed header + Int32 fixedHeaderSize = 1; + + Int32 temp = remainingLength; + // increase fixed header size based on remaining length + // (each remaining length byte can encode until 128) + do { + fixedHeaderSize++; + temp /= 128; + } while (temp > 0); + + // allocate buffer for message + buffer = new Byte[fixedHeaderSize + varHeaderSize + payloadSize]; + + // first fixed header byte + buffer[index++] = protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1 + ? (Byte)((MQTT_MSG_SUBACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_SUBACK_FLAG_BITS) + : (Byte)(MQTT_MSG_SUBACK_TYPE << MSG_TYPE_OFFSET); + + // encode remaining length + index = this.EncodeRemainingLength(remainingLength, buffer, index); + + // message id + buffer[index++] = (Byte)((this.MessageId >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(this.MessageId & 0x00FF); // LSB + + // payload contains QoS levels granted + for (grantedQosIdx = 0; grantedQosIdx < this.GrantedQoSLevels.Length; grantedQosIdx++) { + buffer[index++] = this.GrantedQoSLevels[grantedQosIdx]; + } + + return buffer; + } + + public override String ToString() => #if TRACE - return this.GetTraceString( - "SUBACK", - new object[] { "messageId", "grantedQosLevels" }, - new object[] { this.messageId, this.grantedQosLevels }); + this.GetTraceString( + "SUBACK", + new Object[] { "messageId", "grantedQosLevels" }, + new Object[] { this.MessageId, this.GrantedQoSLevels }); #else - return base.ToString(); + base.ToString(); #endif - } - } + + } } diff --git a/M2Mqtt/Messages/MqttMsgSubscribe.cs b/M2Mqtt/Messages/MqttMsgSubscribe.cs index 0e3927e..e1d2ebf 100644 --- a/M2Mqtt/Messages/MqttMsgSubscribe.cs +++ b/M2Mqtt/Messages/MqttMsgSubscribe.cs @@ -16,257 +16,234 @@ Contributors: using System; // if NOT .Net Micro Framework -#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +#if !MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 using System.Collections.Generic; #endif using System.Collections; using System.Text; using uPLibrary.Networking.M2Mqtt.Exceptions; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Class for SUBSCRIBE message from client to broker + /// + public class MqttMsgSubscribe : MqttMsgBase { + #region Properties... + /// - /// Class for SUBSCRIBE message from client to broker + /// List of topics to subscribe /// - public class MqttMsgSubscribe : MqttMsgBase - { - #region Properties... - - /// - /// List of topics to subscribe - /// - public string[] Topics - { - get { return this.topics; } - set { this.topics = value; } - } - - /// - /// List of QOS Levels related to topics - /// - public byte[] QoSLevels - { - get { return this.qosLevels; } - set { this.qosLevels = value; } - } - - #endregion - - // topics to subscribe - string[] topics; - // QOS levels related to topics - byte[] qosLevels; - - /// - /// Constructor - /// - public MqttMsgSubscribe() - { - this.type = MQTT_MSG_SUBSCRIBE_TYPE; - } - - /// - /// Constructor - /// - /// List of topics to subscribe - /// List of QOS Levels related to topics - public MqttMsgSubscribe(string[] topics, byte[] qosLevels) - { - this.type = MQTT_MSG_SUBSCRIBE_TYPE; - - this.topics = topics; - this.qosLevels = qosLevels; - - // SUBSCRIBE message uses QoS Level 1 (not "officially" in 3.1.1) - this.qosLevel = QOS_LEVEL_AT_LEAST_ONCE; - } - - /// - /// Parse bytes for a SUBSCRIBE message - /// - /// First fixed header byte - /// Protocol Version - /// Channel connected to the broker - /// SUBSCRIBE message instance - public static MqttMsgSubscribe Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel) - { - byte[] buffer; - int index = 0; - byte[] topicUtf8; - int topicUtf8Length; - MqttMsgSubscribe msg = new MqttMsgSubscribe(); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - { - // [v3.1.1] check flag bits - if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_SUBSCRIBE_FLAG_BITS) - throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); - } - - // get remaining length and allocate buffer - int remainingLength = MqttMsgBase.decodeRemainingLength(channel); - buffer = new byte[remainingLength]; - - // read bytes from socket... - int received = channel.Receive(buffer); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1) - { - // only 3.1.0 - - // read QoS level from fixed header - msg.qosLevel = (byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET); - // read DUP flag from fixed header - msg.dupFlag = (((fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET) == 0x01); - // retain flag not used - msg.retain = false; - } - - // message id - msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00); - msg.messageId |= (buffer[index++]); - - // payload contains topics and QoS levels - // NOTE : before, I don't know how many topics will be in the payload (so use List) - -// if .Net Micro Framework -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3) + public String[] Topics { get; set; } + + /// + /// List of QOS Levels related to topics + /// + public Byte[] QoSLevels { get; set; } + + #endregion + + // topics to subscribe + + /// + /// Constructor + /// + public MqttMsgSubscribe() => this.Type = MQTT_MSG_SUBSCRIBE_TYPE; + + /// + /// Constructor + /// + /// List of topics to subscribe + /// List of QOS Levels related to topics + public MqttMsgSubscribe(String[] topics, Byte[] qosLevels) { + this.Type = MQTT_MSG_SUBSCRIBE_TYPE; + + this.Topics = topics; + this.QoSLevels = qosLevels; + + // SUBSCRIBE message uses QoS Level 1 (not "officially" in 3.1.1) + this.QosLevel = QOS_LEVEL_AT_LEAST_ONCE; + } + + /// + /// Parse bytes for a SUBSCRIBE message + /// + /// First fixed header byte + /// Protocol Version + /// Channel connected to the broker + /// SUBSCRIBE message instance + public static MqttMsgSubscribe Parse(Byte fixedHeaderFirstByte, Byte protocolVersion, IMqttNetworkChannel channel) { + Byte[] buffer; + Int32 index = 0; + Byte[] topicUtf8; + Int32 topicUtf8Length; + MqttMsgSubscribe msg = new MqttMsgSubscribe(); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] check flag bits + if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_SUBSCRIBE_FLAG_BITS) { + throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); + } + } + + // get remaining length and allocate buffer + Int32 remainingLength = MqttMsgBase.DecodeRemainingLength(channel); + buffer = new Byte[remainingLength]; + + // read bytes from socket... + Int32 received = channel.Receive(buffer); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1) { + // only 3.1.0 + + // read QoS level from fixed header + msg.QosLevel = (Byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET); + // read DUP flag from fixed header + msg.DupFlag = (fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET == 0x01; + // retain flag not used + msg.Retain = false; + } + + // message id + msg.MessageId = (UInt16)((buffer[index++] << 8) & 0xFF00); + msg.MessageId |= buffer[index++]; + + // payload contains topics and QoS levels + // NOTE : before, I don't know how many topics will be in the payload (so use List) + + // if .Net Micro Framework +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 IList tmpTopics = new ArrayList(); IList tmpQosLevels = new ArrayList(); // else other frameworks (.Net, .Net Compact, Mono, Windows Phone) #else - IList tmpTopics = new List(); - IList tmpQosLevels = new List(); + IList tmpTopics = new List(); + IList tmpQosLevels = new List(); #endif - do - { - // topic name - topicUtf8Length = ((buffer[index++] << 8) & 0xFF00); - topicUtf8Length |= buffer[index++]; - topicUtf8 = new byte[topicUtf8Length]; - Array.Copy(buffer, index, topicUtf8, 0, topicUtf8Length); - index += topicUtf8Length; - tmpTopics.Add(new String(Encoding.UTF8.GetChars(topicUtf8))); - - // QoS level - tmpQosLevels.Add(buffer[index++]); - - } while (index < remainingLength); - - // copy from list to array - msg.topics = new string[tmpTopics.Count]; - msg.qosLevels = new byte[tmpQosLevels.Count]; - for (int i = 0; i < tmpTopics.Count; i++) - { - msg.topics[i] = (string)tmpTopics[i]; - msg.qosLevels[i] = (byte)tmpQosLevels[i]; - } - - return msg; - } - - public override byte[] GetBytes(byte protocolVersion) - { - int fixedHeaderSize = 0; - int varHeaderSize = 0; - int payloadSize = 0; - int remainingLength = 0; - byte[] buffer; - int index = 0; - - // topics list empty - if ((this.topics == null) || (this.topics.Length == 0)) - throw new MqttClientException(MqttClientErrorCode.TopicsEmpty); - - // qos levels list empty - if ((this.qosLevels == null) || (this.qosLevels.Length == 0)) - throw new MqttClientException(MqttClientErrorCode.QosLevelsEmpty); - - // topics and qos levels lists length don't match - if (this.topics.Length != this.qosLevels.Length) - throw new MqttClientException(MqttClientErrorCode.TopicsQosLevelsNotMatch); - - // message identifier - varHeaderSize += MESSAGE_ID_SIZE; - - int topicIdx = 0; - byte[][] topicsUtf8 = new byte[this.topics.Length][]; - - for (topicIdx = 0; topicIdx < this.topics.Length; topicIdx++) - { - // check topic length - if ((this.topics[topicIdx].Length < MIN_TOPIC_LENGTH) || (this.topics[topicIdx].Length > MAX_TOPIC_LENGTH)) - throw new MqttClientException(MqttClientErrorCode.TopicLength); - - topicsUtf8[topicIdx] = Encoding.UTF8.GetBytes(this.topics[topicIdx]); - payloadSize += 2; // topic size (MSB, LSB) - payloadSize += topicsUtf8[topicIdx].Length; - payloadSize++; // byte for QoS - } - - remainingLength += (varHeaderSize + payloadSize); - - // first byte of fixed header - fixedHeaderSize = 1; - - int temp = remainingLength; - // increase fixed header size based on remaining length - // (each remaining length byte can encode until 128) - do - { - fixedHeaderSize++; - temp = temp / 128; - } while (temp > 0); - - // allocate buffer for message - buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize]; - - // first fixed header byte - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - buffer[index++] = (MQTT_MSG_SUBSCRIBE_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_SUBSCRIBE_FLAG_BITS; // [v.3.1.1] - else - { - buffer[index] = (byte)((MQTT_MSG_SUBSCRIBE_TYPE << MSG_TYPE_OFFSET) | - (this.qosLevel << QOS_LEVEL_OFFSET)); - buffer[index] |= this.dupFlag ? (byte)(1 << DUP_FLAG_OFFSET) : (byte)0x00; - index++; - } - - // encode remaining length - index = this.encodeRemainingLength(remainingLength, buffer, index); - - // check message identifier assigned (SUBSCRIBE uses QoS Level 1, so message id is mandatory) - if (this.messageId == 0) - throw new MqttClientException(MqttClientErrorCode.WrongMessageId); - buffer[index++] = (byte)((messageId >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(messageId & 0x00FF); // LSB - - topicIdx = 0; - for (topicIdx = 0; topicIdx < this.topics.Length; topicIdx++) - { - // topic name - buffer[index++] = (byte)((topicsUtf8[topicIdx].Length >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(topicsUtf8[topicIdx].Length & 0x00FF); // LSB - Array.Copy(topicsUtf8[topicIdx], 0, buffer, index, topicsUtf8[topicIdx].Length); - index += topicsUtf8[topicIdx].Length; - - // requested QoS - buffer[index++] = this.qosLevels[topicIdx]; - } - - return buffer; - } - - public override string ToString() - { + do { + // topic name + topicUtf8Length = (buffer[index++] << 8) & 0xFF00; + topicUtf8Length |= buffer[index++]; + topicUtf8 = new Byte[topicUtf8Length]; + Array.Copy(buffer, index, topicUtf8, 0, topicUtf8Length); + index += topicUtf8Length; + tmpTopics.Add(new String(Encoding.UTF8.GetChars(topicUtf8))); + + // QoS level + tmpQosLevels.Add(buffer[index++]); + + } while (index < remainingLength); + + // copy from list to array + msg.Topics = new String[tmpTopics.Count]; + msg.QoSLevels = new Byte[tmpQosLevels.Count]; + for (Int32 i = 0; i < tmpTopics.Count; i++) { + msg.Topics[i] = (String)tmpTopics[i]; + msg.QoSLevels[i] = (Byte)tmpQosLevels[i]; + } + + return msg; + } + + public override Byte[] GetBytes(Byte protocolVersion) { + Int32 varHeaderSize = 0; + Int32 payloadSize = 0; + Int32 remainingLength = 0; + Byte[] buffer; + Int32 index = 0; + + // topics list empty + if (this.Topics == null || this.Topics.Length == 0) { + throw new MqttClientException(MqttClientErrorCode.TopicsEmpty); + } + + // qos levels list empty + if (this.QoSLevels == null || this.QoSLevels.Length == 0) { + throw new MqttClientException(MqttClientErrorCode.QosLevelsEmpty); + } + + // topics and qos levels lists length don't match + if (this.Topics.Length != this.QoSLevels.Length) { + throw new MqttClientException(MqttClientErrorCode.TopicsQosLevelsNotMatch); + } + + // message identifier + varHeaderSize += MESSAGE_ID_SIZE; + Byte[][] topicsUtf8 = new Byte[this.Topics.Length][]; + + + Int32 topicIdx; + for (topicIdx = 0; topicIdx < this.Topics.Length; topicIdx++) { + // check topic length + if (this.Topics[topicIdx].Length < MIN_TOPIC_LENGTH || this.Topics[topicIdx].Length > MAX_TOPIC_LENGTH) { + throw new MqttClientException(MqttClientErrorCode.TopicLength); + } + + topicsUtf8[topicIdx] = Encoding.UTF8.GetBytes(this.Topics[topicIdx]); + payloadSize += 2; // topic size (MSB, LSB) + payloadSize += topicsUtf8[topicIdx].Length; + payloadSize++; // byte for QoS + } + + remainingLength += varHeaderSize + payloadSize; + + // first byte of fixed header + Int32 fixedHeaderSize = 1; + + Int32 temp = remainingLength; + // increase fixed header size based on remaining length + // (each remaining length byte can encode until 128) + do { + fixedHeaderSize++; + temp /= 128; + } while (temp > 0); + + // allocate buffer for message + buffer = new Byte[fixedHeaderSize + varHeaderSize + payloadSize]; + + // first fixed header byte + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + buffer[index++] = (MQTT_MSG_SUBSCRIBE_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_SUBSCRIBE_FLAG_BITS; // [v.3.1.1] + } else { + buffer[index] = (Byte)((MQTT_MSG_SUBSCRIBE_TYPE << MSG_TYPE_OFFSET) | + (this.QosLevel << QOS_LEVEL_OFFSET)); + buffer[index] |= this.DupFlag ? (Byte)(1 << DUP_FLAG_OFFSET) : (Byte)0x00; + index++; + } + + // encode remaining length + index = this.EncodeRemainingLength(remainingLength, buffer, index); + + // check message identifier assigned (SUBSCRIBE uses QoS Level 1, so message id is mandatory) + if (this.MessageId == 0) { + throw new MqttClientException(MqttClientErrorCode.WrongMessageId); + } + + buffer[index++] = (Byte)((this.MessageId >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(this.MessageId & 0x00FF); // LSB + + for (topicIdx = 0; topicIdx < this.Topics.Length; topicIdx++) { + // topic name + buffer[index++] = (Byte)((topicsUtf8[topicIdx].Length >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(topicsUtf8[topicIdx].Length & 0x00FF); // LSB + Array.Copy(topicsUtf8[topicIdx], 0, buffer, index, topicsUtf8[topicIdx].Length); + index += topicsUtf8[topicIdx].Length; + + // requested QoS + buffer[index++] = this.QoSLevels[topicIdx]; + } + + return buffer; + } + + public override String ToString() => #if TRACE - return this.GetTraceString( - "SUBSCRIBE", - new object[] { "messageId", "topics", "qosLevels" }, - new object[] { this.messageId, this.topics, this.qosLevels }); + this.GetTraceString( + "SUBSCRIBE", + new Object[] { "messageId", "topics", "qosLevels" }, + new Object[] { this.MessageId, this.Topics, this.QoSLevels }); #else - return base.ToString(); + base.ToString(); #endif - } - } + + } } diff --git a/M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs b/M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs index 91c85f9..a0c2c10 100644 --- a/M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs +++ b/M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs @@ -14,68 +14,48 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ -#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +#if !MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 using System; #else using Microsoft.SPOT; #endif -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Event Args class for subscribe request on topics + /// + public class MqttMsgSubscribeEventArgs : EventArgs { + #region Properties... + /// - /// Event Args class for subscribe request on topics + /// Message identifier /// - public class MqttMsgSubscribeEventArgs : EventArgs - { - #region Properties... - - /// - /// Message identifier - /// - public ushort MessageId - { - get { return this.messageId; } - internal set { this.messageId = value; } - } - - /// - /// Topics requested to subscribe - /// - public string[] Topics - { - get { return this.topics; } - internal set { this.topics = value; } - } - - /// - /// List of QOS Levels requested - /// - public byte[] QoSLevels - { - get { return this.qosLevels; } - internal set { this.qosLevels = value; } - } - - #endregion - - // message identifier - ushort messageId; - // topics requested to subscribe - string[] topics; - // QoS levels requested - byte[] qosLevels; - - /// - /// Constructor - /// - /// Message identifier for subscribe topics request - /// Topics requested to subscribe - /// List of QOS Levels requested - public MqttMsgSubscribeEventArgs(ushort messageId, string[] topics, byte[] qosLevels) - { - this.messageId = messageId; - this.topics = topics; - this.qosLevels = qosLevels; - } - } + public UInt16 MessageId { get; internal set; } + + /// + /// Topics requested to subscribe + /// + public String[] Topics { get; internal set; } + + /// + /// List of QOS Levels requested + /// + public Byte[] QoSLevels { get; internal set; } + + #endregion + + // message identifier + + /// + /// Constructor + /// + /// Message identifier for subscribe topics request + /// Topics requested to subscribe + /// List of QOS Levels requested + public MqttMsgSubscribeEventArgs(UInt16 messageId, String[] topics, Byte[] qosLevels) { + this.MessageId = messageId; + this.Topics = topics; + this.QoSLevels = qosLevels; + } + } } diff --git a/M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs b/M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs index 58bbb22..d99e7e2 100644 --- a/M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs +++ b/M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs @@ -14,55 +14,41 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ -#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +#if !MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 using System; #else using Microsoft.SPOT; #endif -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Event Args class for subscribed topics + /// + public class MqttMsgSubscribedEventArgs : EventArgs { + #region Properties... + /// - /// Event Args class for subscribed topics + /// Message identifier /// - public class MqttMsgSubscribedEventArgs : EventArgs - { - #region Properties... - - /// - /// Message identifier - /// - public ushort MessageId - { - get { return this.messageId; } - internal set { this.messageId = value; } - } - - /// - /// List of granted QOS Levels - /// - public byte[] GrantedQoSLevels - { - get { return this.grantedQosLevels; } - internal set { this.grantedQosLevels = value; } - } - - #endregion - - // message identifier - ushort messageId; - // granted QOS levels - byte[] grantedQosLevels; - - /// - /// Constructor - /// - /// Message identifier for subscribed topics - /// List of granted QOS Levels - public MqttMsgSubscribedEventArgs(ushort messageId, byte[] grantedQosLevels) - { - this.messageId = messageId; - this.grantedQosLevels = grantedQosLevels; - } - } + public UInt16 MessageId { get; internal set; } + + /// + /// List of granted QOS Levels + /// + public Byte[] GrantedQoSLevels { get; internal set; } + + #endregion + + // message identifier + + /// + /// Constructor + /// + /// Message identifier for subscribed topics + /// List of granted QOS Levels + public MqttMsgSubscribedEventArgs(UInt16 messageId, Byte[] grantedQosLevels) { + this.MessageId = messageId; + this.GrantedQoSLevels = grantedQosLevels; + } + } } diff --git a/M2Mqtt/Messages/MqttMsgUnsuback.cs b/M2Mqtt/Messages/MqttMsgUnsuback.cs index 8e49a06..481ebe1 100644 --- a/M2Mqtt/Messages/MqttMsgUnsuback.cs +++ b/M2Mqtt/Messages/MqttMsgUnsuback.cs @@ -17,110 +17,99 @@ Contributors: using System; using uPLibrary.Networking.M2Mqtt.Exceptions; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Class for UNSUBACK message from broker to client + /// + public class MqttMsgUnsuback : MqttMsgBase { /// - /// Class for UNSUBACK message from broker to client + /// Constructor /// - public class MqttMsgUnsuback : MqttMsgBase - { - /// - /// Constructor - /// - public MqttMsgUnsuback() - { - this.type = MQTT_MSG_UNSUBACK_TYPE; - } - - /// - /// Parse bytes for a UNSUBACK message - /// - /// First fixed header byte - /// Protocol Version - /// Channel connected to the broker - /// UNSUBACK message instance - public static MqttMsgUnsuback Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel) - { - byte[] buffer; - int index = 0; - MqttMsgUnsuback msg = new MqttMsgUnsuback(); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - { - // [v3.1.1] check flag bits - if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_UNSUBACK_FLAG_BITS) - throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); - } - - // get remaining length and allocate buffer - int remainingLength = MqttMsgBase.decodeRemainingLength(channel); - buffer = new byte[remainingLength]; - - // read bytes from socket... - channel.Receive(buffer); - - // message id - msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00); - msg.messageId |= (buffer[index++]); - - return msg; - } - - public override byte[] GetBytes(byte protocolVersion) - { - int fixedHeaderSize = 0; - int varHeaderSize = 0; - int payloadSize = 0; - int remainingLength = 0; - byte[] buffer; - int index = 0; - - // message identifier - varHeaderSize += MESSAGE_ID_SIZE; - - remainingLength += (varHeaderSize + payloadSize); - - // first byte of fixed header - fixedHeaderSize = 1; - - int temp = remainingLength; - // increase fixed header size based on remaining length - // (each remaining length byte can encode until 128) - do - { - fixedHeaderSize++; - temp = temp / 128; - } while (temp > 0); - - // allocate buffer for message - buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize]; - - // first fixed header byte - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - buffer[index++] = (MQTT_MSG_UNSUBACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_UNSUBACK_FLAG_BITS; // [v.3.1.1] - else - buffer[index++] = (byte)(MQTT_MSG_UNSUBACK_TYPE << MSG_TYPE_OFFSET); - - // encode remaining length - index = this.encodeRemainingLength(remainingLength, buffer, index); - - // message id - buffer[index++] = (byte)((this.messageId >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(this.messageId & 0x00FF); // LSB - - return buffer; - } - - public override string ToString() - { + public MqttMsgUnsuback() => this.Type = MQTT_MSG_UNSUBACK_TYPE; + + /// + /// Parse bytes for a UNSUBACK message + /// + /// First fixed header byte + /// Protocol Version + /// Channel connected to the broker + /// UNSUBACK message instance + public static MqttMsgUnsuback Parse(Byte fixedHeaderFirstByte, Byte protocolVersion, IMqttNetworkChannel channel) { + Byte[] buffer; + Int32 index = 0; + MqttMsgUnsuback msg = new MqttMsgUnsuback(); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] check flag bits + if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_UNSUBACK_FLAG_BITS) { + throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); + } + } + + // get remaining length and allocate buffer + Int32 remainingLength = MqttMsgBase.DecodeRemainingLength(channel); + buffer = new Byte[remainingLength]; + + // read bytes from socket... + _ = channel.Receive(buffer); + + // message id + msg.MessageId = (UInt16)((buffer[index++] << 8) & 0xFF00); + msg.MessageId |= buffer[index++]; + + return msg; + } + + public override Byte[] GetBytes(Byte protocolVersion) { + Int32 varHeaderSize = 0; + Int32 payloadSize = 0; + Int32 remainingLength = 0; + Byte[] buffer; + Int32 index = 0; + + // message identifier + varHeaderSize += MESSAGE_ID_SIZE; + + remainingLength += varHeaderSize + payloadSize; + + // first byte of fixed header + Int32 fixedHeaderSize = 1; + + Int32 temp = remainingLength; + // increase fixed header size based on remaining length + // (each remaining length byte can encode until 128) + do { + fixedHeaderSize++; + temp /= 128; + } while (temp > 0); + + // allocate buffer for message + buffer = new Byte[fixedHeaderSize + varHeaderSize + payloadSize]; + + // first fixed header byte + buffer[index++] = protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1 + ? (Byte)((MQTT_MSG_UNSUBACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_UNSUBACK_FLAG_BITS) + : (Byte)(MQTT_MSG_UNSUBACK_TYPE << MSG_TYPE_OFFSET); + + // encode remaining length + index = this.EncodeRemainingLength(remainingLength, buffer, index); + + // message id + buffer[index++] = (Byte)((this.MessageId >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(this.MessageId & 0x00FF); // LSB + + return buffer; + } + + public override String ToString() => #if TRACE - return this.GetTraceString( - "UNSUBACK", - new object[] { "messageId" }, - new object[] { this.messageId }); + this.GetTraceString( + "UNSUBACK", + new Object[] { "messageId" }, + new Object[] { this.MessageId }); #else - return base.ToString(); + base.ToString(); #endif - } - } + + } } diff --git a/M2Mqtt/Messages/MqttMsgUnsubscribe.cs b/M2Mqtt/Messages/MqttMsgUnsubscribe.cs index e485133..d122940 100644 --- a/M2Mqtt/Messages/MqttMsgUnsubscribe.cs +++ b/M2Mqtt/Messages/MqttMsgUnsubscribe.cs @@ -16,224 +16,205 @@ Contributors: using System; // if NOT .Net Micro Framework -#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +#if !MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 using System.Collections.Generic; #endif using System.Collections; using System.Text; using uPLibrary.Networking.M2Mqtt.Exceptions; -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Class for UNSUBSCRIBE message from client to broker + /// + public class MqttMsgUnsubscribe : MqttMsgBase { + #region Properties... + /// - /// Class for UNSUBSCRIBE message from client to broker + /// List of topics to unsubscribe /// - public class MqttMsgUnsubscribe : MqttMsgBase - { - #region Properties... - - /// - /// List of topics to unsubscribe - /// - public string[] Topics - { - get { return this.topics; } - set { this.topics = value; } - } - - #endregion - - // topics to unsubscribe - string[] topics; - - /// - /// Constructor - /// - public MqttMsgUnsubscribe() - { - this.type = MQTT_MSG_UNSUBSCRIBE_TYPE; - } - - /// - /// Constructor - /// - /// List of topics to unsubscribe - public MqttMsgUnsubscribe(string[] topics) - { - this.type = MQTT_MSG_UNSUBSCRIBE_TYPE; - - this.topics = topics; - - // UNSUBSCRIBE message uses QoS Level 1 (not "officially" in 3.1.1) - this.qosLevel = QOS_LEVEL_AT_LEAST_ONCE; - } - - /// - /// Parse bytes for a UNSUBSCRIBE message - /// - /// First fixed header byte - /// Protocol Version - /// Channel connected to the broker - /// UNSUBSCRIBE message instance - public static MqttMsgUnsubscribe Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel) - { - byte[] buffer; - int index = 0; - byte[] topicUtf8; - int topicUtf8Length; - MqttMsgUnsubscribe msg = new MqttMsgUnsubscribe(); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - { - // [v3.1.1] check flag bits - if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_UNSUBSCRIBE_FLAG_BITS) - throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); - } - - // get remaining length and allocate buffer - int remainingLength = MqttMsgBase.decodeRemainingLength(channel); - buffer = new byte[remainingLength]; - - // read bytes from socket... - int received = channel.Receive(buffer); - - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1) - { - // only 3.1.0 - - // read QoS level from fixed header - msg.qosLevel = (byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET); - // read DUP flag from fixed header - msg.dupFlag = (((fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET) == 0x01); - // retain flag not used - msg.retain = false; - } - - // message id - msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00); - msg.messageId |= (buffer[index++]); - - // payload contains topics - // NOTE : before, I don't know how many topics will be in the payload (so use List) - -// if .Net Micro Framework -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3) + public String[] Topics { get; set; } + + #endregion + + // topics to unsubscribe + + /// + /// Constructor + /// + public MqttMsgUnsubscribe() => this.Type = MQTT_MSG_UNSUBSCRIBE_TYPE; + + /// + /// Constructor + /// + /// List of topics to unsubscribe + public MqttMsgUnsubscribe(String[] topics) { + this.Type = MQTT_MSG_UNSUBSCRIBE_TYPE; + + this.Topics = topics; + + // UNSUBSCRIBE message uses QoS Level 1 (not "officially" in 3.1.1) + this.QosLevel = QOS_LEVEL_AT_LEAST_ONCE; + } + + /// + /// Parse bytes for a UNSUBSCRIBE message + /// + /// First fixed header byte + /// Protocol Version + /// Channel connected to the broker + /// UNSUBSCRIBE message instance + public static MqttMsgUnsubscribe Parse(Byte fixedHeaderFirstByte, Byte protocolVersion, IMqttNetworkChannel channel) { + Byte[] buffer; + Int32 index = 0; + Byte[] topicUtf8; + Int32 topicUtf8Length; + MqttMsgUnsubscribe msg = new MqttMsgUnsubscribe(); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + // [v3.1.1] check flag bits + if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_UNSUBSCRIBE_FLAG_BITS) { + throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits); + } + } + + // get remaining length and allocate buffer + Int32 remainingLength = DecodeRemainingLength(channel); + buffer = new Byte[remainingLength]; + + // read bytes from socket... + Int32 received = channel.Receive(buffer); + + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1) { + // only 3.1.0 + + // read QoS level from fixed header + msg.QosLevel = (Byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET); + // read DUP flag from fixed header + msg.DupFlag = (fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET == 0x01; + // retain flag not used + msg.Retain = false; + } + + // message id + msg.MessageId = (UInt16)((buffer[index++] << 8) & 0xFF00); + msg.MessageId |= buffer[index++]; + + // payload contains topics + // NOTE : before, I don't know how many topics will be in the payload (so use List) + + // if .Net Micro Framework +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 IList tmpTopics = new ArrayList(); // else other frameworks (.Net, .Net Compact, Mono, Windows Phone) #else - IList tmpTopics = new List(); + IList tmpTopics = new List(); #endif - do - { - // topic name - topicUtf8Length = ((buffer[index++] << 8) & 0xFF00); - topicUtf8Length |= buffer[index++]; - topicUtf8 = new byte[topicUtf8Length]; - Array.Copy(buffer, index, topicUtf8, 0, topicUtf8Length); - index += topicUtf8Length; - tmpTopics.Add(new String(Encoding.UTF8.GetChars(topicUtf8))); - } while (index < remainingLength); - - // copy from list to array - msg.topics = new string[tmpTopics.Count]; - for (int i = 0; i < tmpTopics.Count; i++) - { - msg.topics[i] = (string)tmpTopics[i]; - } - - return msg; - } - - public override byte[] GetBytes(byte protocolVersion) - { - int fixedHeaderSize = 0; - int varHeaderSize = 0; - int payloadSize = 0; - int remainingLength = 0; - byte[] buffer; - int index = 0; - - // topics list empty - if ((this.topics == null) || (this.topics.Length == 0)) - throw new MqttClientException(MqttClientErrorCode.TopicsEmpty); - - // message identifier - varHeaderSize += MESSAGE_ID_SIZE; - - int topicIdx = 0; - byte[][] topicsUtf8 = new byte[this.topics.Length][]; - - for (topicIdx = 0; topicIdx < this.topics.Length; topicIdx++) - { - // check topic length - if ((this.topics[topicIdx].Length < MIN_TOPIC_LENGTH) || (this.topics[topicIdx].Length > MAX_TOPIC_LENGTH)) - throw new MqttClientException(MqttClientErrorCode.TopicLength); - - topicsUtf8[topicIdx] = Encoding.UTF8.GetBytes(this.topics[topicIdx]); - payloadSize += 2; // topic size (MSB, LSB) - payloadSize += topicsUtf8[topicIdx].Length; - } - - remainingLength += (varHeaderSize + payloadSize); - - // first byte of fixed header - fixedHeaderSize = 1; - - int temp = remainingLength; - // increase fixed header size based on remaining length - // (each remaining length byte can encode until 128) - do - { - fixedHeaderSize++; - temp = temp / 128; - } while (temp > 0); - - // allocate buffer for message - buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize]; - - // first fixed header byte - if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) - buffer[index++] = (MQTT_MSG_UNSUBSCRIBE_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_UNSUBSCRIBE_FLAG_BITS; // [v.3.1.1] - else - { - buffer[index] = (byte)((MQTT_MSG_UNSUBSCRIBE_TYPE << MSG_TYPE_OFFSET) | - (this.qosLevel << QOS_LEVEL_OFFSET)); - buffer[index] |= this.dupFlag ? (byte)(1 << DUP_FLAG_OFFSET) : (byte)0x00; - index++; - } - - // encode remaining length - index = this.encodeRemainingLength(remainingLength, buffer, index); - - // check message identifier assigned - if (this.messageId == 0) - throw new MqttClientException(MqttClientErrorCode.WrongMessageId); - buffer[index++] = (byte)((messageId >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(messageId & 0x00FF); // LSB - - topicIdx = 0; - for (topicIdx = 0; topicIdx < this.topics.Length; topicIdx++) - { - // topic name - buffer[index++] = (byte)((topicsUtf8[topicIdx].Length >> 8) & 0x00FF); // MSB - buffer[index++] = (byte)(topicsUtf8[topicIdx].Length & 0x00FF); // LSB - Array.Copy(topicsUtf8[topicIdx], 0, buffer, index, topicsUtf8[topicIdx].Length); - index += topicsUtf8[topicIdx].Length; - } - - return buffer; - } - - public override string ToString() - { + do { + // topic name + topicUtf8Length = (buffer[index++] << 8) & 0xFF00; + topicUtf8Length |= buffer[index++]; + topicUtf8 = new Byte[topicUtf8Length]; + Array.Copy(buffer, index, topicUtf8, 0, topicUtf8Length); + index += topicUtf8Length; + tmpTopics.Add(new String(Encoding.UTF8.GetChars(topicUtf8))); + } while (index < remainingLength); + + // copy from list to array + msg.Topics = new String[tmpTopics.Count]; + for (Int32 i = 0; i < tmpTopics.Count; i++) { + msg.Topics[i] = (String)tmpTopics[i]; + } + + return msg; + } + + public override Byte[] GetBytes(Byte protocolVersion) { + Int32 varHeaderSize = 0; + Int32 payloadSize = 0; + Int32 remainingLength = 0; + Byte[] buffer; + Int32 index = 0; + + // topics list empty + if (this.Topics == null || this.Topics.Length == 0) { + throw new MqttClientException(MqttClientErrorCode.TopicsEmpty); + } + + // message identifier + varHeaderSize += MESSAGE_ID_SIZE; + Byte[][] topicsUtf8 = new Byte[this.Topics.Length][]; + + + Int32 topicIdx; + for (topicIdx = 0; topicIdx < this.Topics.Length; topicIdx++) { + // check topic length + if (this.Topics[topicIdx].Length < MIN_TOPIC_LENGTH || this.Topics[topicIdx].Length > MAX_TOPIC_LENGTH) { + throw new MqttClientException(MqttClientErrorCode.TopicLength); + } + + topicsUtf8[topicIdx] = Encoding.UTF8.GetBytes(this.Topics[topicIdx]); + payloadSize += 2; // topic size (MSB, LSB) + payloadSize += topicsUtf8[topicIdx].Length; + } + + remainingLength += varHeaderSize + payloadSize; + + // first byte of fixed header + Int32 fixedHeaderSize = 1; + + Int32 temp = remainingLength; + // increase fixed header size based on remaining length + // (each remaining length byte can encode until 128) + do { + fixedHeaderSize++; + temp /= 128; + } while (temp > 0); + + // allocate buffer for message + buffer = new Byte[fixedHeaderSize + varHeaderSize + payloadSize]; + + // first fixed header byte + if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1) { + buffer[index++] = (MQTT_MSG_UNSUBSCRIBE_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_UNSUBSCRIBE_FLAG_BITS; // [v.3.1.1] + } else { + buffer[index] = (Byte)((MQTT_MSG_UNSUBSCRIBE_TYPE << MSG_TYPE_OFFSET) | + (this.QosLevel << QOS_LEVEL_OFFSET)); + buffer[index] |= this.DupFlag ? (Byte)(1 << DUP_FLAG_OFFSET) : (Byte)0x00; + index++; + } + + // encode remaining length + index = this.EncodeRemainingLength(remainingLength, buffer, index); + + // check message identifier assigned + if (this.MessageId == 0) { + throw new MqttClientException(MqttClientErrorCode.WrongMessageId); + } + + buffer[index++] = (Byte)((this.MessageId >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(this.MessageId & 0x00FF); // LSB + + for (topicIdx = 0; topicIdx < this.Topics.Length; topicIdx++) { + // topic name + buffer[index++] = (Byte)((topicsUtf8[topicIdx].Length >> 8) & 0x00FF); // MSB + buffer[index++] = (Byte)(topicsUtf8[topicIdx].Length & 0x00FF); // LSB + Array.Copy(topicsUtf8[topicIdx], 0, buffer, index, topicsUtf8[topicIdx].Length); + index += topicsUtf8[topicIdx].Length; + } + + return buffer; + } + + public override String ToString() => #if TRACE - return this.GetTraceString( - "UNSUBSCRIBE", - new object[] { "messageId", "topics" }, - new object[] { this.messageId, this.topics }); + this.GetTraceString( + "UNSUBSCRIBE", + new Object[] { "messageId", "topics" }, + new Object[] { this.MessageId, this.Topics }); #else - return base.ToString(); + base.ToString(); #endif - } - } + + } } diff --git a/M2Mqtt/Messages/MqttMsgUnsubscribeEventArgs.cs b/M2Mqtt/Messages/MqttMsgUnsubscribeEventArgs.cs index e158783..1f36052 100644 --- a/M2Mqtt/Messages/MqttMsgUnsubscribeEventArgs.cs +++ b/M2Mqtt/Messages/MqttMsgUnsubscribeEventArgs.cs @@ -14,55 +14,41 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ -#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +#if !MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 using System; #else using Microsoft.SPOT; #endif -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Event Args class for unsubscribe request on topics + /// + public class MqttMsgUnsubscribeEventArgs : EventArgs { + #region Properties... + /// - /// Event Args class for unsubscribe request on topics + /// Message identifier /// - public class MqttMsgUnsubscribeEventArgs : EventArgs - { - #region Properties... - - /// - /// Message identifier - /// - public ushort MessageId - { - get { return this.messageId; } - internal set { this.messageId = value; } - } - - /// - /// Topics requested to subscribe - /// - public string[] Topics - { - get { return this.topics; } - internal set { this.topics = value; } - } - - #endregion - - // message identifier - ushort messageId; - // topics requested to unsubscribe - string[] topics; - - /// - /// Constructor - /// - /// Message identifier for subscribed topics - /// Topics requested to subscribe - public MqttMsgUnsubscribeEventArgs(ushort messageId, string[] topics) - { - this.messageId = messageId; - this.topics = topics; - } - } + public UInt16 MessageId { get; internal set; } + + /// + /// Topics requested to subscribe + /// + public String[] Topics { get; internal set; } + + #endregion + + // message identifier + + /// + /// Constructor + /// + /// Message identifier for subscribed topics + /// Topics requested to subscribe + public MqttMsgUnsubscribeEventArgs(UInt16 messageId, String[] topics) { + this.MessageId = messageId; + this.Topics = topics; + } + } } diff --git a/M2Mqtt/Messages/MqttMsgUnsubscribedEventArgs.cs b/M2Mqtt/Messages/MqttMsgUnsubscribedEventArgs.cs index e65c383..6b7d681 100644 --- a/M2Mqtt/Messages/MqttMsgUnsubscribedEventArgs.cs +++ b/M2Mqtt/Messages/MqttMsgUnsubscribedEventArgs.cs @@ -14,42 +14,32 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ -#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +#if !MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 using System; #else using Microsoft.SPOT; #endif -namespace uPLibrary.Networking.M2Mqtt.Messages -{ +namespace uPLibrary.Networking.M2Mqtt.Messages { + /// + /// Event Args class for unsubscribed topic + /// + public class MqttMsgUnsubscribedEventArgs : EventArgs { + #region Properties... + /// - /// Event Args class for unsubscribed topic + /// Message identifier /// - public class MqttMsgUnsubscribedEventArgs : EventArgs - { - #region Properties... - - /// - /// Message identifier - /// - public ushort MessageId - { - get { return this.messageId; } - internal set { this.messageId = value; } - } - - #endregion - - // message identifier - ushort messageId; - - /// - /// Constructor - /// - /// Message identifier for unsubscribed topic - public MqttMsgUnsubscribedEventArgs(ushort messageId) - { - this.messageId = messageId; - } - } + public UInt16 MessageId { get; internal set; } + + #endregion + + // message identifier + + /// + /// Constructor + /// + /// Message identifier for unsubscribed topic + public MqttMsgUnsubscribedEventArgs(UInt16 messageId) => this.MessageId = messageId; + } } diff --git a/M2Mqtt/MqttClient.cs b/M2Mqtt/MqttClient.cs index 6daf7a2..f501b6a 100644 --- a/M2Mqtt/MqttClient.cs +++ b/M2Mqtt/MqttClient.cs @@ -27,7 +27,7 @@ using uPLibrary.Networking.M2Mqtt.Session; using uPLibrary.Networking.M2Mqtt.Utility; using uPLibrary.Networking.M2Mqtt.Internal; // if .Net Micro Framework -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3) +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 using Microsoft.SPOT; #if SSL using Microsoft.SPOT.Net.Security; @@ -35,13 +35,13 @@ using Microsoft.SPOT.Net.Security; // else other frameworks (.Net, .Net Compact, Mono, Windows Phone) #else using System.Collections.Generic; -#if (SSL && !(WINDOWS_APP || WINDOWS_PHONE_APP)) +#if SSL && !(WINDOWS_APP || WINDOWS_PHONE_APP) using System.Security.Authentication; using System.Net.Security; #endif #endif -#if (WINDOWS_APP || WINDOWS_PHONE_APP) +#if WINDOWS_APP || WINDOWS_PHONE_APP using Windows.Networking.Sockets; #endif @@ -51,184 +51,176 @@ using System.Collections; // (it's ambiguos with uPLibrary.Networking.M2Mqtt.Utility.Trace) using MqttUtility = uPLibrary.Networking.M2Mqtt.Utility; using System.IO; -//using System.Net.Security; -namespace uPLibrary.Networking.M2Mqtt -{ +namespace uPLibrary.Networking.M2Mqtt { + /// + /// MQTT Client + /// + public class MqttClient { + #if BROKER + #region Constants ... + // thread names + private const string RECEIVE_THREAD_NAME = "ReceiveThread"; + private const string RECEIVE_EVENT_THREAD_NAME = "DispatchEventThread"; + private const string PROCESS_INFLIGHT_THREAD_NAME = "ProcessInflightThread"; + private const string KEEP_ALIVE_THREAD = "KeepAliveThread"; + #endregion + #endif + /// - /// MQTT Client + /// Delagate that defines event handler for PUBLISH message received /// - public class MqttClient - { -#if BROKER - #region Constants ... + public delegate void MqttMsgPublishEventHandler(Object sender, MqttMsgPublishEventArgs e); - // thread names - private const string RECEIVE_THREAD_NAME = "ReceiveThread"; - private const string RECEIVE_EVENT_THREAD_NAME = "DispatchEventThread"; - private const string PROCESS_INFLIGHT_THREAD_NAME = "ProcessInflightThread"; - private const string KEEP_ALIVE_THREAD = "KeepAliveThread"; + /// + /// Delegate that defines event handler for published message + /// + public delegate void MqttMsgPublishedEventHandler(Object sender, MqttMsgPublishedEventArgs e); - #endregion -#endif + /// + /// Delagate that defines event handler for subscribed topic + /// + public delegate void MqttMsgSubscribedEventHandler(Object sender, MqttMsgSubscribedEventArgs e); - /// - /// Delagate that defines event handler for PUBLISH message received - /// - public delegate void MqttMsgPublishEventHandler(object sender, MqttMsgPublishEventArgs e); + /// + /// Delagate that defines event handler for unsubscribed topic + /// + public delegate void MqttMsgUnsubscribedEventHandler(Object sender, MqttMsgUnsubscribedEventArgs e); - /// - /// Delegate that defines event handler for published message - /// - public delegate void MqttMsgPublishedEventHandler(object sender, MqttMsgPublishedEventArgs e); + #if BROKER + /// + /// Delagate that defines event handler for SUBSCRIBE message received + /// + public delegate void MqttMsgSubscribeEventHandler(object sender, MqttMsgSubscribeEventArgs e); - /// - /// Delagate that defines event handler for subscribed topic - /// - public delegate void MqttMsgSubscribedEventHandler(object sender, MqttMsgSubscribedEventArgs e); + /// + /// Delagate that defines event handler for UNSUBSCRIBE message received + /// + public delegate void MqttMsgUnsubscribeEventHandler(object sender, MqttMsgUnsubscribeEventArgs e); - /// - /// Delagate that defines event handler for unsubscribed topic - /// - public delegate void MqttMsgUnsubscribedEventHandler(object sender, MqttMsgUnsubscribedEventArgs e); + /// + /// Delagate that defines event handler for CONNECT message received + /// + public delegate void MqttMsgConnectEventHandler(object sender, MqttMsgConnectEventArgs e); -#if BROKER - /// - /// Delagate that defines event handler for SUBSCRIBE message received - /// - public delegate void MqttMsgSubscribeEventHandler(object sender, MqttMsgSubscribeEventArgs e); + /// + /// Delegate that defines event handler for client disconnection (DISCONNECT message or not) + /// + public delegate void MqttMsgDisconnectEventHandler(object sender, EventArgs e); + #endif - /// - /// Delagate that defines event handler for UNSUBSCRIBE message received - /// - public delegate void MqttMsgUnsubscribeEventHandler(object sender, MqttMsgUnsubscribeEventArgs e); + /// + /// Delegate that defines event handler for cliet/peer disconnection + /// + public delegate void ConnectionClosedEventHandler(Object sender, EventArgs e); - /// - /// Delagate that defines event handler for CONNECT message received - /// - public delegate void MqttMsgConnectEventHandler(object sender, MqttMsgConnectEventArgs e); + // broker hostname (or ip address) and port + private String brokerHostName; + private Int32 brokerPort; - /// - /// Delegate that defines event handler for client disconnection (DISCONNECT message or not) - /// - public delegate void MqttMsgDisconnectEventHandler(object sender, EventArgs e); -#endif + // running status of threads + private Boolean isRunning; + // event for raising received message event + private AutoResetEvent receiveEventWaitHandle; - /// - /// Delegate that defines event handler for cliet/peer disconnection - /// - public delegate void ConnectionClosedEventHandler(object sender, EventArgs e); + // event for starting process inflight queue asynchronously + private AutoResetEvent inflightWaitHandle; - // broker hostname (or ip address) and port - private string brokerHostName; - private int brokerPort; + // event for signaling synchronous receive + AutoResetEvent syncEndReceiving; + // message received + MqttMsgBase msgReceived; - // running status of threads - private bool isRunning; - // event for raising received message event - private AutoResetEvent receiveEventWaitHandle; + // exeption thrown during receiving + Exception exReceiving; - // event for starting process inflight queue asynchronously - private AutoResetEvent inflightWaitHandle; + // keep alive period (in ms) + private Int32 keepAlivePeriod; + // events for signaling on keep alive thread + private AutoResetEvent keepAliveEvent; + private AutoResetEvent keepAliveEventEnd; + // last communication time in ticks + private Int32 lastCommTime; - // event for signaling synchronous receive - AutoResetEvent syncEndReceiving; - // message received - MqttMsgBase msgReceived; + // event for PUBLISH message received + public event MqttMsgPublishEventHandler MqttMsgPublishReceived; + // event for published message + public event MqttMsgPublishedEventHandler MqttMsgPublished; + // event for subscribed topic + public event MqttMsgSubscribedEventHandler MqttMsgSubscribed; + // event for unsubscribed topic + public event MqttMsgUnsubscribedEventHandler MqttMsgUnsubscribed; + #if BROKER + // event for SUBSCRIBE message received + public event MqttMsgSubscribeEventHandler MqttMsgSubscribeReceived; + // event for USUBSCRIBE message received + public event MqttMsgUnsubscribeEventHandler MqttMsgUnsubscribeReceived; + // event for CONNECT message received + public event MqttMsgConnectEventHandler MqttMsgConnected; + // event for DISCONNECT message received + public event MqttMsgDisconnectEventHandler MqttMsgDisconnected; + #endif - // exeption thrown during receiving - Exception exReceiving; + // event for peer/client disconnection + public event ConnectionClosedEventHandler ConnectionClosed; - // keep alive period (in ms) - private int keepAlivePeriod; - // events for signaling on keep alive thread - private AutoResetEvent keepAliveEvent; - private AutoResetEvent keepAliveEventEnd; - // last communication time in ticks - private int lastCommTime; + // channel to communicate over the network + private IMqttNetworkChannel channel; - // event for PUBLISH message received - public event MqttMsgPublishEventHandler MqttMsgPublishReceived; - // event for published message - public event MqttMsgPublishedEventHandler MqttMsgPublished; - // event for subscribed topic - public event MqttMsgSubscribedEventHandler MqttMsgSubscribed; - // event for unsubscribed topic - public event MqttMsgUnsubscribedEventHandler MqttMsgUnsubscribed; -#if BROKER - // event for SUBSCRIBE message received - public event MqttMsgSubscribeEventHandler MqttMsgSubscribeReceived; - // event for USUBSCRIBE message received - public event MqttMsgUnsubscribeEventHandler MqttMsgUnsubscribeReceived; - // event for CONNECT message received - public event MqttMsgConnectEventHandler MqttMsgConnected; - // event for DISCONNECT message received - public event MqttMsgDisconnectEventHandler MqttMsgDisconnected; -#endif + // inflight messages queue + private Queue inflightQueue; + // internal queue for received messages about inflight messages + private Queue internalQueue; + // internal queue for dispatching events + private Queue eventQueue; + // session + private MqttClientSession session; - // event for peer/client disconnection - public event ConnectionClosedEventHandler ConnectionClosed; - - // channel to communicate over the network - private IMqttNetworkChannel channel; + // current message identifier generated + private UInt16 messageIdCounter = 0; - // inflight messages queue - private Queue inflightQueue; - // internal queue for received messages about inflight messages - private Queue internalQueue; - // internal queue for dispatching events - private Queue eventQueue; - // session - private MqttClientSession session; + // connection is closing due to peer + private Boolean isConnectionClosing; - // reference to avoid access to singleton via property - private MqttSettings settings; + /// + /// Connection status between client and broker + /// + public Boolean IsConnected { get; private set; } - // current message identifier generated - private ushort messageIdCounter = 0; + /// + /// Client identifier + /// + public String ClientId { get; private set; } - // connection is closing due to peer - private bool isConnectionClosing; + /// + /// Clean session flag + /// + public Boolean CleanSession { get; private set; } - /// - /// Connection status between client and broker - /// - public bool IsConnected { get; private set; } + /// + /// Will flag + /// + public Boolean WillFlag { get; private set; } - /// - /// Client identifier - /// - public string ClientId { get; private set; } + /// + /// Will QOS level + /// + public Byte WillQosLevel { get; private set; } - /// - /// Clean session flag - /// - public bool CleanSession { get; private set; } + /// + /// Will topic + /// + public String WillTopic { get; private set; } - /// - /// Will flag - /// - public bool WillFlag { get; private set; } + /// + /// Will message + /// + public String WillMessage { get; private set; } - /// - /// Will QOS level - /// - public byte WillQosLevel { get; private set; } - - /// - /// Will topic - /// - public string WillTopic { get; private set; } - - /// - /// Will message - /// - public string WillMessage { get; private set; } - - /// - /// MQTT protocol version - /// - public MqttProtocolVersion ProtocolVersion { get; set; } + /// + /// MQTT protocol version + /// + public MqttProtocolVersion ProtocolVersion { get; set; } #if BROKER /// @@ -241,134 +233,121 @@ namespace uPLibrary.Networking.M2Mqtt } #endif - /// - /// MQTT client settings - /// - public MqttSettings Settings - { - get { return this.settings; } - } + /// + /// MQTT client settings + /// + public MqttSettings Settings { get; private set; } -#if !(WINDOWS_APP || WINDOWS_PHONE_APP) - /// - /// Constructor - /// - /// Broker IP address - [Obsolete("Use this ctor MqttClient(string brokerHostName) insted")] - public MqttClient(IPAddress brokerIpAddress) : - this(brokerIpAddress, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, null, null, MqttSslProtocols.None) - { - } +#if !(WINDOWS_APP || WINDOWS_PHONE_APP) + /// + /// Constructor + /// + /// Broker IP address + [Obsolete("Use this ctor MqttClient(string brokerHostName) insted")] + public MqttClient(IPAddress brokerIpAddress) : this(brokerIpAddress, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, null, null, MqttSslProtocols.None) { + } - /// - /// Constructor - /// - /// Broker IP address - /// Broker port - /// Using secure connection - /// CA certificate for secure connection - /// Client certificate - /// SSL/TLS protocol version - [Obsolete("Use this ctor MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert) insted")] - public MqttClient(IPAddress brokerIpAddress, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol) - { + /// + /// Constructor + /// + /// Broker IP address + /// Broker port + /// Using secure connection + /// CA certificate for secure connection + /// Client certificate + /// SSL/TLS protocol version + [Obsolete("Use this ctor MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert) insted")] + public MqttClient(IPAddress brokerIpAddress, Int32 brokerPort, Boolean secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol) => #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) - this.Init(brokerIpAddress.ToString(), brokerPort, secure, caCert, clientCert, sslProtocol, null, null); + this.Init(brokerIpAddress.ToString(), brokerPort, secure, caCert, clientCert, sslProtocol, null, null); #else this.Init(brokerIpAddress.ToString(), brokerPort, secure, caCert, clientCert, sslProtocol); #endif - } #endif - /// - /// Constructor - /// - /// Broker Host Name or IP Address - public MqttClient(string brokerHostName) : + /// + /// Constructor + /// + /// Broker Host Name or IP Address + public MqttClient(String brokerHostName) : #if !(WINDOWS_APP || WINDOWS_PHONE_APP) this(brokerHostName, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, null, null, MqttSslProtocols.None) #else this(brokerHostName, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, MqttSslProtocols.None) #endif { - } + } - /// - /// Constructor - /// - /// Broker Host Name or IP Address - /// Broker port - /// Using secure connection - /// SSL/TLS protocol version + /// + /// Constructor + /// + /// Broker Host Name or IP Address + /// Broker port + /// Using secure connection + /// SSL/TLS protocol version #if !(WINDOWS_APP || WINDOWS_PHONE_APP) - /// CA certificate for secure connection - /// Client certificate - public MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol) + /// CA certificate for secure connection + /// Client certificate + public MqttClient(String brokerHostName, Int32 brokerPort, Boolean secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol) => #else - public MqttClient(string brokerHostName, int brokerPort, bool secure, MqttSslProtocols sslProtocol) + public MqttClient(string brokerHostName, int brokerPort, bool secure, MqttSslProtocols sslProtocol) => #endif - { #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP) - this.Init(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol, null, null); -#elif (WINDOWS_APP || WINDOWS_PHONE_APP) + this.Init(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol, null, null); +#elif WINDOWS_APP || WINDOWS_PHONE_APP this.Init(brokerHostName, brokerPort, secure, sslProtocol); #else this.Init(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol); #endif - } + #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP) - /// - /// Constructor - /// - /// Broker Host Name or IP Address - /// Broker port - /// Using secure connection - /// CA certificate for secure connection - /// Client certificate - /// SSL/TLS protocol version - /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party - public MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol, - RemoteCertificateValidationCallback userCertificateValidationCallback) - : this(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol, userCertificateValidationCallback, null) - { - } + /// + /// Constructor + /// + /// Broker Host Name or IP Address + /// Broker port + /// Using secure connection + /// CA certificate for secure connection + /// Client certificate + /// SSL/TLS protocol version + /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party + public MqttClient(String brokerHostName, Int32 brokerPort, Boolean secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol, + RemoteCertificateValidationCallback userCertificateValidationCallback) + : this(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol, userCertificateValidationCallback, null) { + } - /// - /// Constructor - /// - /// Broker Host Name or IP Address - /// Broker port - /// Using secure connection - /// SSL/TLS protocol version - /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party - /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication - public MqttClient(string brokerHostName, int brokerPort, bool secure, MqttSslProtocols sslProtocol, - RemoteCertificateValidationCallback userCertificateValidationCallback, - LocalCertificateSelectionCallback userCertificateSelectionCallback) - : this(brokerHostName, brokerPort, secure, null, null, sslProtocol, userCertificateValidationCallback, userCertificateSelectionCallback) - { - } + /// + /// Constructor + /// + /// Broker Host Name or IP Address + /// Broker port + /// Using secure connection + /// SSL/TLS protocol version + /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party + /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication + public MqttClient(String brokerHostName, Int32 brokerPort, Boolean secure, MqttSslProtocols sslProtocol, + RemoteCertificateValidationCallback userCertificateValidationCallback, + LocalCertificateSelectionCallback userCertificateSelectionCallback) + : this(brokerHostName, brokerPort, secure, null, null, sslProtocol, userCertificateValidationCallback, userCertificateSelectionCallback) { + } - /// - /// Constructor - /// - /// Broker Host Name or IP Address - /// Broker port - /// Using secure connection - /// CA certificate for secure connection - /// Client certificate - /// SSL/TLS protocol version - /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party - /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication - public MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol, - RemoteCertificateValidationCallback userCertificateValidationCallback, - LocalCertificateSelectionCallback userCertificateSelectionCallback) - { - this.Init(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol, userCertificateValidationCallback, userCertificateSelectionCallback); - } + /// + /// Constructor + /// + /// Broker Host Name or IP Address + /// Broker port + /// Using secure connection + /// CA certificate for secure connection + /// Client certificate + /// SSL/TLS protocol version + /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party + /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication + public MqttClient(String brokerHostName, Int32 brokerPort, Boolean secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol, + RemoteCertificateValidationCallback userCertificateValidationCallback, + LocalCertificateSelectionCallback userCertificateSelectionCallback) => this.Init(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol, userCertificateValidationCallback, userCertificateSelectionCallback); #endif #if BROKER @@ -407,213 +386,199 @@ namespace uPLibrary.Networking.M2Mqtt } #endif - /// - /// MqttClient initialization - /// - /// Broker Host Name or IP Address - /// Broker port - /// >Using secure connection - /// CA certificate for secure connection - /// Client certificate - /// SSL/TLS protocol version + /// + /// MqttClient initialization + /// + /// Broker Host Name or IP Address + /// Broker port + /// >Using secure connection + /// CA certificate for secure connection + /// Client certificate + /// SSL/TLS protocol version #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP) - /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party - /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication - private void Init(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol, - RemoteCertificateValidationCallback userCertificateValidationCallback, - LocalCertificateSelectionCallback userCertificateSelectionCallback) -#elif (WINDOWS_APP || WINDOWS_PHONE_APP) + /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party + /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication + private void Init(String brokerHostName, Int32 brokerPort, Boolean secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol, + RemoteCertificateValidationCallback userCertificateValidationCallback, + LocalCertificateSelectionCallback userCertificateSelectionCallback) +#elif WINDOWS_APP || WINDOWS_PHONE_APP private void Init(string brokerHostName, int brokerPort, bool secure, MqttSslProtocols sslProtocol) #else private void Init(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol) #endif { - // set default MQTT protocol version (default is 3.1.1) - this.ProtocolVersion = MqttProtocolVersion.Version_3_1_1; + // set default MQTT protocol version (default is 3.1.1) + this.ProtocolVersion = MqttProtocolVersion.Version_3_1_1; #if !SSL - // check security parameters - if (secure) - throw new ArgumentException("Library compiled without SSL support"); + // check security parameters + if (secure) { + throw new ArgumentException("Library compiled without SSL support"); + } #endif - this.brokerHostName = brokerHostName; - this.brokerPort = brokerPort; + this.brokerHostName = brokerHostName; + this.brokerPort = brokerPort; - // reference to MQTT settings - this.settings = MqttSettings.Instance; - // set settings port based on secure connection or not - if (!secure) - this.settings.Port = this.brokerPort; - else - this.settings.SslPort = this.brokerPort; + // reference to MQTT settings + this.Settings = MqttSettings.Instance; + // set settings port based on secure connection or not + if (!secure) { + this.Settings.Port = this.brokerPort; + } else { + this.Settings.SslPort = this.brokerPort; + } - this.syncEndReceiving = new AutoResetEvent(false); - this.keepAliveEvent = new AutoResetEvent(false); + this.syncEndReceiving = new AutoResetEvent(false); + this.keepAliveEvent = new AutoResetEvent(false); - // queue for handling inflight messages (publishing and acknowledge) - this.inflightWaitHandle = new AutoResetEvent(false); - this.inflightQueue = new Queue(); + // queue for handling inflight messages (publishing and acknowledge) + this.inflightWaitHandle = new AutoResetEvent(false); + this.inflightQueue = new Queue(); - // queue for received message - this.receiveEventWaitHandle = new AutoResetEvent(false); - this.eventQueue = new Queue(); - this.internalQueue = new Queue(); + // queue for received message + this.receiveEventWaitHandle = new AutoResetEvent(false); + this.eventQueue = new Queue(); + this.internalQueue = new Queue(); - // session - this.session = null; + // session + this.session = null; - // create network channel + // create network channel #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP) - this.channel = new MqttNetworkChannel(this.brokerHostName, this.brokerPort, secure, caCert, clientCert, sslProtocol, userCertificateValidationCallback, userCertificateSelectionCallback); -#elif (WINDOWS_APP || WINDOWS_PHONE_APP) + this.channel = new MqttNetworkChannel(this.brokerHostName, this.brokerPort, secure, caCert, clientCert, sslProtocol, userCertificateValidationCallback, userCertificateSelectionCallback); +#elif WINDOWS_APP || WINDOWS_PHONE_APP this.channel = new MqttNetworkChannel(this.brokerHostName, this.brokerPort, secure, sslProtocol); #else this.channel = new MqttNetworkChannel(this.brokerHostName, this.brokerPort, secure, caCert, clientCert, sslProtocol); #endif + } + + /// + /// Connect to broker + /// + /// Client identifier + /// Return code of CONNACK message from broker + public Byte Connect(String clientId) => this.Connect(clientId, null, null, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT); + + /// + /// Connect to broker + /// + /// Client identifier + /// Username + /// Password + /// Return code of CONNACK message from broker + public Byte Connect(String clientId, + String username, + String password) => this.Connect(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT); + + /// + /// Connect to broker + /// + /// Client identifier + /// Username + /// Password + /// Clean sessione flag + /// Keep alive period + /// Return code of CONNACK message from broker + public Byte Connect(String clientId, + String username, + String password, + Boolean cleanSession, + UInt16 keepAlivePeriod) => this.Connect(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, cleanSession, keepAlivePeriod); + + /// + /// Connect to broker + /// + /// Client identifier + /// Username + /// Password + /// Will retain flag + /// Will QOS level + /// Will flag + /// Will topic + /// Will message + /// Clean sessione flag + /// Keep alive period + /// Return code of CONNACK message from broker + public Byte Connect(String clientId, + String username, + String password, + Boolean willRetain, + Byte willQosLevel, + Boolean willFlag, + String willTopic, + String willMessage, + Boolean cleanSession, + UInt16 keepAlivePeriod) { + // create CONNECT message + MqttMsgConnect connect = new MqttMsgConnect(clientId, + username, + password, + willRetain, + willQosLevel, + willFlag, + willTopic, + willMessage, + cleanSession, + keepAlivePeriod, + (Byte)this.ProtocolVersion); + + try { + // connect to the broker + this.channel.Connect(); + } catch (Exception ex) { + throw new MqttConnectionException("Exception connecting to the broker", ex); + } + + this.lastCommTime = 0; + this.isRunning = true; + this.isConnectionClosing = false; + // start thread for receiving messages from broker + Fx.StartThread(this.ReceiveThread); + + MqttMsgConnack connack = (MqttMsgConnack)this.SendReceive(connect); + // if connection accepted, start keep alive timer and + if (connack.ReturnCode == MqttMsgConnack.CONN_ACCEPTED) { + // set all client properties + this.ClientId = clientId; + this.CleanSession = cleanSession; + this.WillFlag = willFlag; + this.WillTopic = willTopic; + this.WillMessage = willMessage; + this.WillQosLevel = willQosLevel; + + this.keepAlivePeriod = keepAlivePeriod * 1000; // convert in ms + + // restore previous session + this.RestoreSession(); + + // keep alive period equals zero means turning off keep alive mechanism + if (this.keepAlivePeriod != 0) { + // start thread for sending keep alive message to the broker + Fx.StartThread(this.KeepAliveThread); } - /// - /// Connect to broker - /// - /// Client identifier - /// Return code of CONNACK message from broker - public byte Connect(string clientId) - { - return this.Connect(clientId, null, null, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT); - } + // start thread for raising received message event from broker + Fx.StartThread(this.DispatchEventThread); - /// - /// Connect to broker - /// - /// Client identifier - /// Username - /// Password - /// Return code of CONNACK message from broker - public byte Connect(string clientId, - string username, - string password) - { - return this.Connect(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT); - } + // start thread for handling inflight messages queue to broker asynchronously (publish and acknowledge) + Fx.StartThread(this.ProcessInflightThread); - /// - /// Connect to broker - /// - /// Client identifier - /// Username - /// Password - /// Clean sessione flag - /// Keep alive period - /// Return code of CONNACK message from broker - public byte Connect(string clientId, - string username, - string password, - bool cleanSession, - ushort keepAlivePeriod) - { - return this.Connect(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, cleanSession, keepAlivePeriod); - } + this.IsConnected = true; + } + return connack.ReturnCode; + } - /// - /// Connect to broker - /// - /// Client identifier - /// Username - /// Password - /// Will retain flag - /// Will QOS level - /// Will flag - /// Will topic - /// Will message - /// Clean sessione flag - /// Keep alive period - /// Return code of CONNACK message from broker - public byte Connect(string clientId, - string username, - string password, - bool willRetain, - byte willQosLevel, - bool willFlag, - string willTopic, - string willMessage, - bool cleanSession, - ushort keepAlivePeriod) - { - // create CONNECT message - MqttMsgConnect connect = new MqttMsgConnect(clientId, - username, - password, - willRetain, - willQosLevel, - willFlag, - willTopic, - willMessage, - cleanSession, - keepAlivePeriod, - (byte)this.ProtocolVersion); + /// + /// Disconnect from broker + /// + public void Disconnect() { + MqttMsgDisconnect disconnect = new MqttMsgDisconnect(); + this.Send(disconnect); - try - { - // connect to the broker - this.channel.Connect(); - } - catch (Exception ex) - { - throw new MqttConnectionException("Exception connecting to the broker", ex); - } - - this.lastCommTime = 0; - this.isRunning = true; - this.isConnectionClosing = false; - // start thread for receiving messages from broker - Fx.StartThread(this.ReceiveThread); - - MqttMsgConnack connack = (MqttMsgConnack)this.SendReceive(connect); - // if connection accepted, start keep alive timer and - if (connack.ReturnCode == MqttMsgConnack.CONN_ACCEPTED) - { - // set all client properties - this.ClientId = clientId; - this.CleanSession = cleanSession; - this.WillFlag = willFlag; - this.WillTopic = willTopic; - this.WillMessage = willMessage; - this.WillQosLevel = willQosLevel; - - this.keepAlivePeriod = keepAlivePeriod * 1000; // convert in ms - - // restore previous session - this.RestoreSession(); - - // keep alive period equals zero means turning off keep alive mechanism - if (this.keepAlivePeriod != 0) - { - // start thread for sending keep alive message to the broker - Fx.StartThread(this.KeepAliveThread); - } - - // start thread for raising received message event from broker - Fx.StartThread(this.DispatchEventThread); - - // start thread for handling inflight messages queue to broker asynchronously (publish and acknowledge) - Fx.StartThread(this.ProcessInflightThread); - - this.IsConnected = true; - } - return connack.ReturnCode; - } - - /// - /// Disconnect from broker - /// - public void Disconnect() - { - MqttMsgDisconnect disconnect = new MqttMsgDisconnect(); - this.Send(disconnect); - - // close client - this.OnConnectionClosing(); - } + // close client + this.OnConnectionClosing(); + } #if BROKER /// @@ -634,71 +599,70 @@ namespace uPLibrary.Networking.M2Mqtt } #endif - /// - /// Close client - /// + /// + /// Close client + /// #if BROKER public void Close() #else - private void Close() + private void Close() #endif { - // stop receiving thread - this.isRunning = false; + // stop receiving thread + this.isRunning = false; - // wait end receive event thread - if (this.receiveEventWaitHandle != null) - this.receiveEventWaitHandle.Set(); + // wait end receive event thread + if (this.receiveEventWaitHandle != null) { + this.receiveEventWaitHandle.Set(); + } - // wait end process inflight thread - if (this.inflightWaitHandle != null) - this.inflightWaitHandle.Set(); + // wait end process inflight thread + if (this.inflightWaitHandle != null) { + this.inflightWaitHandle.Set(); + } #if BROKER // unlock keep alive thread this.keepAliveEvent.Set(); #else - // unlock keep alive thread and wait - this.keepAliveEvent.Set(); + // unlock keep alive thread and wait + this.keepAliveEvent.Set(); - if (this.keepAliveEventEnd != null) - this.keepAliveEventEnd.WaitOne(); + if (this.keepAliveEventEnd != null) { + this.keepAliveEventEnd.WaitOne(); + } #endif - // clear all queues - this.inflightQueue.Clear(); - this.internalQueue.Clear(); - this.eventQueue.Clear(); + // clear all queues + this.inflightQueue.Clear(); + this.internalQueue.Clear(); + this.eventQueue.Clear(); - // close network channel - this.channel.Close(); + // close network channel + this.channel.Close(); - this.IsConnected = false; - } + this.IsConnected = false; + } - /// - /// Execute ping to broker for keep alive - /// - /// PINGRESP message from broker - private MqttMsgPingResp Ping() - { - MqttMsgPingReq pingreq = new MqttMsgPingReq(); - try - { - // broker must send PINGRESP within timeout equal to keep alive period - return (MqttMsgPingResp)this.SendReceive(pingreq, this.keepAlivePeriod); - } - catch (Exception e) - { + /// + /// Execute ping to broker for keep alive + /// + /// PINGRESP message from broker + private MqttMsgPingResp Ping() { + MqttMsgPingReq pingreq = new MqttMsgPingReq(); + try { + // broker must send PINGRESP within timeout equal to keep alive period + return (MqttMsgPingResp)this.SendReceive(pingreq, this.keepAlivePeriod); + } catch (Exception e) { #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString()); + MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString()); #endif - // client must close connection - this.OnConnectionClosing(); - return null; - } - } + // client must close connection + this.OnConnectionClosing(); + return null; + } + } #if BROKER /// @@ -777,155 +741,126 @@ namespace uPLibrary.Networking.M2Mqtt } #endif - /// - /// Subscribe for message topics - /// - /// List of topics to subscribe - /// QOS levels related to topics - /// Message Id related to SUBSCRIBE message - public ushort Subscribe(string[] topics, byte[] qosLevels) - { - MqttMsgSubscribe subscribe = - new MqttMsgSubscribe(topics, qosLevels); - subscribe.MessageId = this.GetMessageId(); + /// + /// Subscribe for message topics + /// + /// List of topics to subscribe + /// QOS levels related to topics + /// Message Id related to SUBSCRIBE message + public UInt16 Subscribe(String[] topics, Byte[] qosLevels) { + MqttMsgSubscribe subscribe = + new MqttMsgSubscribe(topics, qosLevels) { + MessageId = this.GetMessageId() + }; - // enqueue subscribe request into the inflight queue - this.EnqueueInflight(subscribe, MqttMsgFlow.ToPublish); + // enqueue subscribe request into the inflight queue + _ = this.EnqueueInflight(subscribe, MqttMsgFlow.ToPublish); - return subscribe.MessageId; - } + return subscribe.MessageId; + } - /// - /// Unsubscribe for message topics - /// - /// List of topics to unsubscribe - /// Message Id in UNSUBACK message from broker - public ushort Unsubscribe(string[] topics) - { - MqttMsgUnsubscribe unsubscribe = - new MqttMsgUnsubscribe(topics); - unsubscribe.MessageId = this.GetMessageId(); + /// + /// Unsubscribe for message topics + /// + /// List of topics to unsubscribe + /// Message Id in UNSUBACK message from broker + public UInt16 Unsubscribe(String[] topics) { + MqttMsgUnsubscribe unsubscribe = + new MqttMsgUnsubscribe(topics) { + MessageId = this.GetMessageId() + }; - // enqueue unsubscribe request into the inflight queue - this.EnqueueInflight(unsubscribe, MqttMsgFlow.ToPublish); + // enqueue unsubscribe request into the inflight queue + _ = this.EnqueueInflight(unsubscribe, MqttMsgFlow.ToPublish); - return unsubscribe.MessageId; - } + return unsubscribe.MessageId; + } - /// - /// Publish a message asynchronously (QoS Level 0 and not retained) - /// - /// Message topic - /// Message data (payload) - /// Message Id related to PUBLISH message - public ushort Publish(string topic, byte[] message) - { - return this.Publish(topic, message, MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE, false); - } + /// + /// Publish a message asynchronously (QoS Level 0 and not retained) + /// + /// Message topic + /// Message data (payload) + /// Message Id related to PUBLISH message + public UInt16 Publish(String topic, Byte[] message) => this.Publish(topic, message, MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE, false); - /// - /// Publish a message asynchronously - /// - /// Message topic - /// Message data (payload) - /// QoS Level - /// Retain flag - /// Message Id related to PUBLISH message - public ushort Publish(string topic, byte[] message, byte qosLevel, bool retain) - { - MqttMsgPublish publish = - new MqttMsgPublish(topic, message, false, qosLevel, retain); - publish.MessageId = this.GetMessageId(); + /// + /// Publish a message asynchronously + /// + /// Message topic + /// Message data (payload) + /// QoS Level + /// Retain flag + /// Message Id related to PUBLISH message + public UInt16 Publish(String topic, Byte[] message, Byte qosLevel, Boolean retain) { + MqttMsgPublish publish = + new MqttMsgPublish(topic, message, false, qosLevel, retain) { + MessageId = this.GetMessageId() + }; - // enqueue message to publish into the inflight queue - bool enqueue = this.EnqueueInflight(publish, MqttMsgFlow.ToPublish); + // enqueue message to publish into the inflight queue + Boolean enqueue = this.EnqueueInflight(publish, MqttMsgFlow.ToPublish); - // message enqueued - if (enqueue) - return publish.MessageId; - // infligh queue full, message not enqueued - else - throw new MqttClientException(MqttClientErrorCode.InflightQueueFull); - } + // message enqueued + if (enqueue) { + return publish.MessageId; + } + // infligh queue full, message not enqueued + else { + throw new MqttClientException(MqttClientErrorCode.InflightQueueFull); + } + } - /// - /// Wrapper method for raising events - /// - /// Internal event - private void OnInternalEvent(InternalEvent internalEvent) - { - lock (this.eventQueue) - { - this.eventQueue.Enqueue(internalEvent); - } + /// + /// Wrapper method for raising events + /// + /// Internal event + private void OnInternalEvent(InternalEvent internalEvent) { + lock (this.eventQueue) { + this.eventQueue.Enqueue(internalEvent); + } - this.receiveEventWaitHandle.Set(); - } + _ = this.receiveEventWaitHandle.Set(); + } - /// - /// Wrapper method for raising closing connection event - /// - private void OnConnectionClosing() - { - if (!this.isConnectionClosing) - { - this.isConnectionClosing = true; - this.receiveEventWaitHandle.Set(); - } - } + /// + /// Wrapper method for raising closing connection event + /// + private void OnConnectionClosing() { + if (!this.isConnectionClosing) { + this.isConnectionClosing = true; + _ = this.receiveEventWaitHandle.Set(); + } + } - /// - /// Wrapper method for raising PUBLISH message received event - /// - /// PUBLISH message received - private void OnMqttMsgPublishReceived(MqttMsgPublish publish) - { - if (this.MqttMsgPublishReceived != null) - { - this.MqttMsgPublishReceived(this, - new MqttMsgPublishEventArgs(publish.Topic, publish.Message, publish.DupFlag, publish.QosLevel, publish.Retain)); - } - } + /// + /// Wrapper method for raising PUBLISH message received event + /// + /// PUBLISH message received + private void OnMqttMsgPublishReceived(MqttMsgPublish publish) => this.MqttMsgPublishReceived?.Invoke(this, + new MqttMsgPublishEventArgs(publish.Topic, publish.Message, publish.DupFlag, publish.QosLevel, publish.Retain)); - /// - /// Wrapper method for raising published message event - /// - /// Message identifier for published message - /// Publish flag - private void OnMqttMsgPublished(ushort messageId, bool isPublished) - { - if (this.MqttMsgPublished != null) - { - this.MqttMsgPublished(this, - new MqttMsgPublishedEventArgs(messageId, isPublished)); - } - } + /// + /// Wrapper method for raising published message event + /// + /// Message identifier for published message + /// Publish flag + private void OnMqttMsgPublished(UInt16 messageId, Boolean isPublished) => this.MqttMsgPublished?.Invoke(this, + new MqttMsgPublishedEventArgs(messageId, isPublished)); - /// - /// Wrapper method for raising subscribed topic event - /// - /// SUBACK message received - private void OnMqttMsgSubscribed(MqttMsgSuback suback) - { - if (this.MqttMsgSubscribed != null) - { - this.MqttMsgSubscribed(this, - new MqttMsgSubscribedEventArgs(suback.MessageId, suback.GrantedQoSLevels)); - } - } + /// + /// Wrapper method for raising subscribed topic event + /// + /// SUBACK message received + private void OnMqttMsgSubscribed(MqttMsgSuback suback) => this.MqttMsgSubscribed?.Invoke(this, + new MqttMsgSubscribedEventArgs(suback.MessageId, suback.GrantedQoSLevels)); - /// - /// Wrapper method for raising unsubscribed topic event - /// - /// Message identifier for unsubscribed topic - private void OnMqttMsgUnsubscribed(ushort messageId) - { - if (this.MqttMsgUnsubscribed != null) - { - this.MqttMsgUnsubscribed(this, - new MqttMsgUnsubscribedEventArgs(messageId)); - } - } + /// + /// Wrapper method for raising unsubscribed topic event + /// + /// Message identifier for unsubscribed topic + private void OnMqttMsgUnsubscribed(UInt16 messageId) => this.MqttMsgUnsubscribed?.Invoke(this, + new MqttMsgUnsubscribedEventArgs(messageId)); #if BROKER /// @@ -981,389 +916,342 @@ namespace uPLibrary.Networking.M2Mqtt } #endif - /// - /// Wrapper method for peer/client disconnection - /// - private void OnConnectionClosed() - { - if (this.ConnectionClosed != null) - { - this.ConnectionClosed(this, EventArgs.Empty); - } - } + /// + /// Wrapper method for peer/client disconnection + /// + private void OnConnectionClosed() => this.ConnectionClosed?.Invoke(this, EventArgs.Empty); - /// - /// Send a message - /// - /// Message bytes - private void Send(byte[] msgBytes) - { - try - { - // send message - this.channel.Send(msgBytes); + /// + /// Send a message + /// + /// Message bytes + private void Send(Byte[] msgBytes) { + try { + // send message + this.channel.Send(msgBytes); #if !BROKER - // update last message sent ticks - this.lastCommTime = Environment.TickCount; + // update last message sent ticks + this.lastCommTime = Environment.TickCount; #endif - } - catch (Exception e) - { + } catch (Exception e) { #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString()); + MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString()); #endif - throw new MqttCommunicationException(e); - } - } + throw new MqttCommunicationException(e); + } + } - /// - /// Send a message - /// - /// Message - private void Send(MqttMsgBase msg) - { + /// + /// Send a message + /// + /// Message + private void Send(MqttMsgBase msg) { #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Frame, "SEND {0}", msg); + MqttUtility.Trace.WriteLine(TraceLevel.Frame, "SEND {0}", msg); #endif - this.Send(msg.GetBytes((byte)this.ProtocolVersion)); - } + this.Send(msg.GetBytes((Byte)this.ProtocolVersion)); + } - /// - /// Send a message to the broker and wait answer - /// - /// Message bytes - /// MQTT message response - private MqttMsgBase SendReceive(byte[] msgBytes) - { - return this.SendReceive(msgBytes, MqttSettings.MQTT_DEFAULT_TIMEOUT); - } + /// + /// Send a message to the broker and wait answer + /// + /// Message bytes + /// MQTT message response + private MqttMsgBase SendReceive(Byte[] msgBytes) => this.SendReceive(msgBytes, MqttSettings.MQTT_DEFAULT_TIMEOUT); - /// - /// Send a message to the broker and wait answer - /// - /// Message bytes - /// Timeout for receiving answer - /// MQTT message response - private MqttMsgBase SendReceive(byte[] msgBytes, int timeout) - { - // reset handle before sending - this.syncEndReceiving.Reset(); - try - { - // send message - this.channel.Send(msgBytes); + /// + /// Send a message to the broker and wait answer + /// + /// Message bytes + /// Timeout for receiving answer + /// MQTT message response + private MqttMsgBase SendReceive(Byte[] msgBytes, Int32 timeout) { + // reset handle before sending + this.syncEndReceiving.Reset(); + try { + // send message + this.channel.Send(msgBytes); - // update last message sent ticks - this.lastCommTime = Environment.TickCount; - } - catch (Exception e) - { + // update last message sent ticks + this.lastCommTime = Environment.TickCount; + } catch (Exception e) { #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP) - if (typeof(SocketException) == e.GetType()) - { - // connection reset by broker - if (((SocketException)e).SocketErrorCode == SocketError.ConnectionReset) - this.IsConnected = false; - } + if (typeof(SocketException) == e.GetType()) { + // connection reset by broker + if (((SocketException)e).SocketErrorCode == SocketError.ConnectionReset) { + this.IsConnected = false; + } + } #endif #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString()); + Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString()); #endif - throw new MqttCommunicationException(e); - } + throw new MqttCommunicationException(e); + } -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK // wait for answer from broker if (this.syncEndReceiving.WaitOne(timeout, false)) #else - // wait for answer from broker - if (this.syncEndReceiving.WaitOne(timeout)) + // wait for answer from broker + if (this.syncEndReceiving.WaitOne(timeout)) #endif { - // message received without exception - if (this.exReceiving == null) - return this.msgReceived; - // receiving thread catched exception - else - throw this.exReceiving; - } - else - { - // throw timeout exception - throw new MqttCommunicationException(); - } + // message received without exception + if (this.exReceiving == null) { + return this.msgReceived; } - - /// - /// Send a message to the broker and wait answer - /// - /// Message - /// MQTT message response - private MqttMsgBase SendReceive(MqttMsgBase msg) - { - return this.SendReceive(msg, MqttSettings.MQTT_DEFAULT_TIMEOUT); + // receiving thread catched exception + else { + throw this.exReceiving; } + } else { + // throw timeout exception + throw new MqttCommunicationException(); + } + } - /// - /// Send a message to the broker and wait answer - /// - /// Message - /// Timeout for receiving answer - /// MQTT message response - private MqttMsgBase SendReceive(MqttMsgBase msg, int timeout) - { + /// + /// Send a message to the broker and wait answer + /// + /// Message + /// MQTT message response + private MqttMsgBase SendReceive(MqttMsgBase msg) => this.SendReceive(msg, MqttSettings.MQTT_DEFAULT_TIMEOUT); + + /// + /// Send a message to the broker and wait answer + /// + /// Message + /// Timeout for receiving answer + /// MQTT message response + private MqttMsgBase SendReceive(MqttMsgBase msg, Int32 timeout) { #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Frame, "SEND {0}", msg); + MqttUtility.Trace.WriteLine(TraceLevel.Frame, "SEND {0}", msg); #endif - return this.SendReceive(msg.GetBytes((byte)this.ProtocolVersion), timeout); + return this.SendReceive(msg.GetBytes((Byte)this.ProtocolVersion), timeout); + } + + /// + /// Enqueue a message into the inflight queue + /// + /// Message to enqueue + /// Message flow (publish, acknowledge) + /// Message enqueued or not + private Boolean EnqueueInflight(MqttMsgBase msg, MqttMsgFlow flow) { + // enqueue is needed (or not) + Boolean enqueue = true; + + // if it is a PUBLISH message with QoS Level 2 + if (msg.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE && + msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE) { + lock (this.inflightQueue) { + // if it is a PUBLISH message already received (it is in the inflight queue), the publisher + // re-sent it because it didn't received the PUBREC. In this case, we have to re-send PUBREC + + // NOTE : I need to find on message id and flow because the broker could be publish/received + // to/from client and message id could be the same (one tracked by broker and the other by client) + MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToAcknowledge); + MqttMsgContext msgCtx = (MqttMsgContext)QueueExtension.Get(this.inflightQueue, msgCtxFinder.Find); + + // the PUBLISH message is alredy in the inflight queue, we don't need to re-enqueue but we need + // to change state to re-send PUBREC + if (msgCtx != null) { + msgCtx.State = MqttMsgState.QueuedQos2; + msgCtx.Flow = MqttMsgFlow.ToAcknowledge; + enqueue = false; + } + } + } + + if (enqueue) { + // set a default state + MqttMsgState state = MqttMsgState.QueuedQos0; + + // based on QoS level, the messages flow between broker and client changes + switch (msg.QosLevel) { + // QoS Level 0 + case MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE: + + state = MqttMsgState.QueuedQos0; + break; + + // QoS Level 1 + case MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE: + + state = MqttMsgState.QueuedQos1; + break; + + // QoS Level 2 + case MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE: + + state = MqttMsgState.QueuedQos2; + break; } - /// - /// Enqueue a message into the inflight queue - /// - /// Message to enqueue - /// Message flow (publish, acknowledge) - /// Message enqueued or not - private bool EnqueueInflight(MqttMsgBase msg, MqttMsgFlow flow) - { - // enqueue is needed (or not) - bool enqueue = true; + // [v3.1.1] SUBSCRIBE and UNSUBSCRIBE aren't "officially" QOS = 1 + // so QueuedQos1 state isn't valid for them + if (msg.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE) { + state = MqttMsgState.SendSubscribe; + } else if (msg.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE) { + state = MqttMsgState.SendUnsubscribe; + } - // if it is a PUBLISH message with QoS Level 2 - if ((msg.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) && - (msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE)) - { - lock (this.inflightQueue) - { - // if it is a PUBLISH message already received (it is in the inflight queue), the publisher - // re-sent it because it didn't received the PUBREC. In this case, we have to re-send PUBREC + // queue message context + MqttMsgContext msgContext = new MqttMsgContext() { + Message = msg, + State = state, + Flow = flow, + Attempt = 0 + }; - // NOTE : I need to find on message id and flow because the broker could be publish/received - // to/from client and message id could be the same (one tracked by broker and the other by client) - MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToAcknowledge); - MqttMsgContext msgCtx = (MqttMsgContext) QueueExtension.Get(this.inflightQueue, msgCtxFinder.Find); + lock (this.inflightQueue) { + // check number of messages inside inflight queue + enqueue = this.inflightQueue.Count < this.Settings.InflightQueueSize; - // the PUBLISH message is alredy in the inflight queue, we don't need to re-enqueue but we need - // to change state to re-send PUBREC - if (msgCtx != null) - { - msgCtx.State = MqttMsgState.QueuedQos2; - msgCtx.Flow = MqttMsgFlow.ToAcknowledge; - enqueue = false; - } - } - } - - if (enqueue) - { - // set a default state - MqttMsgState state = MqttMsgState.QueuedQos0; - - // based on QoS level, the messages flow between broker and client changes - switch (msg.QosLevel) - { - // QoS Level 0 - case MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE: - - state = MqttMsgState.QueuedQos0; - break; - - // QoS Level 1 - case MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE: - - state = MqttMsgState.QueuedQos1; - break; - - // QoS Level 2 - case MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE: - - state = MqttMsgState.QueuedQos2; - break; - } - - // [v3.1.1] SUBSCRIBE and UNSUBSCRIBE aren't "officially" QOS = 1 - // so QueuedQos1 state isn't valid for them - if (msg.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE) - state = MqttMsgState.SendSubscribe; - else if (msg.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE) - state = MqttMsgState.SendUnsubscribe; - - // queue message context - MqttMsgContext msgContext = new MqttMsgContext() - { - Message = msg, - State = state, - Flow = flow, - Attempt = 0 - }; - - lock (this.inflightQueue) - { - // check number of messages inside inflight queue - enqueue = (this.inflightQueue.Count < this.settings.InflightQueueSize); - - if (enqueue) - { - // enqueue message and unlock send thread - this.inflightQueue.Enqueue(msgContext); + if (enqueue) { + // enqueue message and unlock send thread + this.inflightQueue.Enqueue(msgContext); #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "enqueued {0}", msg); + MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "enqueued {0}", msg); #endif - // PUBLISH message - if (msg.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) - { - // to publish and QoS level 1 or 2 - if ((msgContext.Flow == MqttMsgFlow.ToPublish) && - ((msg.QosLevel == MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE) || - (msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE))) - { - if (this.session != null) - this.session.InflightMessages.Add(msgContext.Key, msgContext); - } - // to acknowledge and QoS level 2 - else if ((msgContext.Flow == MqttMsgFlow.ToAcknowledge) && - (msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE)) - { - if (this.session != null) - this.session.InflightMessages.Add(msgContext.Key, msgContext); - } - } - } + // PUBLISH message + if (msg.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) { + // to publish and QoS level 1 or 2 + if (msgContext.Flow == MqttMsgFlow.ToPublish && + (msg.QosLevel == MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE || + msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE)) { + if (this.session != null) { + this.session.InflightMessages.Add(msgContext.Key, msgContext); } + } + // to acknowledge and QoS level 2 + else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge && + msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE) { + if (this.session != null) { + this.session.InflightMessages.Add(msgContext.Key, msgContext); + } + } } - - this.inflightWaitHandle.Set(); - - return enqueue; + } } + } - /// - /// Enqueue a message into the internal queue - /// - /// Message to enqueue - private void EnqueueInternal(MqttMsgBase msg) - { - // enqueue is needed (or not) - bool enqueue = true; + this.inflightWaitHandle.Set(); - // if it is a PUBREL message (for QoS Level 2) - if (msg.Type == MqttMsgBase.MQTT_MSG_PUBREL_TYPE) - { - lock (this.inflightQueue) - { - // if it is a PUBREL but the corresponding PUBLISH isn't in the inflight queue, - // it means that we processed PUBLISH message and received PUBREL and we sent PUBCOMP - // but publisher didn't receive PUBCOMP so it re-sent PUBREL. We need only to re-send PUBCOMP. + return enqueue; + } - // NOTE : I need to find on message id and flow because the broker could be publish/received - // to/from client and message id could be the same (one tracked by broker and the other by client) - MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToAcknowledge); - MqttMsgContext msgCtx = (MqttMsgContext) QueueExtension.Get(this.inflightQueue,msgCtxFinder.Find); + /// + /// Enqueue a message into the internal queue + /// + /// Message to enqueue + private void EnqueueInternal(MqttMsgBase msg) { + // enqueue is needed (or not) + Boolean enqueue = true; - // the PUBLISH message isn't in the inflight queue, it was already processed so - // we need to re-send PUBCOMP only - if (msgCtx == null) - { - MqttMsgPubcomp pubcomp = new MqttMsgPubcomp(); - pubcomp.MessageId = msg.MessageId; + // if it is a PUBREL message (for QoS Level 2) + if (msg.Type == MqttMsgBase.MQTT_MSG_PUBREL_TYPE) { + lock (this.inflightQueue) { + // if it is a PUBREL but the corresponding PUBLISH isn't in the inflight queue, + // it means that we processed PUBLISH message and received PUBREL and we sent PUBCOMP + // but publisher didn't receive PUBCOMP so it re-sent PUBREL. We need only to re-send PUBCOMP. - this.Send(pubcomp); + // NOTE : I need to find on message id and flow because the broker could be publish/received + // to/from client and message id could be the same (one tracked by broker and the other by client) + MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToAcknowledge); + MqttMsgContext msgCtx = (MqttMsgContext)QueueExtension.Get(this.inflightQueue, msgCtxFinder.Find); - enqueue = false; - } - } - } - // if it is a PUBCOMP message (for QoS Level 2) - else if (msg.Type == MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE) - { - lock (this.inflightQueue) - { - // if it is a PUBCOMP but the corresponding PUBLISH isn't in the inflight queue, - // it means that we sent PUBLISH message, sent PUBREL (after receiving PUBREC) and already received PUBCOMP - // but publisher didn't receive PUBREL so it re-sent PUBCOMP. We need only to ignore this PUBCOMP. + // the PUBLISH message isn't in the inflight queue, it was already processed so + // we need to re-send PUBCOMP only + if (msgCtx == null) { + MqttMsgPubcomp pubcomp = new MqttMsgPubcomp { + MessageId = msg.MessageId + }; - // NOTE : I need to find on message id and flow because the broker could be publish/received - // to/from client and message id could be the same (one tracked by broker and the other by client) - MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToPublish); - MqttMsgContext msgCtx = (MqttMsgContext) QueueExtension.Get(this.inflightQueue, msgCtxFinder.Find); + this.Send(pubcomp); - // the PUBLISH message isn't in the inflight queue, it was already sent so we need to ignore this PUBCOMP - if (msgCtx == null) - { - enqueue = false; - } - } - } - // if it is a PUBREC message (for QoS Level 2) - else if (msg.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE) - { - lock (this.inflightQueue) - { - // if it is a PUBREC but the corresponding PUBLISH isn't in the inflight queue, - // it means that we sent PUBLISH message more times (retries) but broker didn't send PUBREC in time - // the publish is failed and we need only to ignore this PUBREC. + enqueue = false; + } + } + } + // if it is a PUBCOMP message (for QoS Level 2) + else if (msg.Type == MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE) { + lock (this.inflightQueue) { + // if it is a PUBCOMP but the corresponding PUBLISH isn't in the inflight queue, + // it means that we sent PUBLISH message, sent PUBREL (after receiving PUBREC) and already received PUBCOMP + // but publisher didn't receive PUBREL so it re-sent PUBCOMP. We need only to ignore this PUBCOMP. - // NOTE : I need to find on message id and flow because the broker could be publish/received - // to/from client and message id could be the same (one tracked by broker and the other by client) - MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToPublish); - MqttMsgContext msgCtx = (MqttMsgContext) QueueExtension.Get(this.inflightQueue, msgCtxFinder.Find); + // NOTE : I need to find on message id and flow because the broker could be publish/received + // to/from client and message id could be the same (one tracked by broker and the other by client) + MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToPublish); + MqttMsgContext msgCtx = (MqttMsgContext)QueueExtension.Get(this.inflightQueue, msgCtxFinder.Find); - // the PUBLISH message isn't in the inflight queue, it was already sent so we need to ignore this PUBREC - if (msgCtx == null) - { - enqueue = false; - } - } - } + // the PUBLISH message isn't in the inflight queue, it was already sent so we need to ignore this PUBCOMP + if (msgCtx == null) { + enqueue = false; + } + } + } + // if it is a PUBREC message (for QoS Level 2) + else if (msg.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE) { + lock (this.inflightQueue) { + // if it is a PUBREC but the corresponding PUBLISH isn't in the inflight queue, + // it means that we sent PUBLISH message more times (retries) but broker didn't send PUBREC in time + // the publish is failed and we need only to ignore this PUBREC. - if (enqueue) - { - lock (this.internalQueue) - { - this.internalQueue.Enqueue(msg); + // NOTE : I need to find on message id and flow because the broker could be publish/received + // to/from client and message id could be the same (one tracked by broker and the other by client) + MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToPublish); + MqttMsgContext msgCtx = (MqttMsgContext)QueueExtension.Get(this.inflightQueue, msgCtxFinder.Find); + + // the PUBLISH message isn't in the inflight queue, it was already sent so we need to ignore this PUBREC + if (msgCtx == null) { + enqueue = false; + } + } + } + + if (enqueue) { + lock (this.internalQueue) { + this.internalQueue.Enqueue(msg); #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "enqueued {0}", msg); + MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "enqueued {0}", msg); #endif - this.inflightWaitHandle.Set(); - } - } + this.inflightWaitHandle.Set(); } + } + } - /// - /// Thread for receiving messages - /// - private void ReceiveThread() - { - int readBytes = 0; - byte[] fixedHeaderFirstByte = new byte[1]; - byte msgType; + /// + /// Thread for receiving messages + /// + private void ReceiveThread() { + Int32 readBytes = 0; + Byte[] fixedHeaderFirstByte = new Byte[1]; + Byte msgType; - while (this.isRunning) - { - try - { - // read first byte (fixed header) - readBytes = this.channel.Receive(fixedHeaderFirstByte); + while (this.isRunning) { + try { + // read first byte (fixed header) + readBytes = this.channel.Receive(fixedHeaderFirstByte); - if (readBytes > 0) - { + if (readBytes > 0) { #if BROKER // update last message received ticks this.lastCommTime = Environment.TickCount; #endif - // extract message type from received byte - msgType = (byte)((fixedHeaderFirstByte[0] & MqttMsgBase.MSG_TYPE_MASK) >> MqttMsgBase.MSG_TYPE_OFFSET); + // extract message type from received byte + msgType = (Byte)((fixedHeaderFirstByte[0] & MqttMsgBase.MSG_TYPE_MASK) >> MqttMsgBase.MSG_TYPE_OFFSET); - switch (msgType) - { - // CONNECT message received - case MqttMsgBase.MQTT_MSG_CONNECT_TYPE: + switch (msgType) { + // CONNECT message received + case MqttMsgBase.MQTT_MSG_CONNECT_TYPE: #if BROKER MqttMsgConnect connect = MqttMsgConnect.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); @@ -1375,25 +1263,25 @@ namespace uPLibrary.Networking.M2Mqtt this.OnInternalEvent(new MsgInternalEvent(connect)); break; #else - throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); #endif - - // CONNACK message received - case MqttMsgBase.MQTT_MSG_CONNACK_TYPE: + + // CONNACK message received + case MqttMsgBase.MQTT_MSG_CONNACK_TYPE: #if BROKER throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); #else - this.msgReceived = MqttMsgConnack.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); + this.msgReceived = MqttMsgConnack.Parse(fixedHeaderFirstByte[0], (Byte)this.ProtocolVersion, this.channel); #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", this.msgReceived); + MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", this.msgReceived); #endif - this.syncEndReceiving.Set(); - break; + this.syncEndReceiving.Set(); + break; #endif - // PINGREQ message received - case MqttMsgBase.MQTT_MSG_PINGREQ_TYPE: + // PINGREQ message received + case MqttMsgBase.MQTT_MSG_PINGREQ_TYPE: #if BROKER this.msgReceived = MqttMsgPingReq.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); @@ -1406,25 +1294,25 @@ namespace uPLibrary.Networking.M2Mqtt break; #else - throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); #endif - // PINGRESP message received - case MqttMsgBase.MQTT_MSG_PINGRESP_TYPE: + // PINGRESP message received + case MqttMsgBase.MQTT_MSG_PINGRESP_TYPE: #if BROKER throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); #else - this.msgReceived = MqttMsgPingResp.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); + this.msgReceived = MqttMsgPingResp.Parse(fixedHeaderFirstByte[0], (Byte)this.ProtocolVersion, this.channel); #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", this.msgReceived); + MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", this.msgReceived); #endif - this.syncEndReceiving.Set(); - break; + this.syncEndReceiving.Set(); + break; #endif - // SUBSCRIBE message received - case MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE: + // SUBSCRIBE message received + case MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE: #if BROKER MqttMsgSubscribe subscribe = MqttMsgSubscribe.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); @@ -1437,98 +1325,98 @@ namespace uPLibrary.Networking.M2Mqtt break; #else - throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); #endif - // SUBACK message received - case MqttMsgBase.MQTT_MSG_SUBACK_TYPE: + // SUBACK message received + case MqttMsgBase.MQTT_MSG_SUBACK_TYPE: #if BROKER throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); #else - // enqueue SUBACK message received (for QoS Level 1) into the internal queue - MqttMsgSuback suback = MqttMsgSuback.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); + // enqueue SUBACK message received (for QoS Level 1) into the internal queue + MqttMsgSuback suback = MqttMsgSuback.Parse(fixedHeaderFirstByte[0], (Byte)this.ProtocolVersion, this.channel); #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", suback); + MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", suback); #endif - // enqueue SUBACK message into the internal queue - this.EnqueueInternal(suback); + // enqueue SUBACK message into the internal queue + this.EnqueueInternal(suback); - break; + break; #endif - // PUBLISH message received - case MqttMsgBase.MQTT_MSG_PUBLISH_TYPE: + // PUBLISH message received + case MqttMsgBase.MQTT_MSG_PUBLISH_TYPE: - MqttMsgPublish publish = MqttMsgPublish.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); + MqttMsgPublish publish = MqttMsgPublish.Parse(fixedHeaderFirstByte[0], (Byte)this.ProtocolVersion, this.channel); #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", publish); + MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", publish); #endif - // enqueue PUBLISH message to acknowledge into the inflight queue - this.EnqueueInflight(publish, MqttMsgFlow.ToAcknowledge); + // enqueue PUBLISH message to acknowledge into the inflight queue + this.EnqueueInflight(publish, MqttMsgFlow.ToAcknowledge); - break; + break; - // PUBACK message received - case MqttMsgBase.MQTT_MSG_PUBACK_TYPE: + // PUBACK message received + case MqttMsgBase.MQTT_MSG_PUBACK_TYPE: - // enqueue PUBACK message received (for QoS Level 1) into the internal queue - MqttMsgPuback puback = MqttMsgPuback.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); + // enqueue PUBACK message received (for QoS Level 1) into the internal queue + MqttMsgPuback puback = MqttMsgPuback.Parse(fixedHeaderFirstByte[0], (Byte)this.ProtocolVersion, this.channel); #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", puback); + MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", puback); #endif - // enqueue PUBACK message into the internal queue - this.EnqueueInternal(puback); + // enqueue PUBACK message into the internal queue + this.EnqueueInternal(puback); - break; + break; - // PUBREC message received - case MqttMsgBase.MQTT_MSG_PUBREC_TYPE: + // PUBREC message received + case MqttMsgBase.MQTT_MSG_PUBREC_TYPE: - // enqueue PUBREC message received (for QoS Level 2) into the internal queue - MqttMsgPubrec pubrec = MqttMsgPubrec.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); + // enqueue PUBREC message received (for QoS Level 2) into the internal queue + MqttMsgPubrec pubrec = MqttMsgPubrec.Parse(fixedHeaderFirstByte[0], (Byte)this.ProtocolVersion, this.channel); #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", pubrec); + MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", pubrec); #endif - // enqueue PUBREC message into the internal queue - this.EnqueueInternal(pubrec); + // enqueue PUBREC message into the internal queue + this.EnqueueInternal(pubrec); - break; + break; - // PUBREL message received - case MqttMsgBase.MQTT_MSG_PUBREL_TYPE: + // PUBREL message received + case MqttMsgBase.MQTT_MSG_PUBREL_TYPE: - // enqueue PUBREL message received (for QoS Level 2) into the internal queue - MqttMsgPubrel pubrel = MqttMsgPubrel.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); + // enqueue PUBREL message received (for QoS Level 2) into the internal queue + MqttMsgPubrel pubrel = MqttMsgPubrel.Parse(fixedHeaderFirstByte[0], (Byte)this.ProtocolVersion, this.channel); #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", pubrel); + MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", pubrel); #endif - // enqueue PUBREL message into the internal queue - this.EnqueueInternal(pubrel); + // enqueue PUBREL message into the internal queue + this.EnqueueInternal(pubrel); - break; - - // PUBCOMP message received - case MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE: + break; - // enqueue PUBCOMP message received (for QoS Level 2) into the internal queue - MqttMsgPubcomp pubcomp = MqttMsgPubcomp.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); + // PUBCOMP message received + case MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE: + + // enqueue PUBCOMP message received (for QoS Level 2) into the internal queue + MqttMsgPubcomp pubcomp = MqttMsgPubcomp.Parse(fixedHeaderFirstByte[0], (Byte)this.ProtocolVersion, this.channel); #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", pubcomp); + MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", pubcomp); #endif - // enqueue PUBCOMP message into the internal queue - this.EnqueueInternal(pubcomp); + // enqueue PUBCOMP message into the internal queue + this.EnqueueInternal(pubcomp); - break; + break; - // UNSUBSCRIBE message received - case MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE: + // UNSUBSCRIBE message received + case MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE: #if BROKER MqttMsgUnsubscribe unsubscribe = MqttMsgUnsubscribe.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); @@ -1541,29 +1429,29 @@ namespace uPLibrary.Networking.M2Mqtt break; #else - throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); #endif - // UNSUBACK message received - case MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE: + // UNSUBACK message received + case MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE: #if BROKER throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); #else - // enqueue UNSUBACK message received (for QoS Level 1) into the internal queue - MqttMsgUnsuback unsuback = MqttMsgUnsuback.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); + // enqueue UNSUBACK message received (for QoS Level 1) into the internal queue + MqttMsgUnsuback unsuback = MqttMsgUnsuback.Parse(fixedHeaderFirstByte[0], (Byte)this.ProtocolVersion, this.channel); #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", unsuback); + MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", unsuback); #endif - // enqueue UNSUBACK message into the internal queue - this.EnqueueInternal(unsuback); + // enqueue UNSUBACK message into the internal queue + this.EnqueueInternal(unsuback); - break; + break; #endif - // DISCONNECT message received - case MqttMsgDisconnect.MQTT_MSG_DISCONNECT_TYPE: + // DISCONNECT message received + case MqttMsgDisconnect.MQTT_MSG_DISCONNECT_TYPE: #if BROKER MqttMsgDisconnect disconnect = MqttMsgDisconnect.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel); @@ -1576,112 +1464,99 @@ namespace uPLibrary.Networking.M2Mqtt break; #else - throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); #endif - default: + default: - throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); - } - - this.exReceiving = null; - } - // zero bytes read, peer gracefully closed socket - else - { - // wake up thread that will notify connection is closing - this.OnConnectionClosing(); - } - } - catch (Exception e) - { -#if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString()); -#endif - this.exReceiving = new MqttCommunicationException(e); - - bool close = false; - if (e.GetType() == typeof(MqttClientException)) - { - // [v3.1.1] scenarios the receiver MUST close the network connection - MqttClientException ex = e as MqttClientException; - close = ((ex.ErrorCode == MqttClientErrorCode.InvalidFlagBits) || - (ex.ErrorCode == MqttClientErrorCode.InvalidProtocolName) || - (ex.ErrorCode == MqttClientErrorCode.InvalidConnectFlags)); - } -#if !(WINDOWS_APP || WINDOWS_PHONE_APP) - else if ((e.GetType() == typeof(IOException)) || (e.GetType() == typeof(SocketException)) || - ((e.InnerException != null) && (e.InnerException.GetType() == typeof(SocketException)))) // added for SSL/TLS incoming connection that use SslStream that wraps SocketException - { - close = true; - } -#endif - - if (close) - { - // wake up thread that will notify connection is closing - this.OnConnectionClosing(); - } - } + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); } + + this.exReceiving = null; + } + // zero bytes read, peer gracefully closed socket + else { + // wake up thread that will notify connection is closing + this.OnConnectionClosing(); + } + } catch (Exception e) { +#if TRACE + MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString()); +#endif + this.exReceiving = new MqttCommunicationException(e); + + Boolean close = false; + if (e.GetType() == typeof(MqttClientException)) { + // [v3.1.1] scenarios the receiver MUST close the network connection + MqttClientException ex = e as MqttClientException; + close = ex.ErrorCode == MqttClientErrorCode.InvalidFlagBits || + ex.ErrorCode == MqttClientErrorCode.InvalidProtocolName || + ex.ErrorCode == MqttClientErrorCode.InvalidConnectFlags; + } +#if !(WINDOWS_APP || WINDOWS_PHONE_APP) + else if (e.GetType() == typeof(IOException) || e.GetType() == typeof(SocketException) || + e.InnerException != null && e.InnerException.GetType() == typeof(SocketException)) // added for SSL/TLS incoming connection that use SslStream that wraps SocketException + { + close = true; + } +#endif + + if (close) { + // wake up thread that will notify connection is closing + this.OnConnectionClosing(); + } } + } + } - /// - /// Thread for handling keep alive message - /// - private void KeepAliveThread() - { - int delta = 0; - int wait = this.keepAlivePeriod; - - // create event to signal that current thread is end - this.keepAliveEventEnd = new AutoResetEvent(false); + /// + /// Thread for handling keep alive message + /// + private void KeepAliveThread() { + Int32 delta = 0; + Int32 wait = this.keepAlivePeriod; - while (this.isRunning) - { -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) + // create event to signal that current thread is end + this.keepAliveEventEnd = new AutoResetEvent(false); + + while (this.isRunning) { +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK // waiting... this.keepAliveEvent.WaitOne(wait, false); #else - // waiting... - this.keepAliveEvent.WaitOne(wait); + // waiting... + this.keepAliveEvent.WaitOne(wait); #endif - if (this.isRunning) - { - delta = Environment.TickCount - this.lastCommTime; + if (this.isRunning) { + delta = Environment.TickCount - this.lastCommTime; - // if timeout exceeded ... - if (delta >= this.keepAlivePeriod) - { + // if timeout exceeded ... + if (delta >= this.keepAlivePeriod) { #if BROKER // client must close connection this.OnConnectionClosing(); #else - // ... send keep alive + // ... send keep alive this.Ping(); wait = this.keepAlivePeriod; #endif - } - else - { - // update waiting time - wait = this.keepAlivePeriod - delta; - } - } - } - - // signal thread end - this.keepAliveEventEnd.Set(); + } else { + // update waiting time + wait = this.keepAlivePeriod - delta; + } } + } - /// - /// Thread for raising event - /// - private void DispatchEventThread() - { - while (this.isRunning) - { + // signal thread end + this.keepAliveEventEnd.Set(); + } + + /// + /// Thread for raising event + /// + private void DispatchEventThread() { + while (this.isRunning) { #if BROKER if ((this.eventQueue.Count == 0) && !this.isConnectionClosing) { @@ -1706,44 +1581,41 @@ namespace uPLibrary.Networking.M2Mqtt } } #else - if ((this.eventQueue.Count == 0) && !this.isConnectionClosing) - // wait on receiving message from client - this.receiveEventWaitHandle.WaitOne(); + if (this.eventQueue.Count == 0 && !this.isConnectionClosing) { + // wait on receiving message from client + this.receiveEventWaitHandle.WaitOne(); + } #endif - // check if it is running or we are closing client - if (this.isRunning) - { - // get event from queue - InternalEvent internalEvent = null; - lock (this.eventQueue) - { - if (this.eventQueue.Count > 0) - internalEvent = (InternalEvent)this.eventQueue.Dequeue(); - } + // check if it is running or we are closing client + if (this.isRunning) { + // get event from queue + InternalEvent internalEvent = null; + lock (this.eventQueue) { + if (this.eventQueue.Count > 0) { + internalEvent = (InternalEvent)this.eventQueue.Dequeue(); + } + } - // it's an event with a message inside - if (internalEvent != null) - { - MqttMsgBase msg = ((MsgInternalEvent)internalEvent).Message; + // it's an event with a message inside + if (internalEvent != null) { + MqttMsgBase msg = ((MsgInternalEvent)internalEvent).Message; - if (msg != null) - { - switch (msg.Type) - { - // CONNECT message received - case MqttMsgBase.MQTT_MSG_CONNECT_TYPE: + if (msg != null) { + switch (msg.Type) { + // CONNECT message received + case MqttMsgBase.MQTT_MSG_CONNECT_TYPE: #if BROKER // raise connected client event (CONNECT message received) this.OnMqttMsgConnected((MqttMsgConnect)msg); break; #else - throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); #endif - // SUBSCRIBE message received - case MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE: + // SUBSCRIBE message received + case MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE: #if BROKER MqttMsgSubscribe subscribe = (MqttMsgSubscribe)msg; @@ -1751,53 +1623,55 @@ namespace uPLibrary.Networking.M2Mqtt this.OnMqttMsgSubscribeReceived(subscribe.MessageId, subscribe.Topics, subscribe.QoSLevels); break; #else - throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); #endif - // SUBACK message received - case MqttMsgBase.MQTT_MSG_SUBACK_TYPE: + // SUBACK message received + case MqttMsgBase.MQTT_MSG_SUBACK_TYPE: - // raise subscribed topic event (SUBACK message received) - this.OnMqttMsgSubscribed((MqttMsgSuback)msg); - break; + // raise subscribed topic event (SUBACK message received) + this.OnMqttMsgSubscribed((MqttMsgSuback)msg); + break; - // PUBLISH message received - case MqttMsgBase.MQTT_MSG_PUBLISH_TYPE: + // PUBLISH message received + case MqttMsgBase.MQTT_MSG_PUBLISH_TYPE: - // PUBLISH message received in a published internal event, no publish succeeded - if (internalEvent.GetType() == typeof(MsgPublishedInternalEvent)) - this.OnMqttMsgPublished(msg.MessageId, false); - else - // raise PUBLISH message received event - this.OnMqttMsgPublishReceived((MqttMsgPublish)msg); - break; + // PUBLISH message received in a published internal event, no publish succeeded + if (internalEvent.GetType() == typeof(MsgPublishedInternalEvent)) { + this.OnMqttMsgPublished(msg.MessageId, false); + } else { + // raise PUBLISH message received event + this.OnMqttMsgPublishReceived((MqttMsgPublish)msg); + } - // PUBACK message received - case MqttMsgBase.MQTT_MSG_PUBACK_TYPE: + break; - // raise published message event - // (PUBACK received for QoS Level 1) - this.OnMqttMsgPublished(msg.MessageId, true); - break; + // PUBACK message received + case MqttMsgBase.MQTT_MSG_PUBACK_TYPE: - // PUBREL message received - case MqttMsgBase.MQTT_MSG_PUBREL_TYPE: + // raise published message event + // (PUBACK received for QoS Level 1) + this.OnMqttMsgPublished(msg.MessageId, true); + break; - // raise message received event - // (PUBREL received for QoS Level 2) - this.OnMqttMsgPublishReceived((MqttMsgPublish)msg); - break; + // PUBREL message received + case MqttMsgBase.MQTT_MSG_PUBREL_TYPE: - // PUBCOMP message received - case MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE: + // raise message received event + // (PUBREL received for QoS Level 2) + this.OnMqttMsgPublishReceived((MqttMsgPublish)msg); + break; - // raise published message event - // (PUBCOMP received for QoS Level 2) - this.OnMqttMsgPublished(msg.MessageId, true); - break; + // PUBCOMP message received + case MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE: - // UNSUBSCRIBE message received from client - case MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE: + // raise published message event + // (PUBCOMP received for QoS Level 2) + this.OnMqttMsgPublished(msg.MessageId, true); + break; + + // UNSUBSCRIBE message received from client + case MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE: #if BROKER MqttMsgUnsubscribe unsubscribe = (MqttMsgUnsubscribe)msg; @@ -1805,759 +1679,692 @@ namespace uPLibrary.Networking.M2Mqtt this.OnMqttMsgUnsubscribeReceived(unsubscribe.MessageId, unsubscribe.Topics); break; #else - throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); #endif - // UNSUBACK message received - case MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE: + // UNSUBACK message received + case MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE: - // raise unsubscribed topic event - this.OnMqttMsgUnsubscribed(msg.MessageId); - break; + // raise unsubscribed topic event + this.OnMqttMsgUnsubscribed(msg.MessageId); + break; - // DISCONNECT message received from client - case MqttMsgDisconnect.MQTT_MSG_DISCONNECT_TYPE: + // DISCONNECT message received from client + case MqttMsgDisconnect.MQTT_MSG_DISCONNECT_TYPE: #if BROKER // raise disconnected client event (DISCONNECT message received) this.OnMqttMsgDisconnected(); break; #else - throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); #endif - } - } - } - - // all events for received messages dispatched, check if there is closing connection - if ((this.eventQueue.Count == 0) && this.isConnectionClosing) - { - // client must close connection - this.Close(); - - // client raw disconnection - this.OnConnectionClosed(); - } - } + } } + } + + // all events for received messages dispatched, check if there is closing connection + if (this.eventQueue.Count == 0 && this.isConnectionClosing) { + // client must close connection + this.Close(); + + // client raw disconnection + this.OnConnectionClosed(); + } } + } + } - /// - /// Process inflight messages queue - /// - private void ProcessInflightThread() - { - MqttMsgContext msgContext = null; - MqttMsgBase msgInflight = null; - MqttMsgBase msgReceived = null; - InternalEvent internalEvent = null; - bool acknowledge = false; - int timeout = Timeout.Infinite; - int delta; - bool msgReceivedProcessed = false; + /// + /// Process inflight messages queue + /// + private void ProcessInflightThread() { + MqttMsgContext msgContext = null; + MqttMsgBase msgInflight = null; + MqttMsgBase msgReceived = null; + InternalEvent internalEvent = null; + Boolean acknowledge = false; + Int32 timeout = Timeout.Infinite; + Int32 delta; + Boolean msgReceivedProcessed = false; - try - { - while (this.isRunning) - { -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) + try { + while (this.isRunning) { +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK // wait on message queueud to inflight this.inflightWaitHandle.WaitOne(timeout, false); #else - // wait on message queueud to inflight - this.inflightWaitHandle.WaitOne(timeout); + // wait on message queueud to inflight + this.inflightWaitHandle.WaitOne(timeout); #endif - // it could be unblocked because Close() method is joining - if (this.isRunning) - { - lock (this.inflightQueue) - { - // message received and peeked from internal queue is processed - // NOTE : it has the corresponding message in inflight queue based on messageId - // (ex. a PUBREC for a PUBLISH, a SUBACK for a SUBSCRIBE, ...) - // if it's orphan we need to remove from internal queue - msgReceivedProcessed = false; - acknowledge = false; - msgReceived = null; - - // set timeout tu MaxValue instead of Infinte (-1) to perform - // compare with calcultad current msgTimeout - timeout = Int32.MaxValue; - - // a message inflight could be re-enqueued but we have to - // analyze it only just one time for cycle - int count = this.inflightQueue.Count; - // process all inflight queued messages - while (count > 0) - { - count--; - acknowledge = false; - msgReceived = null; - - // check to be sure that client isn't closing and all queues are now empty ! - if (!this.isRunning) - break; - - // dequeue message context from queue - msgContext = (MqttMsgContext)this.inflightQueue.Dequeue(); - - // get inflight message - msgInflight = (MqttMsgBase)msgContext.Message; - - switch (msgContext.State) - { - case MqttMsgState.QueuedQos0: - - // QoS 0, PUBLISH message to send to broker, no state change, no acknowledge - if (msgContext.Flow == MqttMsgFlow.ToPublish) - { - this.Send(msgInflight); - } - // QoS 0, no need acknowledge - else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) - { - internalEvent = new MsgInternalEvent(msgInflight); - // notify published message from broker (no need acknowledged) - this.OnInternalEvent(internalEvent); - } - -#if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight); -#endif - break; - - case MqttMsgState.QueuedQos1: - // [v3.1.1] SUBSCRIBE and UNSIBSCRIBE aren't "officially" QOS = 1 - case MqttMsgState.SendSubscribe: - case MqttMsgState.SendUnsubscribe: - - // QoS 1, PUBLISH or SUBSCRIBE/UNSUBSCRIBE message to send to broker, state change to wait PUBACK or SUBACK/UNSUBACK - if (msgContext.Flow == MqttMsgFlow.ToPublish) - { - msgContext.Timestamp = Environment.TickCount; - msgContext.Attempt++; - - if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) - { - // PUBLISH message to send, wait for PUBACK - msgContext.State = MqttMsgState.WaitForPuback; - // retry ? set dup flag [v3.1.1] only for PUBLISH message - if (msgContext.Attempt > 1) - msgInflight.DupFlag = true; - } - else if (msgInflight.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE) - // SUBSCRIBE message to send, wait for SUBACK - msgContext.State = MqttMsgState.WaitForSuback; - else if (msgInflight.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE) - // UNSUBSCRIBE message to send, wait for UNSUBACK - msgContext.State = MqttMsgState.WaitForUnsuback; - - this.Send(msgInflight); - - // update timeout : minimum between delay (based on current message sent) or current timeout - timeout = (this.settings.DelayOnRetry < timeout) ? this.settings.DelayOnRetry : timeout; - - // re-enqueue message (I have to re-analyze for receiving PUBACK, SUBACK or UNSUBACK) - this.inflightQueue.Enqueue(msgContext); - } - // QoS 1, PUBLISH message received from broker to acknowledge, send PUBACK - else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) - { - MqttMsgPuback puback = new MqttMsgPuback(); - puback.MessageId = msgInflight.MessageId; - - this.Send(puback); - - internalEvent = new MsgInternalEvent(msgInflight); - // notify published message from broker and acknowledged - this.OnInternalEvent(internalEvent); - -#if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight); -#endif - } - break; - - case MqttMsgState.QueuedQos2: - - // QoS 2, PUBLISH message to send to broker, state change to wait PUBREC - if (msgContext.Flow == MqttMsgFlow.ToPublish) - { - msgContext.Timestamp = Environment.TickCount; - msgContext.Attempt++; - msgContext.State = MqttMsgState.WaitForPubrec; - // retry ? set dup flag - if (msgContext.Attempt > 1) - msgInflight.DupFlag = true; - - this.Send(msgInflight); - - // update timeout : minimum between delay (based on current message sent) or current timeout - timeout = (this.settings.DelayOnRetry < timeout) ? this.settings.DelayOnRetry : timeout; - - // re-enqueue message (I have to re-analyze for receiving PUBREC) - this.inflightQueue.Enqueue(msgContext); - } - // QoS 2, PUBLISH message received from broker to acknowledge, send PUBREC, state change to wait PUBREL - else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) - { - MqttMsgPubrec pubrec = new MqttMsgPubrec(); - pubrec.MessageId = msgInflight.MessageId; - - msgContext.State = MqttMsgState.WaitForPubrel; - - this.Send(pubrec); - - // re-enqueue message (I have to re-analyze for receiving PUBREL) - this.inflightQueue.Enqueue(msgContext); - } - break; - - case MqttMsgState.WaitForPuback: - case MqttMsgState.WaitForSuback: - case MqttMsgState.WaitForUnsuback: - - // QoS 1, waiting for PUBACK of a PUBLISH message sent or - // waiting for SUBACK of a SUBSCRIBE message sent or - // waiting for UNSUBACK of a UNSUBSCRIBE message sent or - if (msgContext.Flow == MqttMsgFlow.ToPublish) - { - acknowledge = false; - lock (this.internalQueue) - { - if (this.internalQueue.Count > 0) - msgReceived = (MqttMsgBase)this.internalQueue.Peek(); - } - - // it is a PUBACK message or a SUBACK/UNSUBACK message - if (msgReceived != null) - { - // PUBACK message or SUBACK/UNSUBACK message for the current message - if (((msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBACK_TYPE) && (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) && (msgReceived.MessageId == msgInflight.MessageId)) || - ((msgReceived.Type == MqttMsgBase.MQTT_MSG_SUBACK_TYPE) && (msgInflight.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE) && (msgReceived.MessageId == msgInflight.MessageId)) || - ((msgReceived.Type == MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE) && (msgInflight.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE) && (msgReceived.MessageId == msgInflight.MessageId))) - { - lock (this.internalQueue) - { - // received message processed - this.internalQueue.Dequeue(); - acknowledge = true; - msgReceivedProcessed = true; -#if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived); -#endif - } - - // if PUBACK received, confirm published with flag - if (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBACK_TYPE) - internalEvent = new MsgPublishedInternalEvent(msgReceived, true); - else - internalEvent = new MsgInternalEvent(msgReceived); - - // notify received acknowledge from broker of a published message or subscribe/unsubscribe message - this.OnInternalEvent(internalEvent); - - // PUBACK received for PUBLISH message with QoS Level 1, remove from session state - if ((msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) && - (this.session != null) && -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) - (this.session.InflightMessages.Contains(msgContext.Key))) -#else - (this.session.InflightMessages.ContainsKey(msgContext.Key))) -#endif - { - this.session.InflightMessages.Remove(msgContext.Key); - } - -#if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight); -#endif - } - } - - // current message not acknowledged, no PUBACK or SUBACK/UNSUBACK or not equal messageid - if (!acknowledge) - { - delta = Environment.TickCount - msgContext.Timestamp; - // check timeout for receiving PUBACK since PUBLISH was sent or - // for receiving SUBACK since SUBSCRIBE was sent or - // for receiving UNSUBACK since UNSUBSCRIBE was sent - if (delta >= this.settings.DelayOnRetry) - { - // max retry not reached, resend - if (msgContext.Attempt < this.settings.AttemptsOnRetry) - { - msgContext.State = MqttMsgState.QueuedQos1; - - // re-enqueue message - this.inflightQueue.Enqueue(msgContext); - - // update timeout (0 -> reanalyze queue immediately) - timeout = 0; - } - else - { - // if PUBACK for a PUBLISH message not received after retries, raise event for not published - if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) - { - // PUBACK not received in time, PUBLISH retries failed, need to remove from session inflight messages too - if ((this.session != null) && -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) - (this.session.InflightMessages.Contains(msgContext.Key))) -#else - (this.session.InflightMessages.ContainsKey(msgContext.Key))) -#endif - { - this.session.InflightMessages.Remove(msgContext.Key); - } - - internalEvent = new MsgPublishedInternalEvent(msgInflight, false); - - // notify not received acknowledge from broker and message not published - this.OnInternalEvent(internalEvent); - } - // NOTE : not raise events for SUBACK or UNSUBACK not received - // for the user no event raised means subscribe/unsubscribe failed - } - } - else - { - // re-enqueue message (I have to re-analyze for receiving PUBACK, SUBACK or UNSUBACK) - this.inflightQueue.Enqueue(msgContext); - - // update timeout - int msgTimeout = (this.settings.DelayOnRetry - delta); - timeout = (msgTimeout < timeout) ? msgTimeout : timeout; - } - } - } - break; - - case MqttMsgState.WaitForPubrec: - - // QoS 2, waiting for PUBREC of a PUBLISH message sent - if (msgContext.Flow == MqttMsgFlow.ToPublish) - { - acknowledge = false; - lock (this.internalQueue) - { - if (this.internalQueue.Count > 0) - msgReceived = (MqttMsgBase)this.internalQueue.Peek(); - } - - // it is a PUBREC message - if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE)) - { - // PUBREC message for the current PUBLISH message, send PUBREL, wait for PUBCOMP - if (msgReceived.MessageId == msgInflight.MessageId) - { - lock (this.internalQueue) - { - // received message processed - this.internalQueue.Dequeue(); - acknowledge = true; - msgReceivedProcessed = true; -#if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived); -#endif - } - - MqttMsgPubrel pubrel = new MqttMsgPubrel(); - pubrel.MessageId = msgInflight.MessageId; - - msgContext.State = MqttMsgState.WaitForPubcomp; - msgContext.Timestamp = Environment.TickCount; - msgContext.Attempt = 1; - - this.Send(pubrel); - - // update timeout : minimum between delay (based on current message sent) or current timeout - timeout = (this.settings.DelayOnRetry < timeout) ? this.settings.DelayOnRetry : timeout; - - // re-enqueue message - this.inflightQueue.Enqueue(msgContext); - } - } - - // current message not acknowledged - if (!acknowledge) - { - delta = Environment.TickCount - msgContext.Timestamp; - // check timeout for receiving PUBREC since PUBLISH was sent - if (delta >= this.settings.DelayOnRetry) - { - // max retry not reached, resend - if (msgContext.Attempt < this.settings.AttemptsOnRetry) - { - msgContext.State = MqttMsgState.QueuedQos2; - - // re-enqueue message - this.inflightQueue.Enqueue(msgContext); - - // update timeout (0 -> reanalyze queue immediately) - timeout = 0; - } - else - { - // PUBREC not received in time, PUBLISH retries failed, need to remove from session inflight messages too - if ((this.session != null) && -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) - (this.session.InflightMessages.Contains(msgContext.Key))) -#else - (this.session.InflightMessages.ContainsKey(msgContext.Key))) -#endif - { - this.session.InflightMessages.Remove(msgContext.Key); - } - - // if PUBREC for a PUBLISH message not received after retries, raise event for not published - internalEvent = new MsgPublishedInternalEvent(msgInflight, false); - // notify not received acknowledge from broker and message not published - this.OnInternalEvent(internalEvent); - } - } - else - { - // re-enqueue message - this.inflightQueue.Enqueue(msgContext); - - // update timeout - int msgTimeout = (this.settings.DelayOnRetry - delta); - timeout = (msgTimeout < timeout) ? msgTimeout : timeout; - } - } - } - break; - - case MqttMsgState.WaitForPubrel: - - // QoS 2, waiting for PUBREL of a PUBREC message sent - if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) - { - lock (this.internalQueue) - { - if (this.internalQueue.Count > 0) - msgReceived = (MqttMsgBase)this.internalQueue.Peek(); - } - - // it is a PUBREL message - if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREL_TYPE)) - { - // PUBREL message for the current message, send PUBCOMP - if (msgReceived.MessageId == msgInflight.MessageId) - { - lock (this.internalQueue) - { - // received message processed - this.internalQueue.Dequeue(); - msgReceivedProcessed = true; -#if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived); -#endif - } - - MqttMsgPubcomp pubcomp = new MqttMsgPubcomp(); - pubcomp.MessageId = msgInflight.MessageId; - - this.Send(pubcomp); - - internalEvent = new MsgInternalEvent(msgInflight); - // notify published message from broker and acknowledged - this.OnInternalEvent(internalEvent); - - // PUBREL received (and PUBCOMP sent) for PUBLISH message with QoS Level 2, remove from session state - if ((msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) && - (this.session != null) && -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) - (this.session.InflightMessages.Contains(msgContext.Key))) -#else - (this.session.InflightMessages.ContainsKey(msgContext.Key))) -#endif - { - this.session.InflightMessages.Remove(msgContext.Key); - } - -#if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight); -#endif - } - else - { - // re-enqueue message - this.inflightQueue.Enqueue(msgContext); - } - } - else - { - // re-enqueue message - this.inflightQueue.Enqueue(msgContext); - } - } - break; - - case MqttMsgState.WaitForPubcomp: - - // QoS 2, waiting for PUBCOMP of a PUBREL message sent - if (msgContext.Flow == MqttMsgFlow.ToPublish) - { - acknowledge = false; - lock (this.internalQueue) - { - if (this.internalQueue.Count > 0) - msgReceived = (MqttMsgBase)this.internalQueue.Peek(); - } - - // it is a PUBCOMP message - if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE)) - { - // PUBCOMP message for the current message - if (msgReceived.MessageId == msgInflight.MessageId) - { - lock (this.internalQueue) - { - // received message processed - this.internalQueue.Dequeue(); - acknowledge = true; - msgReceivedProcessed = true; -#if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived); -#endif - } - - internalEvent = new MsgPublishedInternalEvent(msgReceived, true); - // notify received acknowledge from broker of a published message - this.OnInternalEvent(internalEvent); - - // PUBCOMP received for PUBLISH message with QoS Level 2, remove from session state - if ((msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) && - (this.session != null) && -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) - (this.session.InflightMessages.Contains(msgContext.Key))) -#else - (this.session.InflightMessages.ContainsKey(msgContext.Key))) -#endif - { - this.session.InflightMessages.Remove(msgContext.Key); - } - -#if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight); -#endif - } - } - // it is a PUBREC message - else if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE)) - { - // another PUBREC message for the current message due to a retransmitted PUBLISH - // I'm in waiting for PUBCOMP, so I can discard this PUBREC - if (msgReceived.MessageId == msgInflight.MessageId) - { - lock (this.internalQueue) - { - // received message processed - this.internalQueue.Dequeue(); - acknowledge = true; - msgReceivedProcessed = true; -#if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived); -#endif - - // re-enqueue message - this.inflightQueue.Enqueue(msgContext); - } - } - } - - // current message not acknowledged - if (!acknowledge) - { - delta = Environment.TickCount - msgContext.Timestamp; - // check timeout for receiving PUBCOMP since PUBREL was sent - if (delta >= this.settings.DelayOnRetry) - { - // max retry not reached, resend - if (msgContext.Attempt < this.settings.AttemptsOnRetry) - { - msgContext.State = MqttMsgState.SendPubrel; - - // re-enqueue message - this.inflightQueue.Enqueue(msgContext); - - // update timeout (0 -> reanalyze queue immediately) - timeout = 0; - } - else - { - // PUBCOMP not received, PUBREL retries failed, need to remove from session inflight messages too - if ((this.session != null) && -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) - (this.session.InflightMessages.Contains(msgContext.Key))) -#else - (this.session.InflightMessages.ContainsKey(msgContext.Key))) -#endif - { - this.session.InflightMessages.Remove(msgContext.Key); - } - - // if PUBCOMP for a PUBLISH message not received after retries, raise event for not published - internalEvent = new MsgPublishedInternalEvent(msgInflight, false); - // notify not received acknowledge from broker and message not published - this.OnInternalEvent(internalEvent); - } - } - else - { - // re-enqueue message - this.inflightQueue.Enqueue(msgContext); - - // update timeout - int msgTimeout = (this.settings.DelayOnRetry - delta); - timeout = (msgTimeout < timeout) ? msgTimeout : timeout; - } - } - } - break; - - case MqttMsgState.SendPubrec: - - // TODO : impossible ? --> QueuedQos2 ToAcknowledge - break; - - case MqttMsgState.SendPubrel: - - // QoS 2, PUBREL message to send to broker, state change to wait PUBCOMP - if (msgContext.Flow == MqttMsgFlow.ToPublish) - { - MqttMsgPubrel pubrel = new MqttMsgPubrel(); - pubrel.MessageId = msgInflight.MessageId; - - msgContext.State = MqttMsgState.WaitForPubcomp; - msgContext.Timestamp = Environment.TickCount; - msgContext.Attempt++; - // retry ? set dup flag [v3.1.1] no needed - if (this.ProtocolVersion == MqttProtocolVersion.Version_3_1) - { - if (msgContext.Attempt > 1) - pubrel.DupFlag = true; - } - - this.Send(pubrel); - - // update timeout : minimum between delay (based on current message sent) or current timeout - timeout = (this.settings.DelayOnRetry < timeout) ? this.settings.DelayOnRetry : timeout; - - // re-enqueue message - this.inflightQueue.Enqueue(msgContext); - } - break; - - case MqttMsgState.SendPubcomp: - // TODO : impossible ? - break; - case MqttMsgState.SendPuback: - // TODO : impossible ? --> QueuedQos1 ToAcknowledge - break; - default: - break; - } - } - - // if calculated timeout is MaxValue, it means that must be Infinite (-1) - if (timeout == Int32.MaxValue) - timeout = Timeout.Infinite; - - // if message received is orphan, no corresponding message in inflight queue - // based on messageId, we need to remove from the queue - if ((msgReceived != null) && !msgReceivedProcessed) - { - this.internalQueue.Dequeue(); -#if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0} orphan", msgReceived); -#endif - } - } - } + // it could be unblocked because Close() method is joining + if (this.isRunning) { + lock (this.inflightQueue) { + // message received and peeked from internal queue is processed + // NOTE : it has the corresponding message in inflight queue based on messageId + // (ex. a PUBREC for a PUBLISH, a SUBACK for a SUBSCRIBE, ...) + // if it's orphan we need to remove from internal queue + msgReceivedProcessed = false; + acknowledge = false; + msgReceived = null; + + // set timeout tu MaxValue instead of Infinte (-1) to perform + // compare with calcultad current msgTimeout + timeout = Int32.MaxValue; + + // a message inflight could be re-enqueued but we have to + // analyze it only just one time for cycle + Int32 count = this.inflightQueue.Count; + // process all inflight queued messages + while (count > 0) { + count--; + acknowledge = false; + msgReceived = null; + + // check to be sure that client isn't closing and all queues are now empty ! + if (!this.isRunning) { + break; } - } - catch (MqttCommunicationException e) - { - // possible exception on Send, I need to re-enqueue not sent message - if (msgContext != null) - // re-enqueue message - this.inflightQueue.Enqueue(msgContext); + + // dequeue message context from queue + msgContext = (MqttMsgContext)this.inflightQueue.Dequeue(); + + // get inflight message + msgInflight = (MqttMsgBase)msgContext.Message; + + switch (msgContext.State) { + case MqttMsgState.QueuedQos0: + + // QoS 0, PUBLISH message to send to broker, no state change, no acknowledge + if (msgContext.Flow == MqttMsgFlow.ToPublish) { + this.Send(msgInflight); + } + // QoS 0, no need acknowledge + else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) { + internalEvent = new MsgInternalEvent(msgInflight); + // notify published message from broker (no need acknowledged) + this.OnInternalEvent(internalEvent); + } #if TRACE - MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString()); + MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight); #endif + break; - // raise disconnection client event - this.OnConnectionClosing(); - } - } + case MqttMsgState.QueuedQos1: + // [v3.1.1] SUBSCRIBE and UNSIBSCRIBE aren't "officially" QOS = 1 + case MqttMsgState.SendSubscribe: + case MqttMsgState.SendUnsubscribe: - /// - /// Restore session - /// - private void RestoreSession() - { - // if not clean session - if (!this.CleanSession) - { - // there is a previous session - if (this.session != null) - { - lock (this.inflightQueue) - { - foreach (MqttMsgContext msgContext in this.session.InflightMessages.Values) - { + // QoS 1, PUBLISH or SUBSCRIBE/UNSUBSCRIBE message to send to broker, state change to wait PUBACK or SUBACK/UNSUBACK + if (msgContext.Flow == MqttMsgFlow.ToPublish) { + msgContext.Timestamp = Environment.TickCount; + msgContext.Attempt++; + + if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) { + // PUBLISH message to send, wait for PUBACK + msgContext.State = MqttMsgState.WaitForPuback; + // retry ? set dup flag [v3.1.1] only for PUBLISH message + if (msgContext.Attempt > 1) { + msgInflight.DupFlag = true; + } + } else if (msgInflight.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE) { + // SUBSCRIBE message to send, wait for SUBACK + msgContext.State = MqttMsgState.WaitForSuback; + } else if (msgInflight.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE) { + // UNSUBSCRIBE message to send, wait for UNSUBACK + msgContext.State = MqttMsgState.WaitForUnsuback; + } + + this.Send(msgInflight); + + // update timeout : minimum between delay (based on current message sent) or current timeout + timeout = (this.Settings.DelayOnRetry < timeout) ? this.Settings.DelayOnRetry : timeout; + + // re-enqueue message (I have to re-analyze for receiving PUBACK, SUBACK or UNSUBACK) + this.inflightQueue.Enqueue(msgContext); + } + // QoS 1, PUBLISH message received from broker to acknowledge, send PUBACK + else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) { + MqttMsgPuback puback = new MqttMsgPuback { + MessageId = msgInflight.MessageId + }; + + this.Send(puback); + + internalEvent = new MsgInternalEvent(msgInflight); + // notify published message from broker and acknowledged + this.OnInternalEvent(internalEvent); + +#if TRACE + MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight); +#endif + } + break; + + case MqttMsgState.QueuedQos2: + + // QoS 2, PUBLISH message to send to broker, state change to wait PUBREC + if (msgContext.Flow == MqttMsgFlow.ToPublish) { + msgContext.Timestamp = Environment.TickCount; + msgContext.Attempt++; + msgContext.State = MqttMsgState.WaitForPubrec; + // retry ? set dup flag + if (msgContext.Attempt > 1) { + msgInflight.DupFlag = true; + } + + this.Send(msgInflight); + + // update timeout : minimum between delay (based on current message sent) or current timeout + timeout = (this.Settings.DelayOnRetry < timeout) ? this.Settings.DelayOnRetry : timeout; + + // re-enqueue message (I have to re-analyze for receiving PUBREC) + this.inflightQueue.Enqueue(msgContext); + } + // QoS 2, PUBLISH message received from broker to acknowledge, send PUBREC, state change to wait PUBREL + else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) { + MqttMsgPubrec pubrec = new MqttMsgPubrec { + MessageId = msgInflight.MessageId + }; + + msgContext.State = MqttMsgState.WaitForPubrel; + + this.Send(pubrec); + + // re-enqueue message (I have to re-analyze for receiving PUBREL) + this.inflightQueue.Enqueue(msgContext); + } + break; + + case MqttMsgState.WaitForPuback: + case MqttMsgState.WaitForSuback: + case MqttMsgState.WaitForUnsuback: + + // QoS 1, waiting for PUBACK of a PUBLISH message sent or + // waiting for SUBACK of a SUBSCRIBE message sent or + // waiting for UNSUBACK of a UNSUBSCRIBE message sent or + if (msgContext.Flow == MqttMsgFlow.ToPublish) { + acknowledge = false; + lock (this.internalQueue) { + if (this.internalQueue.Count > 0) { + msgReceived = (MqttMsgBase)this.internalQueue.Peek(); + } + } + + // it is a PUBACK message or a SUBACK/UNSUBACK message + if (msgReceived != null) { + // PUBACK message or SUBACK/UNSUBACK message for the current message + if (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBACK_TYPE && msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE && msgReceived.MessageId == msgInflight.MessageId || + msgReceived.Type == MqttMsgBase.MQTT_MSG_SUBACK_TYPE && msgInflight.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE && msgReceived.MessageId == msgInflight.MessageId || + msgReceived.Type == MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE && msgInflight.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE && msgReceived.MessageId == msgInflight.MessageId) { + lock (this.internalQueue) { + // received message processed + this.internalQueue.Dequeue(); + acknowledge = true; + msgReceivedProcessed = true; +#if TRACE + MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived); +#endif + } + + // if PUBACK received, confirm published with flag + internalEvent = msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBACK_TYPE + ? new MsgPublishedInternalEvent(msgReceived, true) + : new MsgInternalEvent(msgReceived); + + // notify received acknowledge from broker of a published message or subscribe/unsubscribe message + this.OnInternalEvent(internalEvent); + + // PUBACK received for PUBLISH message with QoS Level 1, remove from session state + if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE && + this.session != null && +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK + (this.session.InflightMessages.Contains(msgContext.Key))) +#else + this.session.InflightMessages.ContainsKey(msgContext.Key)) +#endif + { + this.session.InflightMessages.Remove(msgContext.Key); + } + +#if TRACE + MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight); +#endif + } + } + + // current message not acknowledged, no PUBACK or SUBACK/UNSUBACK or not equal messageid + if (!acknowledge) { + delta = Environment.TickCount - msgContext.Timestamp; + // check timeout for receiving PUBACK since PUBLISH was sent or + // for receiving SUBACK since SUBSCRIBE was sent or + // for receiving UNSUBACK since UNSUBSCRIBE was sent + if (delta >= this.Settings.DelayOnRetry) { + // max retry not reached, resend + if (msgContext.Attempt < this.Settings.AttemptsOnRetry) { + msgContext.State = MqttMsgState.QueuedQos1; + + // re-enqueue message this.inflightQueue.Enqueue(msgContext); - // if it is a PUBLISH message to publish - if ((msgContext.Message.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) && - (msgContext.Flow == MqttMsgFlow.ToPublish)) - { - // it's QoS 1 and we haven't received PUBACK - if ((msgContext.Message.QosLevel == MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE) && - (msgContext.State == MqttMsgState.WaitForPuback)) - { - // we haven't received PUBACK, we need to resend PUBLISH message - msgContext.State = MqttMsgState.QueuedQos1; - } - // it's QoS 2 - else if (msgContext.Message.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE) - { - // we haven't received PUBREC, we need to resend PUBLISH message - if (msgContext.State == MqttMsgState.WaitForPubrec) - { - msgContext.State = MqttMsgState.QueuedQos2; - } - // we haven't received PUBCOMP, we need to resend PUBREL for it - else if (msgContext.State == MqttMsgState.WaitForPubcomp) - { - msgContext.State = MqttMsgState.SendPubrel; - } - } - } - } - } + // update timeout (0 -> reanalyze queue immediately) + timeout = 0; + } else { + // if PUBACK for a PUBLISH message not received after retries, raise event for not published + if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) { + // PUBACK not received in time, PUBLISH retries failed, need to remove from session inflight messages too + if (this.session != null && +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK + (this.session.InflightMessages.Contains(msgContext.Key))) +#else + this.session.InflightMessages.ContainsKey(msgContext.Key)) +#endif + { + this.session.InflightMessages.Remove(msgContext.Key); + } - // unlock process inflight queue - this.inflightWaitHandle.Set(); - } - else - { - // create new session - this.session = new MqttClientSession(this.ClientId); + internalEvent = new MsgPublishedInternalEvent(msgInflight, false); + + // notify not received acknowledge from broker and message not published + this.OnInternalEvent(internalEvent); + } + // NOTE : not raise events for SUBACK or UNSUBACK not received + // for the user no event raised means subscribe/unsubscribe failed + } + } else { + // re-enqueue message (I have to re-analyze for receiving PUBACK, SUBACK or UNSUBACK) + this.inflightQueue.Enqueue(msgContext); + + // update timeout + Int32 msgTimeout = this.Settings.DelayOnRetry - delta; + timeout = (msgTimeout < timeout) ? msgTimeout : timeout; + } + } + } + break; + + case MqttMsgState.WaitForPubrec: + + // QoS 2, waiting for PUBREC of a PUBLISH message sent + if (msgContext.Flow == MqttMsgFlow.ToPublish) { + acknowledge = false; + lock (this.internalQueue) { + if (this.internalQueue.Count > 0) { + msgReceived = (MqttMsgBase)this.internalQueue.Peek(); + } + } + + // it is a PUBREC message + if (msgReceived != null && msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE) { + // PUBREC message for the current PUBLISH message, send PUBREL, wait for PUBCOMP + if (msgReceived.MessageId == msgInflight.MessageId) { + lock (this.internalQueue) { + // received message processed + this.internalQueue.Dequeue(); + acknowledge = true; + msgReceivedProcessed = true; +#if TRACE + MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived); +#endif + } + + MqttMsgPubrel pubrel = new MqttMsgPubrel { + MessageId = msgInflight.MessageId + }; + + msgContext.State = MqttMsgState.WaitForPubcomp; + msgContext.Timestamp = Environment.TickCount; + msgContext.Attempt = 1; + + this.Send(pubrel); + + // update timeout : minimum between delay (based on current message sent) or current timeout + timeout = (this.Settings.DelayOnRetry < timeout) ? this.Settings.DelayOnRetry : timeout; + + // re-enqueue message + this.inflightQueue.Enqueue(msgContext); + } + } + + // current message not acknowledged + if (!acknowledge) { + delta = Environment.TickCount - msgContext.Timestamp; + // check timeout for receiving PUBREC since PUBLISH was sent + if (delta >= this.Settings.DelayOnRetry) { + // max retry not reached, resend + if (msgContext.Attempt < this.Settings.AttemptsOnRetry) { + msgContext.State = MqttMsgState.QueuedQos2; + + // re-enqueue message + this.inflightQueue.Enqueue(msgContext); + + // update timeout (0 -> reanalyze queue immediately) + timeout = 0; + } else { + // PUBREC not received in time, PUBLISH retries failed, need to remove from session inflight messages too + if (this.session != null && +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK + (this.session.InflightMessages.Contains(msgContext.Key))) +#else + this.session.InflightMessages.ContainsKey(msgContext.Key)) +#endif + { + this.session.InflightMessages.Remove(msgContext.Key); + } + + // if PUBREC for a PUBLISH message not received after retries, raise event for not published + internalEvent = new MsgPublishedInternalEvent(msgInflight, false); + // notify not received acknowledge from broker and message not published + this.OnInternalEvent(internalEvent); + } + } else { + // re-enqueue message + this.inflightQueue.Enqueue(msgContext); + + // update timeout + Int32 msgTimeout = this.Settings.DelayOnRetry - delta; + timeout = (msgTimeout < timeout) ? msgTimeout : timeout; + } + } + } + break; + + case MqttMsgState.WaitForPubrel: + + // QoS 2, waiting for PUBREL of a PUBREC message sent + if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) { + lock (this.internalQueue) { + if (this.internalQueue.Count > 0) { + msgReceived = (MqttMsgBase)this.internalQueue.Peek(); + } + } + + // it is a PUBREL message + if (msgReceived != null && msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREL_TYPE) { + // PUBREL message for the current message, send PUBCOMP + if (msgReceived.MessageId == msgInflight.MessageId) { + lock (this.internalQueue) { + // received message processed + this.internalQueue.Dequeue(); + msgReceivedProcessed = true; +#if TRACE + MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived); +#endif + } + + MqttMsgPubcomp pubcomp = new MqttMsgPubcomp { + MessageId = msgInflight.MessageId + }; + + this.Send(pubcomp); + + internalEvent = new MsgInternalEvent(msgInflight); + // notify published message from broker and acknowledged + this.OnInternalEvent(internalEvent); + + // PUBREL received (and PUBCOMP sent) for PUBLISH message with QoS Level 2, remove from session state + if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE && + this.session != null && +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK + (this.session.InflightMessages.Contains(msgContext.Key))) +#else + this.session.InflightMessages.ContainsKey(msgContext.Key)) +#endif + { + this.session.InflightMessages.Remove(msgContext.Key); + } + +#if TRACE + MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight); +#endif + } else { + // re-enqueue message + this.inflightQueue.Enqueue(msgContext); + } + } else { + // re-enqueue message + this.inflightQueue.Enqueue(msgContext); + } + } + break; + + case MqttMsgState.WaitForPubcomp: + + // QoS 2, waiting for PUBCOMP of a PUBREL message sent + if (msgContext.Flow == MqttMsgFlow.ToPublish) { + acknowledge = false; + lock (this.internalQueue) { + if (this.internalQueue.Count > 0) { + msgReceived = (MqttMsgBase)this.internalQueue.Peek(); + } + } + + // it is a PUBCOMP message + if (msgReceived != null && msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE) { + // PUBCOMP message for the current message + if (msgReceived.MessageId == msgInflight.MessageId) { + lock (this.internalQueue) { + // received message processed + this.internalQueue.Dequeue(); + acknowledge = true; + msgReceivedProcessed = true; +#if TRACE + MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived); +#endif + } + + internalEvent = new MsgPublishedInternalEvent(msgReceived, true); + // notify received acknowledge from broker of a published message + this.OnInternalEvent(internalEvent); + + // PUBCOMP received for PUBLISH message with QoS Level 2, remove from session state + if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE && + this.session != null && +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK + (this.session.InflightMessages.Contains(msgContext.Key))) +#else + this.session.InflightMessages.ContainsKey(msgContext.Key)) +#endif + { + this.session.InflightMessages.Remove(msgContext.Key); + } + +#if TRACE + MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight); +#endif + } + } + // it is a PUBREC message + else if (msgReceived != null && msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE) { + // another PUBREC message for the current message due to a retransmitted PUBLISH + // I'm in waiting for PUBCOMP, so I can discard this PUBREC + if (msgReceived.MessageId == msgInflight.MessageId) { + lock (this.internalQueue) { + // received message processed + this.internalQueue.Dequeue(); + acknowledge = true; + msgReceivedProcessed = true; +#if TRACE + MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived); +#endif + + // re-enqueue message + this.inflightQueue.Enqueue(msgContext); + } + } + } + + // current message not acknowledged + if (!acknowledge) { + delta = Environment.TickCount - msgContext.Timestamp; + // check timeout for receiving PUBCOMP since PUBREL was sent + if (delta >= this.Settings.DelayOnRetry) { + // max retry not reached, resend + if (msgContext.Attempt < this.Settings.AttemptsOnRetry) { + msgContext.State = MqttMsgState.SendPubrel; + + // re-enqueue message + this.inflightQueue.Enqueue(msgContext); + + // update timeout (0 -> reanalyze queue immediately) + timeout = 0; + } else { + // PUBCOMP not received, PUBREL retries failed, need to remove from session inflight messages too + if (this.session != null && +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK + (this.session.InflightMessages.Contains(msgContext.Key))) +#else + this.session.InflightMessages.ContainsKey(msgContext.Key)) +#endif + { + this.session.InflightMessages.Remove(msgContext.Key); + } + + // if PUBCOMP for a PUBLISH message not received after retries, raise event for not published + internalEvent = new MsgPublishedInternalEvent(msgInflight, false); + // notify not received acknowledge from broker and message not published + this.OnInternalEvent(internalEvent); + } + } else { + // re-enqueue message + this.inflightQueue.Enqueue(msgContext); + + // update timeout + Int32 msgTimeout = this.Settings.DelayOnRetry - delta; + timeout = (msgTimeout < timeout) ? msgTimeout : timeout; + } + } + } + break; + + case MqttMsgState.SendPubrec: + + // TODO : impossible ? --> QueuedQos2 ToAcknowledge + break; + + case MqttMsgState.SendPubrel: + + // QoS 2, PUBREL message to send to broker, state change to wait PUBCOMP + if (msgContext.Flow == MqttMsgFlow.ToPublish) { + MqttMsgPubrel pubrel = new MqttMsgPubrel { + MessageId = msgInflight.MessageId + }; + + msgContext.State = MqttMsgState.WaitForPubcomp; + msgContext.Timestamp = Environment.TickCount; + msgContext.Attempt++; + // retry ? set dup flag [v3.1.1] no needed + if (this.ProtocolVersion == MqttProtocolVersion.Version_3_1) { + if (msgContext.Attempt > 1) { + pubrel.DupFlag = true; + } + } + + this.Send(pubrel); + + // update timeout : minimum between delay (based on current message sent) or current timeout + timeout = (this.Settings.DelayOnRetry < timeout) ? this.Settings.DelayOnRetry : timeout; + + // re-enqueue message + this.inflightQueue.Enqueue(msgContext); + } + break; + + case MqttMsgState.SendPubcomp: + // TODO : impossible ? + break; + case MqttMsgState.SendPuback: + // TODO : impossible ? --> QueuedQos1 ToAcknowledge + break; + default: + break; } + } + + // if calculated timeout is MaxValue, it means that must be Infinite (-1) + if (timeout == Int32.MaxValue) { + timeout = Timeout.Infinite; + } + + // if message received is orphan, no corresponding message in inflight queue + // based on messageId, we need to remove from the queue + if (msgReceived != null && !msgReceivedProcessed) { + this.internalQueue.Dequeue(); +#if TRACE + MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0} orphan", msgReceived); +#endif + } } - // clean any previous session - else - { - if (this.session != null) - this.session.Clear(); - } + } } + } catch (MqttCommunicationException e) { + // possible exception on Send, I need to re-enqueue not sent message + if (msgContext != null) { + // re-enqueue message + this.inflightQueue.Enqueue(msgContext); + } + +#if TRACE + MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString()); +#endif + + // raise disconnection client event + this.OnConnectionClosing(); + } + } + + /// + /// Restore session + /// + private void RestoreSession() { + // if not clean session + if (!this.CleanSession) { + // there is a previous session + if (this.session != null) { + lock (this.inflightQueue) { + foreach (MqttMsgContext msgContext in this.session.InflightMessages.Values) { + this.inflightQueue.Enqueue(msgContext); + + // if it is a PUBLISH message to publish + if (msgContext.Message.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE && + msgContext.Flow == MqttMsgFlow.ToPublish) { + // it's QoS 1 and we haven't received PUBACK + if (msgContext.Message.QosLevel == MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE && + msgContext.State == MqttMsgState.WaitForPuback) { + // we haven't received PUBACK, we need to resend PUBLISH message + msgContext.State = MqttMsgState.QueuedQos1; + } + // it's QoS 2 + else if (msgContext.Message.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE) { + // we haven't received PUBREC, we need to resend PUBLISH message + if (msgContext.State == MqttMsgState.WaitForPubrec) { + msgContext.State = MqttMsgState.QueuedQos2; + } + // we haven't received PUBCOMP, we need to resend PUBREL for it + else if (msgContext.State == MqttMsgState.WaitForPubcomp) { + msgContext.State = MqttMsgState.SendPubrel; + } + } + } + } + } + + // unlock process inflight queue + _ = this.inflightWaitHandle.Set(); + } else { + // create new session + this.session = new MqttClientSession(this.ClientId); + } + } + // clean any previous session + else { + if (this.session != null) { + this.session.Clear(); + } + } + } #if BROKER @@ -2578,55 +2385,50 @@ namespace uPLibrary.Networking.M2Mqtt } #endif - /// - /// Generate the next message identifier - /// - /// Message identifier - private ushort GetMessageId() - { - // if 0 or max UInt16, it becomes 1 (first valid messageId) - this.messageIdCounter = ((this.messageIdCounter % UInt16.MaxValue) != 0) ? (ushort)(this.messageIdCounter + 1) : (ushort)1; - return this.messageIdCounter; - } - - /// - /// Finder class for PUBLISH message inside a queue - /// - internal class MqttMsgContextFinder - { - // PUBLISH message id - internal ushort MessageId { get; set; } - // message flow into inflight queue - internal MqttMsgFlow Flow { get; set; } - - /// - /// Constructor - /// - /// Message Id - /// Message flow inside inflight queue - internal MqttMsgContextFinder(ushort messageId, MqttMsgFlow flow) - { - this.MessageId = messageId; - this.Flow = flow; - } - - internal bool Find(object item) - { - MqttMsgContext msgCtx = (MqttMsgContext)item; - return ((msgCtx.Message.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) && - (msgCtx.Message.MessageId == this.MessageId) && - msgCtx.Flow == this.Flow); - - } - } + /// + /// Generate the next message identifier + /// + /// Message identifier + private UInt16 GetMessageId() { + // if 0 or max UInt16, it becomes 1 (first valid messageId) + this.messageIdCounter = (this.messageIdCounter % UInt16.MaxValue != 0) ? (UInt16)(this.messageIdCounter + 1) : (UInt16)1; + return this.messageIdCounter; } /// - /// MQTT protocol version + /// Finder class for PUBLISH message inside a queue /// - public enum MqttProtocolVersion - { - Version_3_1 = MqttMsgConnect.PROTOCOL_VERSION_V3_1, - Version_3_1_1 = MqttMsgConnect.PROTOCOL_VERSION_V3_1_1 + internal class MqttMsgContextFinder { + // PUBLISH message id + internal UInt16 MessageId { get; set; } + // message flow into inflight queue + internal MqttMsgFlow Flow { get; set; } + + /// + /// Constructor + /// + /// Message Id + /// Message flow inside inflight queue + internal MqttMsgContextFinder(UInt16 messageId, MqttMsgFlow flow) { + this.MessageId = messageId; + this.Flow = flow; + } + + internal Boolean Find(Object item) { + MqttMsgContext msgCtx = (MqttMsgContext)item; + return msgCtx.Message.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE && + msgCtx.Message.MessageId == this.MessageId && + msgCtx.Flow == this.Flow; + + } } -} + } + + /// + /// MQTT protocol version + /// + public enum MqttProtocolVersion { + Version_3_1 = MqttMsgConnect.PROTOCOL_VERSION_V3_1, + Version_3_1_1 = MqttMsgConnect.PROTOCOL_VERSION_V3_1_1 + } +} \ No newline at end of file diff --git a/M2Mqtt/MqttSecurity.cs b/M2Mqtt/MqttSecurity.cs index b19f108..afd54e3 100644 --- a/M2Mqtt/MqttSecurity.cs +++ b/M2Mqtt/MqttSecurity.cs @@ -14,17 +14,15 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ -namespace uPLibrary.Networking.M2Mqtt -{ - /// - /// Supported SSL/TLS protocol versions - /// - public enum MqttSslProtocols - { - None, - SSLv3, - TLSv1_0, - TLSv1_1, - TLSv1_2 - } +namespace uPLibrary.Networking.M2Mqtt { + /// + /// Supported SSL/TLS protocol versions + /// + public enum MqttSslProtocols { + None, + SSLv3, + TLSv1_0, + TLSv1_1, + TLSv1_2 + } } diff --git a/M2Mqtt/MqttSettings.cs b/M2Mqtt/MqttSettings.cs index 994f622..af83d39 100644 --- a/M2Mqtt/MqttSettings.cs +++ b/M2Mqtt/MqttSettings.cs @@ -14,91 +14,90 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ -namespace uPLibrary.Networking.M2Mqtt -{ +using System; + +namespace uPLibrary.Networking.M2Mqtt { + /// + /// Settings class for the MQTT broker + /// + public class MqttSettings { + // default port for MQTT protocol + public const Int32 MQTT_BROKER_DEFAULT_PORT = 1883; + public const Int32 MQTT_BROKER_DEFAULT_SSL_PORT = 8883; + // default timeout on receiving from client + public const Int32 MQTT_DEFAULT_TIMEOUT = 30000; + // max publish, subscribe and unsubscribe retry for QoS Level 1 or 2 + public const Int32 MQTT_ATTEMPTS_RETRY = 3; + // delay for retry publish, subscribe and unsubscribe for QoS Level 1 or 2 + public const Int32 MQTT_DELAY_RETRY = 10000; + // broker need to receive the first message (CONNECT) + // within a reasonable amount of time after TCP/IP connection + public const Int32 MQTT_CONNECT_TIMEOUT = 30000; + // default inflight queue size + public const Int32 MQTT_MAX_INFLIGHT_QUEUE_SIZE = Int32.MaxValue; + /// - /// Settings class for the MQTT broker + /// Listening connection port /// - public class MqttSettings - { - // default port for MQTT protocol - public const int MQTT_BROKER_DEFAULT_PORT = 1883; - public const int MQTT_BROKER_DEFAULT_SSL_PORT = 8883; - // default timeout on receiving from client - public const int MQTT_DEFAULT_TIMEOUT = 30000; - // max publish, subscribe and unsubscribe retry for QoS Level 1 or 2 - public const int MQTT_ATTEMPTS_RETRY = 3; - // delay for retry publish, subscribe and unsubscribe for QoS Level 1 or 2 - public const int MQTT_DELAY_RETRY = 10000; - // broker need to receive the first message (CONNECT) - // within a reasonable amount of time after TCP/IP connection - public const int MQTT_CONNECT_TIMEOUT = 30000; - // default inflight queue size - public const int MQTT_MAX_INFLIGHT_QUEUE_SIZE = int.MaxValue; - - /// - /// Listening connection port - /// - public int Port { get; internal set; } - - /// - /// Listening connection SSL port - /// - public int SslPort { get; internal set; } - - /// - /// Timeout on client connection (before receiving CONNECT message) - /// - public int TimeoutOnConnection { get; internal set; } - - /// - /// Timeout on receiving - /// - public int TimeoutOnReceiving { get; internal set; } - - /// - /// Attempts on retry - /// - public int AttemptsOnRetry { get; internal set; } - - /// - /// Delay on retry - /// - public int DelayOnRetry { get; internal set; } - - /// - /// Inflight queue size - /// - public int InflightQueueSize { get; set; } - - /// - /// Singleton instance of settings - /// - public static MqttSettings Instance - { - get - { - if (instance == null) - instance = new MqttSettings(); - return instance; - } - } - - // singleton instance - private static MqttSettings instance; - - /// - /// Constructor - /// - private MqttSettings() - { - this.Port = MQTT_BROKER_DEFAULT_PORT; - this.SslPort = MQTT_BROKER_DEFAULT_SSL_PORT; - this.TimeoutOnReceiving = MQTT_DEFAULT_TIMEOUT; - this.AttemptsOnRetry = MQTT_ATTEMPTS_RETRY; - this.DelayOnRetry = MQTT_DELAY_RETRY; - this.TimeoutOnConnection = MQTT_CONNECT_TIMEOUT; - this.InflightQueueSize = MQTT_MAX_INFLIGHT_QUEUE_SIZE; - } - } + public Int32 Port { get; internal set; } + + /// + /// Listening connection SSL port + /// + public Int32 SslPort { get; internal set; } + + /// + /// Timeout on client connection (before receiving CONNECT message) + /// + public Int32 TimeoutOnConnection { get; internal set; } + + /// + /// Timeout on receiving + /// + public Int32 TimeoutOnReceiving { get; internal set; } + + /// + /// Attempts on retry + /// + public Int32 AttemptsOnRetry { get; internal set; } + + /// + /// Delay on retry + /// + public Int32 DelayOnRetry { get; internal set; } + + /// + /// Inflight queue size + /// + public Int32 InflightQueueSize { get; set; } + + /// + /// Singleton instance of settings + /// + public static MqttSettings Instance { + get { + if (instance == null) { + instance = new MqttSettings(); + } + + return instance; + } + } + + // singleton instance + private static MqttSettings instance; + + /// + /// Constructor + /// + private MqttSettings() { + this.Port = MQTT_BROKER_DEFAULT_PORT; + this.SslPort = MQTT_BROKER_DEFAULT_SSL_PORT; + this.TimeoutOnReceiving = MQTT_DEFAULT_TIMEOUT; + this.AttemptsOnRetry = MQTT_ATTEMPTS_RETRY; + this.DelayOnRetry = MQTT_DELAY_RETRY; + this.TimeoutOnConnection = MQTT_CONNECT_TIMEOUT; + this.InflightQueueSize = MQTT_MAX_INFLIGHT_QUEUE_SIZE; + } + } } diff --git a/M2Mqtt/Net/Fx.cs b/M2Mqtt/Net/Fx.cs index 3fcc505..a2a243c 100644 --- a/M2Mqtt/Net/Fx.cs +++ b/M2Mqtt/Net/Fx.cs @@ -14,23 +14,16 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ +using System; using System.Threading; -namespace uPLibrary.Networking.M2Mqtt -{ - /// - /// Support methods fos specific framework - /// - public class Fx - { - public static void StartThread(ThreadStart threadStart) - { - new Thread(threadStart).Start(); - } - - public static void SleepThread(int millisecondsTimeout) - { - Thread.Sleep(millisecondsTimeout); - } - } +namespace uPLibrary.Networking.M2Mqtt { + /// + /// Support methods fos specific framework + /// + public class Fx { + public static void StartThread(ThreadStart threadStart) => new Thread(threadStart).Start(); + + public static void SleepThread(Int32 millisecondsTimeout) => Thread.Sleep(millisecondsTimeout); + } } diff --git a/M2Mqtt/Net/MqttNetworkChannel.cs b/M2Mqtt/Net/MqttNetworkChannel.cs index c98879a..3aeb662 100644 --- a/M2Mqtt/Net/MqttNetworkChannel.cs +++ b/M2Mqtt/Net/MqttNetworkChannel.cs @@ -15,13 +15,17 @@ Contributors: */ #if SSL -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3) +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 using Microsoft.SPOT.Net.Security; #else -using System.Net.Security; using System.Security.Authentication; +#if NETCOREAPP +using System.Net.Security; +#endif + #endif #endif + using System.Net.Sockets; using System.Net; using System.Security.Cryptography.X509Certificates; @@ -29,90 +33,73 @@ using System; //using System.Security.Authentication; //using System.Net.Security; -namespace uPLibrary.Networking.M2Mqtt -{ - /// - /// Channel to communicate over the network - /// - public class MqttNetworkChannel : IMqttNetworkChannel - { +namespace uPLibrary.Networking.M2Mqtt { + /// + /// Channel to communicate over the network + /// + public class MqttNetworkChannel : IMqttNetworkChannel { #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) - private readonly RemoteCertificateValidationCallback userCertificateValidationCallback; - private readonly LocalCertificateSelectionCallback userCertificateSelectionCallback; + private readonly RemoteCertificateValidationCallback userCertificateValidationCallback; + private readonly LocalCertificateSelectionCallback userCertificateSelectionCallback; #endif - // remote host information - private string remoteHostName; - private IPAddress remoteIpAddress; - private int remotePort; + // remote host information - // socket for communication - private Socket socket; - // using SSL - private bool secure; + // socket for communication + private Socket socket; + // using SSL + private readonly Boolean secure; - // CA certificate (on client) - private X509Certificate caCert; - // Server certificate (on broker) - private X509Certificate serverCert; - // client certificate (on client) - private X509Certificate clientCert; + // CA certificate (on client) + private readonly X509Certificate caCert; + // Server certificate (on broker) + private readonly X509Certificate serverCert; + // client certificate (on client) + private readonly X509Certificate clientCert; - // SSL/TLS protocol version - private MqttSslProtocols sslProtocol; + // SSL/TLS protocol version + private readonly MqttSslProtocols sslProtocol; - /// - /// Remote host name - /// - public string RemoteHostName { get { return this.remoteHostName; } } + /// + /// Remote host name + /// + public String RemoteHostName { get; } - /// - /// Remote IP address - /// - public IPAddress RemoteIpAddress { get { return this.remoteIpAddress; } } + /// + /// Remote IP address + /// + public IPAddress RemoteIpAddress { get; } - /// - /// Remote port - /// - public int RemotePort { get { return this.remotePort; } } + /// + /// Remote port + /// + public Int32 RemotePort { get; } #if SSL - // SSL stream - private SslStream sslStream; -#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) - private NetworkStream netStream; + // SSL stream + private SslStream sslStream; +#if !MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 + private NetworkStream netStream; #endif #endif - /// - /// Data available on the channel - /// - public bool DataAvailable - { - get - { + /// + /// Data available on the channel + /// #if SSL -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3) - if (secure) - return this.sslStream.DataAvailable; - else - return (this.socket.Available > 0); +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 + public Boolean DataAvailable => this.secure ? this.sslStream.DataAvailable : this.socket.Available > 0; #else - if (secure) - return this.netStream.DataAvailable; - else - return (this.socket.Available > 0); + public Boolean DataAvailable => this.secure ? this.netStream.DataAvailable : this.socket.Available > 0; #endif #else - return (this.socket.Available > 0); + public Boolean DataAvailable => this.socket.Available > 0; #endif - } - } - /// - /// Constructor - /// - /// Socket opened with the client - public MqttNetworkChannel(Socket socket) + /// + /// Constructor + /// + /// Socket opened with the client + public MqttNetworkChannel(Socket socket) #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) : this(socket, false, null, MqttSslProtocols.None, null, null) #else @@ -120,212 +107,203 @@ namespace uPLibrary.Networking.M2Mqtt #endif { - } - - /// - /// Constructor - /// - /// Socket opened with the client - /// Secure connection (SSL/TLS) - /// Server X509 certificate for secure connection - /// SSL/TLS protocol version + } + + /// + /// Constructor + /// + /// Socket opened with the client + /// Secure connection (SSL/TLS) + /// Server X509 certificate for secure connection + /// SSL/TLS protocol version #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) - /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party - /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication - public MqttNetworkChannel(Socket socket, bool secure, X509Certificate serverCert, MqttSslProtocols sslProtocol, - RemoteCertificateValidationCallback userCertificateValidationCallback, - LocalCertificateSelectionCallback userCertificateSelectionCallback) + /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party + /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication + public MqttNetworkChannel(Socket socket, Boolean secure, X509Certificate serverCert, MqttSslProtocols sslProtocol, + RemoteCertificateValidationCallback userCertificateValidationCallback, + LocalCertificateSelectionCallback userCertificateSelectionCallback) #else public MqttNetworkChannel(Socket socket, bool secure, X509Certificate serverCert, MqttSslProtocols sslProtocol) #endif { - this.socket = socket; - this.secure = secure; - this.serverCert = serverCert; - this.sslProtocol = sslProtocol; + this.socket = socket; + this.secure = secure; + this.serverCert = serverCert; + this.sslProtocol = sslProtocol; #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) - this.userCertificateValidationCallback = userCertificateValidationCallback; - this.userCertificateSelectionCallback = userCertificateSelectionCallback; + this.userCertificateValidationCallback = userCertificateValidationCallback; + this.userCertificateSelectionCallback = userCertificateSelectionCallback; #endif - } + } - /// - /// Constructor - /// - /// Remote Host name - /// Remote port - public MqttNetworkChannel(string remoteHostName, int remotePort) + /// + /// Constructor + /// + /// Remote Host name + /// Remote port + public MqttNetworkChannel(String remoteHostName, Int32 remotePort) #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) : this(remoteHostName, remotePort, false, null, null, MqttSslProtocols.None, null, null) #else : this(remoteHostName, remotePort, false, null, null, MqttSslProtocols.None) #endif { - } + } - /// - /// Constructor - /// - /// Remote Host name - /// Remote port - /// Using SSL - /// CA certificate - /// Client certificate - /// SSL/TLS protocol version + /// + /// Constructor + /// + /// Remote Host name + /// Remote port + /// Using SSL + /// CA certificate + /// Client certificate + /// SSL/TLS protocol version #if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) - /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party - /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication - public MqttNetworkChannel(string remoteHostName, int remotePort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol, - RemoteCertificateValidationCallback userCertificateValidationCallback, - LocalCertificateSelectionCallback userCertificateSelectionCallback) + /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party + /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication + public MqttNetworkChannel(String remoteHostName, Int32 remotePort, Boolean secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol, + RemoteCertificateValidationCallback userCertificateValidationCallback, + LocalCertificateSelectionCallback userCertificateSelectionCallback) #else public MqttNetworkChannel(string remoteHostName, int remotePort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol) #endif { - IPAddress remoteIpAddress = null; - try - { - // check if remoteHostName is a valid IP address and get it - remoteIpAddress = IPAddress.Parse(remoteHostName); - } - catch - { - } + IPAddress remoteIpAddress = null; + try { + // check if remoteHostName is a valid IP address and get it + remoteIpAddress = IPAddress.Parse(remoteHostName); + } catch { + } - // in this case the parameter remoteHostName isn't a valid IP address - if (remoteIpAddress == null) - { - IPHostEntry hostEntry = Dns.GetHostEntry(remoteHostName); - if ((hostEntry != null) && (hostEntry.AddressList.Length > 0)) - { - // check for the first address not null - // it seems that with .Net Micro Framework, the IPV6 addresses aren't supported and return "null" - int i = 0; - while (hostEntry.AddressList[i] == null) i++; - remoteIpAddress = hostEntry.AddressList[i]; - } - else - { - throw new Exception("No address found for the remote host name"); - } - } + // in this case the parameter remoteHostName isn't a valid IP address + if (remoteIpAddress == null) { + IPHostEntry hostEntry = Dns.GetHostEntry(remoteHostName); + if (hostEntry != null && hostEntry.AddressList.Length > 0) { + // check for the first address not null + // it seems that with .Net Micro Framework, the IPV6 addresses aren't supported and return "null" + Int32 i = 0; + while (hostEntry.AddressList[i] == null) { + i++; + } - this.remoteHostName = remoteHostName; - this.remoteIpAddress = remoteIpAddress; - this.remotePort = remotePort; - this.secure = secure; - this.caCert = caCert; - this.clientCert = clientCert; - this.sslProtocol = sslProtocol; -#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) - this.userCertificateValidationCallback = userCertificateValidationCallback; - this.userCertificateSelectionCallback = userCertificateSelectionCallback; -#endif + remoteIpAddress = hostEntry.AddressList[i]; + } else { + throw new Exception("No address found for the remote host name"); } + } - /// - /// Connect to remote server - /// - public void Connect() - { - this.socket = new Socket(IPAddressUtility.GetAddressFamily(this.remoteIpAddress), SocketType.Stream, ProtocolType.Tcp); - // try connection to the broker - this.socket.Connect(new IPEndPoint(this.remoteIpAddress, this.remotePort)); + this.RemoteHostName = remoteHostName; + this.RemoteIpAddress = remoteIpAddress; + this.RemotePort = remotePort; + this.secure = secure; + this.caCert = caCert; + this.clientCert = clientCert; + this.sslProtocol = sslProtocol; +#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) + this.userCertificateValidationCallback = userCertificateValidationCallback; + this.userCertificateSelectionCallback = userCertificateSelectionCallback; +#endif + } + + /// + /// Connect to remote server + /// + public void Connect() { + this.socket = new Socket(IPAddressUtility.GetAddressFamily(this.RemoteIpAddress), SocketType.Stream, ProtocolType.Tcp); + // try connection to the broker + this.socket.Connect(new IPEndPoint(this.RemoteIpAddress, this.RemotePort)); #if SSL - // secure channel requested - if (secure) - { - // create SSL stream -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3) + // secure channel requested + if (this.secure) { + // create SSL stream +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 this.sslStream = new SslStream(this.socket); #else - this.netStream = new NetworkStream(this.socket); - this.sslStream = new SslStream(this.netStream, false, this.userCertificateValidationCallback, this.userCertificateSelectionCallback); + this.netStream = new NetworkStream(this.socket); + this.sslStream = new SslStream(this.netStream, false, this.userCertificateValidationCallback, this.userCertificateSelectionCallback); #endif - // server authentication (SSL/TLS handshake) -#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3) + // server authentication (SSL/TLS handshake) +#if MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 this.sslStream.AuthenticateAsClient(this.remoteHostName, this.clientCert, new X509Certificate[] { this.caCert }, SslVerification.CertificateRequired, MqttSslUtility.ToSslPlatformEnum(this.sslProtocol)); #else - X509CertificateCollection clientCertificates = null; - // check if there is a client certificate to add to the collection, otherwise it's null (as empty) - if (this.clientCert != null) - clientCertificates = new X509CertificateCollection(new X509Certificate[] { this.clientCert }); - - this.sslStream.AuthenticateAsClient(this.remoteHostName, - clientCertificates, - MqttSslUtility.ToSslPlatformEnum(this.sslProtocol), - false); - -#endif - } -#endif + X509CertificateCollection clientCertificates = null; + // check if there is a client certificate to add to the collection, otherwise it's null (as empty) + if (this.clientCert != null) { + clientCertificates = new X509CertificateCollection(new X509Certificate[] { this.clientCert }); } - /// - /// Send data on the network channel - /// - /// Data buffer to send - /// Number of byte sent - public int Send(byte[] buffer) - { + this.sslStream.AuthenticateAsClient(this.RemoteHostName, + clientCertificates, + MqttSslUtility.ToSslPlatformEnum(this.sslProtocol), + false); + +#endif + } +#endif + } + + /// + /// Send data on the network channel + /// + /// Data buffer to send + /// Number of byte sent + public Int32 Send(Byte[] buffer) { #if SSL - if (this.secure) - { - this.sslStream.Write(buffer, 0, buffer.Length); - this.sslStream.Flush(); - return buffer.Length; - } - else - return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None); + if (this.secure) { + this.sslStream.Write(buffer, 0, buffer.Length); + this.sslStream.Flush(); + return buffer.Length; + } else { + return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None); + } #else return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None); #endif - } + } - /// - /// Receive data from the network - /// - /// Data buffer for receiving data - /// Number of bytes received - public int Receive(byte[] buffer) - { + /// + /// Receive data from the network + /// + /// Data buffer for receiving data + /// Number of bytes received + public Int32 Receive(Byte[] buffer) { #if SSL - if (this.secure) - { - // read all data needed (until fill buffer) - int idx = 0, read = 0; - while (idx < buffer.Length) - { - // fixed scenario with socket closed gracefully by peer/broker and - // Read return 0. Avoid infinite loop. - read = this.sslStream.Read(buffer, idx, buffer.Length - idx); - if (read == 0) - return 0; - idx += read; - } - return buffer.Length; - } - else - { - // read all data needed (until fill buffer) - int idx = 0, read = 0; - while (idx < buffer.Length) - { - // fixed scenario with socket closed gracefully by peer/broker and - // Read return 0. Avoid infinite loop. - read = this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None); - if (read == 0) - return 0; - idx += read; - } - return buffer.Length; - } + if (this.secure) { + // read all data needed (until fill buffer) + Int32 idx = 0, read = 0; + while (idx < buffer.Length) { + // fixed scenario with socket closed gracefully by peer/broker and + // Read return 0. Avoid infinite loop. + read = this.sslStream.Read(buffer, idx, buffer.Length - idx); + if (read == 0) { + return 0; + } + + idx += read; + } + return buffer.Length; + } else { + // read all data needed (until fill buffer) + Int32 idx = 0, read = 0; + while (idx < buffer.Length) { + // fixed scenario with socket closed gracefully by peer/broker and + // Read return 0. Avoid infinite loop. + read = this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None); + if (read == 0) { + return 0; + } + + idx += read; + } + return buffer.Length; + } #else // read all data needed (until fill buffer) int idx = 0, read = 0; @@ -340,118 +318,94 @@ namespace uPLibrary.Networking.M2Mqtt } return buffer.Length; #endif - } - - /// - /// Receive data from the network channel with a specified timeout - /// - /// Data buffer for receiving data - /// Timeout on receiving (in milliseconds) - /// Number of bytes received - public int Receive(byte[] buffer, int timeout) - { - // check data availability (timeout is in microseconds) - if (this.socket.Poll(timeout * 1000, SelectMode.SelectRead)) - { - return this.Receive(buffer); - } - else - { - return 0; - } - } - - /// - /// Close the network channel - /// - public void Close() - { -#if SSL - if (this.secure) - { -#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) - this.netStream.Close(); -#endif - this.sslStream.Close(); - } - this.socket.Close(); -#else - this.socket.Close(); -#endif - } - - /// - /// Accept connection from a remote client - /// - public void Accept() - { -#if SSL - // secure channel requested - if (secure) - { -#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3) - - this.netStream = new NetworkStream(this.socket); - this.sslStream = new SslStream(this.netStream, false, this.userCertificateValidationCallback, this.userCertificateSelectionCallback); - - this.sslStream.AuthenticateAsServer(this.serverCert, false, MqttSslUtility.ToSslPlatformEnum(this.sslProtocol), false); -#endif - } - - return; -#else - return; -#endif - } } /// - /// IPAddress Utility class + /// Receive data from the network channel with a specified timeout /// - public static class IPAddressUtility - { - /// - /// Return AddressFamily for the IP address - /// - /// IP address to check - /// Address family - public static AddressFamily GetAddressFamily(IPAddress ipAddress) - { -#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) - return ipAddress.AddressFamily; + /// Data buffer for receiving data + /// Timeout on receiving (in milliseconds) + /// Number of bytes received + public Int32 Receive(Byte[] buffer, Int32 timeout) => + // check data availability (timeout is in microseconds) + this.socket.Poll(timeout * 1000, SelectMode.SelectRead) ? this.Receive(buffer) : 0; + + /// + /// Close the network channel + /// + public void Close() { +#if SSL + if (this.secure) { +#if !MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 + this.netStream.Close(); +#endif + this.sslStream.Close(); + } + this.socket.Close(); +#else + this.socket.Close(); +#endif + } + + /// + /// Accept connection from a remote client + /// + public void Accept() { +#if SSL + // secure channel requested + if (this.secure) { +#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3) + + this.netStream = new NetworkStream(this.socket); + this.sslStream = new SslStream(this.netStream, false, this.userCertificateValidationCallback, this.userCertificateSelectionCallback); + + this.sslStream.AuthenticateAsServer(this.serverCert, false, MqttSslUtility.ToSslPlatformEnum(this.sslProtocol), false); +#endif + } + + return; +#else + return; +#endif + } + } + + /// + /// IPAddress Utility class + /// + public static class IPAddressUtility { + /// + /// Return AddressFamily for the IP address + /// + /// IP address to check + /// Address family + public static AddressFamily GetAddressFamily(IPAddress ipAddress) => +#if !MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 + ipAddress.AddressFamily; #else return (ipAddress.ToString().IndexOf(':') != -1) ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork; #endif - } - } - /// - /// MQTT SSL utility class - /// - public static class MqttSslUtility + } + + /// + /// MQTT SSL utility class + /// + public static class MqttSslUtility { +#if !MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 && !COMPACT_FRAMEWORK + + public static SslProtocols ToSslPlatformEnum(MqttSslProtocols mqttSslProtocol) => mqttSslProtocol switch { -#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 && !COMPACT_FRAMEWORK) - public static SslProtocols ToSslPlatformEnum(MqttSslProtocols mqttSslProtocol) - { - switch (mqttSslProtocol) - { - case MqttSslProtocols.None: - return SslProtocols.None; - case MqttSslProtocols.SSLv3: - return SslProtocols.Ssl3; - case MqttSslProtocols.TLSv1_0: - return SslProtocols.Tls; - /*case MqttSslProtocols.TLSv1_1: - return SslProtocols.Tls11; - case MqttSslProtocols.TLSv1_2: - return SslProtocols.Tls12;*/ - default: - throw new ArgumentException("SSL/TLS protocol version not supported"); - } - } -#elif (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3) - public static SslProtocols ToSslPlatformEnum(MqttSslProtocols mqttSslProtocol) + MqttSslProtocols.None => SslProtocols.None, + //MqttSslProtocols.SSLv3 => SslProtocols.Ssl3, + MqttSslProtocols.TLSv1_0 => SslProtocols.Tls, + MqttSslProtocols.TLSv1_1 => SslProtocols.Tls11, + MqttSslProtocols.TLSv1_2 => SslProtocols.Tls12, + _ => throw new ArgumentException("SSL/TLS protocol version not supported"), + }; +#elif MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 + public static SslProtocols ToSslPlatformEnum(MqttSslProtocols mqttSslProtocol) { switch (mqttSslProtocol) { @@ -468,5 +422,5 @@ namespace uPLibrary.Networking.M2Mqtt } } #endif - } + } } diff --git a/M2Mqtt/Properties/AssemblyInfo.cs b/M2Mqtt/Properties/AssemblyInfo.cs index b218618..d2d0755 100644 --- a/M2Mqtt/Properties/AssemblyInfo.cs +++ b/M2Mqtt/Properties/AssemblyInfo.cs @@ -13,7 +13,7 @@ and the Eclipse Distribution License is available at Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ - +#if !NETCOREAPP using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -41,4 +41,5 @@ using System.Runtime.InteropServices; // to avoid compilation error (AssemblyFileVersionAttribute doesn't exist) under .Net CF 3.5 #if !WindowsCE [assembly: AssemblyFileVersion("4.3.0.0")] +#endif #endif \ No newline at end of file diff --git a/M2Mqtt/Session/MqttClientSession.cs b/M2Mqtt/Session/MqttClientSession.cs index 7613880..cd3f824 100644 --- a/M2Mqtt/Session/MqttClientSession.cs +++ b/M2Mqtt/Session/MqttClientSession.cs @@ -14,20 +14,18 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ -namespace uPLibrary.Networking.M2Mqtt.Session -{ +using System; + +namespace uPLibrary.Networking.M2Mqtt.Session { + /// + /// MQTT Client Session + /// + public class MqttClientSession : MqttSession { /// - /// MQTT Client Session + /// Constructor /// - public class MqttClientSession : MqttSession - { - /// - /// Constructor - /// - /// Client Id to create session - public MqttClientSession(string clientId) - : base(clientId) - { - } - } + /// Client Id to create session + public MqttClientSession(String clientId) : base(clientId) { + } + } } diff --git a/M2Mqtt/Session/MqttSession.cs b/M2Mqtt/Session/MqttSession.cs index 712a854..91cc416 100644 --- a/M2Mqtt/Session/MqttSession.cs +++ b/M2Mqtt/Session/MqttSession.cs @@ -14,50 +14,46 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ +using System; using System.Collections; -namespace uPLibrary.Networking.M2Mqtt.Session -{ +namespace uPLibrary.Networking.M2Mqtt.Session { + /// + /// MQTT Session base class + /// + public abstract class MqttSession { /// - /// MQTT Session base class + /// Client Id /// - public abstract class MqttSession - { - /// - /// Client Id - /// - public string ClientId { get; set; } - - /// - /// Messages inflight during session - /// - public Hashtable InflightMessages { get; set; } - - /// - /// Constructor - /// - public MqttSession() - : this(null) - { - } - - /// - /// Constructor - /// - /// Client Id to create session - public MqttSession(string clientId) - { - this.ClientId = clientId; - this.InflightMessages = new Hashtable(); - } - - /// - /// Clean session - /// - public virtual void Clear() - { - this.ClientId = null; - this.InflightMessages.Clear(); - } - } -} + public String ClientId { get; set; } + + /// + /// Messages inflight during session + /// + public Hashtable InflightMessages { get; set; } + + /// + /// Constructor + /// + public MqttSession() + : this(null) { + } + + /// + /// Constructor + /// + /// Client Id to create session + public MqttSession(String clientId) { + this.ClientId = clientId; + this.InflightMessages = new Hashtable(); + } + + /// + /// Clean session + /// + public virtual void Clear() { + this.ClientId = null; + this.InflightMessages.Clear(); + } + } +} \ No newline at end of file diff --git a/M2Mqtt/Utility/QueueExtension.cs b/M2Mqtt/Utility/QueueExtension.cs index 70175c3..39de963 100644 --- a/M2Mqtt/Utility/QueueExtension.cs +++ b/M2Mqtt/Utility/QueueExtension.cs @@ -17,34 +17,31 @@ Contributors: using System; using System.Collections; -namespace uPLibrary.Networking.M2Mqtt.Utility -{ +namespace uPLibrary.Networking.M2Mqtt.Utility { + /// + /// Extension class for a Queue + /// + internal static class QueueExtension { /// - /// Extension class for a Queue + /// Predicate for searching inside a queue /// - internal static class QueueExtension - { - /// - /// Predicate for searching inside a queue - /// - /// Item of the queue - /// Result of predicate - internal delegate bool QueuePredicate(object item); - - /// - /// Get (without removing) an item from queue based on predicate - /// - /// Queue in which to search - /// Predicate to verify to get item - /// Item matches the predicate - internal static object Get(Queue queue, QueuePredicate predicate) - { - foreach (var item in queue) - { - if (predicate(item)) - return item; - } - return null; - } - } + /// Item of the queue + /// Result of predicate + internal delegate Boolean QueuePredicate(Object item); + + /// + /// Get (without removing) an item from queue based on predicate + /// + /// Queue in which to search + /// Predicate to verify to get item + /// Item matches the predicate + internal static Object Get(Queue queue, QueuePredicate predicate) { + foreach (Object item in queue) { + if (predicate(item)) { + return item; + } + } + return null; + } + } } diff --git a/M2Mqtt/Utility/Trace.cs b/M2Mqtt/Utility/Trace.cs index c299ebe..3771285 100644 --- a/M2Mqtt/Utility/Trace.cs +++ b/M2Mqtt/Utility/Trace.cs @@ -14,73 +14,57 @@ Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ +using System; using System.Diagnostics; -namespace uPLibrary.Networking.M2Mqtt.Utility -{ - /// - /// Tracing levels - /// - public enum TraceLevel - { - Error = 0x01, - Warning = 0x02, - Information = 0x04, - Verbose = 0x0F, - Frame = 0x10, - Queuing = 0x20 - } - - // delegate for writing trace - public delegate void WriteTrace(string format, params object[] args); - - /// - /// Tracing class - /// - public static class Trace - { - public static TraceLevel TraceLevel; - public static WriteTrace TraceListener; - - [Conditional("DEBUG")] - public static void Debug(string format, params object[] args) - { - if (TraceListener != null) - { - TraceListener(format, args); - } - } - - public static void WriteLine(TraceLevel level, string format) - { - if (TraceListener != null && (level & TraceLevel) > 0) - { - TraceListener(format); - } - } - - public static void WriteLine(TraceLevel level, string format, object arg1) - { - if (TraceListener != null && (level & TraceLevel) > 0) - { - TraceListener(format, arg1); - } - } - - public static void WriteLine(TraceLevel level, string format, object arg1, object arg2) - { - if (TraceListener != null && (level & TraceLevel) > 0) - { - TraceListener(format, arg1, arg2); - } - } - - public static void WriteLine(TraceLevel level, string format, object arg1, object arg2, object arg3) - { - if (TraceListener != null && (level & TraceLevel) > 0) - { - TraceListener(format, arg1, arg2, arg3); - } - } - } +namespace uPLibrary.Networking.M2Mqtt.Utility { + /// + /// Tracing levels + /// + public enum TraceLevel { + Error = 0x01, + Warning = 0x02, + Information = 0x04, + Verbose = 0x0F, + Frame = 0x10, + Queuing = 0x20 + } + + // delegate for writing trace + public delegate void WriteTrace(String format, params Object[] args); + + /// + /// Tracing class + /// + public static class Trace { + public static TraceLevel TraceLevel; + public static WriteTrace TraceListener; + + [Conditional("DEBUG")] + public static void Debug(String format, params Object[] args) => TraceListener?.Invoke(format, args); + + public static void WriteLine(TraceLevel level, String format) { + if ((level & TraceLevel) > 0) { + TraceListener.Invoke(format); + } + } + + public static void WriteLine(TraceLevel level, String format, Object arg1) { + if ( (level & TraceLevel) > 0) { + TraceListener.Invoke(format, arg1); + } + } + + public static void WriteLine(TraceLevel level, String format, Object arg1, Object arg2) { + if ((level & TraceLevel) > 0) { + TraceListener.Invoke(format, arg1, arg2); + } + } + + public static void WriteLine(TraceLevel level, String format, Object arg1, Object arg2, Object arg3) { + if ((level & TraceLevel) > 0) { + TraceListener.Invoke(format, arg1, arg2, arg3); + } + } + } } \ No newline at end of file diff --git a/M2Mqtt/WinRT/Fx.cs b/M2Mqtt/WinRT/Fx.cs index 6d62d28..b853cd6 100644 --- a/M2Mqtt/WinRT/Fx.cs +++ b/M2Mqtt/WinRT/Fx.cs @@ -13,7 +13,7 @@ and the Eclipse Distribution License is available at Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ - +#if !NETCOREAPP using System.Threading; using System.Threading.Tasks; @@ -33,4 +33,5 @@ namespace uPLibrary.Networking.M2Mqtt public static void SleepThread(int millisecondsTimeout) { Task.Delay(millisecondsTimeout).RunSynchronously(); } } -} +} +#endif \ No newline at end of file diff --git a/M2Mqtt/WinRT/Hashtable.cs b/M2Mqtt/WinRT/Hashtable.cs index a42532b..2eeeebd 100644 --- a/M2Mqtt/WinRT/Hashtable.cs +++ b/M2Mqtt/WinRT/Hashtable.cs @@ -13,7 +13,7 @@ and the Eclipse Distribution License is available at Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ - +#if !NETCOREAPP using System.Collections.Generic; namespace uPLibrary.Networking.M2Mqtt @@ -25,3 +25,4 @@ namespace uPLibrary.Networking.M2Mqtt { } } +#endif \ No newline at end of file diff --git a/M2Mqtt/WinRT/MqttNetworkChannel.cs b/M2Mqtt/WinRT/MqttNetworkChannel.cs index 305992c..4028f07 100644 --- a/M2Mqtt/WinRT/MqttNetworkChannel.cs +++ b/M2Mqtt/WinRT/MqttNetworkChannel.cs @@ -13,7 +13,7 @@ and the Eclipse Distribution License is available at Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ - +#if !NETCOREAPP using System; using System.Collections.Generic; using System.Linq; @@ -177,3 +177,4 @@ namespace uPLibrary.Networking.M2Mqtt } } } +#endif \ No newline at end of file diff --git a/M2Mqtt/WinRT/Queue.cs b/M2Mqtt/WinRT/Queue.cs index bc6728f..3492a52 100644 --- a/M2Mqtt/WinRT/Queue.cs +++ b/M2Mqtt/WinRT/Queue.cs @@ -13,7 +13,7 @@ and the Eclipse Distribution License is available at Contributors: Paolo Patierno - initial API and implementation and/or initial documentation */ - +#if !NETCOREAPP using System.Collections.Generic; namespace uPLibrary.Networking.M2Mqtt @@ -25,3 +25,4 @@ namespace uPLibrary.Networking.M2Mqtt { } } +#endif \ No newline at end of file diff --git a/M2Mqtt_Core.sln b/M2Mqtt_Core.sln new file mode 100644 index 0000000..dd105ab --- /dev/null +++ b/M2Mqtt_Core.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29519.87 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt", "M2Mqtt\M2Mqtt_Core.csproj", "{18F706D2-7610-428C-8324-8327F9D7BE90}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {18F706D2-7610-428C-8324-8327F9D7BE90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18F706D2-7610-428C-8324-8327F9D7BE90}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18F706D2-7610-428C-8324-8327F9D7BE90}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18F706D2-7610-428C-8324-8327F9D7BE90}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {EBFDF320-2C59-4F89-8211-5B6EF9C310ED} + EndGlobalSection +EndGlobal