From ede25d06ae16a1c48742bffd8790d9f3d4853fb0 Mon Sep 17 00:00:00 2001 From: BlubbFish Date: Sat, 31 Mar 2018 20:42:28 +0000 Subject: [PATCH] [DW] Flex4Grid deleted [NF] BosMon Mqtt Plugin created --- M2Mqtt.sln | 25 + M2Mqtt/Exceptions/MqttClientException.cs | 105 + .../Exceptions/MqttCommunicationException.cs | 44 + M2Mqtt/Exceptions/MqttConnectionException.cs | 33 + M2Mqtt/Exceptions/MqttTimeoutException.cs | 29 + M2Mqtt/IMqttNetworkChannel.cs | 58 + M2Mqtt/M2Mqtt.csproj | 82 + M2Mqtt/M2MqttMono.csproj | 71 + M2Mqtt/M2MqttNetCf35.csproj | 106 + M2Mqtt/M2MqttNetCf39.csproj | 87 + M2Mqtt/Messages/MqttMsgBase.cs | 176 ++ M2Mqtt/Messages/MqttMsgConnack.cs | 137 ++ M2Mqtt/Messages/MqttMsgConnect.cs | 509 +++++ M2Mqtt/Messages/MqttMsgConnectEventArgs.cs | 46 + M2Mqtt/Messages/MqttMsgContext.cs | 141 ++ M2Mqtt/Messages/MqttMsgDisconnect.cs | 64 + M2Mqtt/Messages/MqttMsgPingReq.cs | 64 + M2Mqtt/Messages/MqttMsgPingResp.cs | 65 + M2Mqtt/Messages/MqttMsgPuback.cs | 119 + M2Mqtt/Messages/MqttMsgPubcomp.cs | 118 + M2Mqtt/Messages/MqttMsgPublish.cs | 270 +++ M2Mqtt/Messages/MqttMsgPublishEventArgs.cs | 113 + M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs | 57 + M2Mqtt/Messages/MqttMsgPubrec.cs | 118 + M2Mqtt/Messages/MqttMsgPubrel.cs | 128 ++ M2Mqtt/Messages/MqttMsgSuback.cs | 153 ++ M2Mqtt/Messages/MqttMsgSubscribe.cs | 255 +++ M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs | 83 + M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs | 70 + M2Mqtt/Messages/MqttMsgUnsuback.cs | 122 + M2Mqtt/Messages/MqttMsgUnsubscribe.cs | 222 ++ .../Messages/MqttMsgUnsubscribeEventArgs.cs | 70 + .../Messages/MqttMsgUnsubscribedEventArgs.cs | 57 + M2Mqtt/MqttClient.cs | 1999 +++++++++++++++++ M2Mqtt/MqttNetworkChannel.cs | 272 +++ M2Mqtt/MqttSettings.cs | 98 + M2Mqtt/Properties/AssemblyInfo.cs | 46 + M2Mqtt/Utility/QueueExtension.cs | 52 + M2Mqtt/bin/Release/M2Mqtt.dll | Bin 0 -> 38912 bytes M2Mqtt/uM2MqttNetMf42.csproj | 75 + M2Mqtt/uM2MqttNetMf43.csproj | 75 + 41 files changed, 6414 insertions(+) create mode 100644 M2Mqtt.sln create mode 100644 M2Mqtt/Exceptions/MqttClientException.cs create mode 100644 M2Mqtt/Exceptions/MqttCommunicationException.cs create mode 100644 M2Mqtt/Exceptions/MqttConnectionException.cs create mode 100644 M2Mqtt/Exceptions/MqttTimeoutException.cs create mode 100644 M2Mqtt/IMqttNetworkChannel.cs create mode 100644 M2Mqtt/M2Mqtt.csproj create mode 100644 M2Mqtt/M2MqttMono.csproj create mode 100644 M2Mqtt/M2MqttNetCf35.csproj create mode 100644 M2Mqtt/M2MqttNetCf39.csproj create mode 100644 M2Mqtt/Messages/MqttMsgBase.cs create mode 100644 M2Mqtt/Messages/MqttMsgConnack.cs create mode 100644 M2Mqtt/Messages/MqttMsgConnect.cs create mode 100644 M2Mqtt/Messages/MqttMsgConnectEventArgs.cs create mode 100644 M2Mqtt/Messages/MqttMsgContext.cs create mode 100644 M2Mqtt/Messages/MqttMsgDisconnect.cs create mode 100644 M2Mqtt/Messages/MqttMsgPingReq.cs create mode 100644 M2Mqtt/Messages/MqttMsgPingResp.cs create mode 100644 M2Mqtt/Messages/MqttMsgPuback.cs create mode 100644 M2Mqtt/Messages/MqttMsgPubcomp.cs create mode 100644 M2Mqtt/Messages/MqttMsgPublish.cs create mode 100644 M2Mqtt/Messages/MqttMsgPublishEventArgs.cs create mode 100644 M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs create mode 100644 M2Mqtt/Messages/MqttMsgPubrec.cs create mode 100644 M2Mqtt/Messages/MqttMsgPubrel.cs create mode 100644 M2Mqtt/Messages/MqttMsgSuback.cs create mode 100644 M2Mqtt/Messages/MqttMsgSubscribe.cs create mode 100644 M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs create mode 100644 M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs create mode 100644 M2Mqtt/Messages/MqttMsgUnsuback.cs create mode 100644 M2Mqtt/Messages/MqttMsgUnsubscribe.cs create mode 100644 M2Mqtt/Messages/MqttMsgUnsubscribeEventArgs.cs create mode 100644 M2Mqtt/Messages/MqttMsgUnsubscribedEventArgs.cs create mode 100644 M2Mqtt/MqttClient.cs create mode 100644 M2Mqtt/MqttNetworkChannel.cs create mode 100644 M2Mqtt/MqttSettings.cs create mode 100644 M2Mqtt/Properties/AssemblyInfo.cs create mode 100644 M2Mqtt/Utility/QueueExtension.cs create mode 100644 M2Mqtt/bin/Release/M2Mqtt.dll create mode 100644 M2Mqtt/uM2MqttNetMf42.csproj create mode 100644 M2Mqtt/uM2MqttNetMf43.csproj diff --git a/M2Mqtt.sln b/M2Mqtt.sln new file mode 100644 index 0000000..cd45efc --- /dev/null +++ b/M2Mqtt.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2010 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt", "M2Mqtt\M2Mqtt.csproj", "{A11AEF5A-B246-4FE8-8330-06DB73CC8074}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E4CE8710-7B51-439F-A427-9AEFFE71E682} + EndGlobalSection +EndGlobal diff --git a/M2Mqtt/Exceptions/MqttClientException.cs b/M2Mqtt/Exceptions/MqttClientException.cs new file mode 100644 index 0000000..bd58745 --- /dev/null +++ b/M2Mqtt/Exceptions/MqttClientException.cs @@ -0,0 +1,105 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; + +namespace uPLibrary.Networking.M2Mqtt.Exceptions +{ + /// + /// MQTT client exception + /// + public class MqttClientException : ApplicationException + { + /// + /// 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; } + } + } + + /// + /// MQTT client erroro code + /// + public enum MqttClientErrorCode + { + /// + /// Will topic length error + /// + WillTopicWrong = 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 + } +} diff --git a/M2Mqtt/Exceptions/MqttCommunicationException.cs b/M2Mqtt/Exceptions/MqttCommunicationException.cs new file mode 100644 index 0000000..9d2591c --- /dev/null +++ b/M2Mqtt/Exceptions/MqttCommunicationException.cs @@ -0,0 +1,44 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; + +namespace uPLibrary.Networking.M2Mqtt.Exceptions +{ + /// + /// Exception due to error communication with broker on socket + /// + public class MqttCommunicationException : ApplicationException + { + /// + /// Default constructor + /// + 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 new file mode 100644 index 0000000..9d178c2 --- /dev/null +++ b/M2Mqtt/Exceptions/MqttConnectionException.cs @@ -0,0 +1,33 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; + +namespace uPLibrary.Networking.M2Mqtt.Exceptions +{ + /// + /// Connection to the broker exception + /// + public class MqttConnectionException : ApplicationException + { + public MqttConnectionException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/M2Mqtt/Exceptions/MqttTimeoutException.cs b/M2Mqtt/Exceptions/MqttTimeoutException.cs new file mode 100644 index 0000000..a5b825e --- /dev/null +++ b/M2Mqtt/Exceptions/MqttTimeoutException.cs @@ -0,0 +1,29 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; + +namespace uPLibrary.Networking.M2Mqtt.Exceptions +{ + /// + /// Timeout on receiving from broker exception + /// + public class MqttTimeoutException : ApplicationException + { + } +} diff --git a/M2Mqtt/IMqttNetworkChannel.cs b/M2Mqtt/IMqttNetworkChannel.cs new file mode 100644 index 0000000..bf01880 --- /dev/null +++ b/M2Mqtt/IMqttNetworkChannel.cs @@ -0,0 +1,58 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; +using System.Text; + +namespace uPLibrary.Networking.M2Mqtt +{ + /// + /// Interface for channel under MQTT library + /// + 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); + + /// + /// 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(); + } +} diff --git a/M2Mqtt/M2Mqtt.csproj b/M2Mqtt/M2Mqtt.csproj new file mode 100644 index 0000000..5e1ab26 --- /dev/null +++ b/M2Mqtt/M2Mqtt.csproj @@ -0,0 +1,82 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {A11AEF5A-B246-4FE8-8330-06DB73CC8074} + Library + Properties + uPLibrary.Networking.M2Mqtt + M2Mqtt + v2.0 + 512 + + + + true + full + false + bin\Debug\ + TRACE;DEBUG;SSL + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;SSL + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/M2Mqtt/M2MqttMono.csproj b/M2Mqtt/M2MqttMono.csproj new file mode 100644 index 0000000..da48297 --- /dev/null +++ b/M2Mqtt/M2MqttMono.csproj @@ -0,0 +1,71 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {9B706DEC-4CE7-4764-BDBE-8A5F855E5857} + Library + uPLibrary.Networking.M2Mqtt + M2Mqtt + + + true + full + false + bin\Debug\Mono + DEBUG;SSL + prompt + 4 + false + + + none + false + bin\Release\Mono + prompt + 4 + false + SSL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/M2Mqtt/M2MqttNetCf35.csproj b/M2Mqtt/M2MqttNetCf35.csproj new file mode 100644 index 0000000..bf64adf --- /dev/null +++ b/M2Mqtt/M2MqttNetCf35.csproj @@ -0,0 +1,106 @@ + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {194FEA1B-E67F-4FC0-AC47-CD71F7F060CC} + Library + Properties + uPLibrary.Networking.M2Mqtt + M2Mqtt + {4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + WindowsCE + E2BECB1F-8C8C-41ba-B736-9BE7D946A398 + 5.0 + M2MqttNetCf35 + v3.5 + Windows CE + + + + + true + full + false + bin\Debug\NetCf35\ + TRACE;DEBUG;WindowsCE,COMPACT_FRAMEWORK + true + true + prompt + 512 + 4 + Off + + + pdbonly + true + bin\Release\NetCf35\ + TRACE;WindowsCE,COMPACT_FRAMEWORK + true + true + prompt + 512 + 4 + Off + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/M2Mqtt/M2MqttNetCf39.csproj b/M2Mqtt/M2MqttNetCf39.csproj new file mode 100644 index 0000000..0ca164d --- /dev/null +++ b/M2Mqtt/M2MqttNetCf39.csproj @@ -0,0 +1,87 @@ + + + + + Debug + AnyCPU + {BB9B7FF4-6502-41AF-8851-5060B67645E8} + {6AFDAB0D-95EF-424D-8A49-099ECD40B0FF};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + uPLibrary.Networking.M2Mqtt + M2Mqtt + WindowsEmbeddedCompact + v3.9 + v8.0 + 512 + + + true + full + false + bin\Debug\NetCf39\ + TRACE;DEBUG;COMPACT_FRAMEWORK + prompt + 4 + ManagedMinimumRules.ruleset + + + pdbonly + true + bin\Release\NetCf39\ + TRACE;COMPACT_FRAMEWORK + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/M2Mqtt/Messages/MqttMsgBase.cs b/M2Mqtt/Messages/MqttMsgBase.cs new file mode 100644 index 0000000..f8459e0 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgBase.cs @@ -0,0 +1,176 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +#if SSL && !WINDOWS_PHONE +#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3) +using Microsoft.SPOT.Net.Security; +#else +using System.Net.Security; +#endif +#endif +using System; + +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 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; + + // 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; + + 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; } + } + + #endregion + + // message type + protected byte type; + // duplicate delivery + protected bool dupFlag; + // quality of service level + protected byte qosLevel; + // retain flag + protected bool retain; + + /// + /// Returns message bytes rapresentation + /// + /// Bytes rapresentation + public abstract byte[] GetBytes(); + + /// + /// 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgConnack.cs b/M2Mqtt/Messages/MqttMsgConnack.cs new file mode 100644 index 0000000..d6ca107 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgConnack.cs @@ -0,0 +1,137 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; + +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; + private const byte CONN_RETURN_CODE_BYTE_OFFSET = 1; + private const byte CONN_RETURN_CODE_BYTE_SIZE = 1; + + #endregion + + #region Properties... + + /// + /// Return Code + /// + public byte ReturnCode + { + get { return this.returnCode; } + set { this.returnCode = value; } + } + + #endregion + + // 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 + /// Channel connected to the broker + /// CONNACK message instance + public static MqttMsgConnack Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel) + { + byte[] buffer; + MqttMsgConnack msg = new MqttMsgConnack(); + + // get remaining length and allocate buffer + int remainingLength = MqttMsgBase.decodeRemainingLength(channel); + buffer = new byte[remainingLength]; + + // read bytes from socket... + channel.Receive(buffer); + // ...and set return code from broker + msg.returnCode = buffer[CONN_RETURN_CODE_BYTE_OFFSET]; + + return msg; + } + + public override byte[] GetBytes() + { + int fixedHeaderSize = 0; + int varHeaderSize = 0; + int payloadSize = 0; + int remainingLength = 0; + byte[] buffer; + int index = 0; + + // 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 + buffer[index] = (byte)(MQTT_MSG_CONNACK_TYPE << MSG_TYPE_OFFSET); + index++; + + // encode remaining length + index = this.encodeRemainingLength(remainingLength, buffer, index); + + // topic name compression response (reserved values. not used); + buffer[index++] = 0x00; + + // connect return code + buffer[index++] = this.returnCode; + + return buffer; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgConnect.cs b/M2Mqtt/Messages/MqttMsgConnect.cs new file mode 100644 index 0000000..67e0835 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgConnect.cs @@ -0,0 +1,509 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; +using System.Text; +using uPLibrary.Networking.M2Mqtt.Exceptions; + +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 = "MQIsdp"; + + // max length for client id + internal const int CLIENT_ID_MAX_LENGTH = 23; + + // variable header fields + internal const byte PROTOCOL_NAME_LEN_SIZE = 2; + internal const byte PROTOCOL_NAME_SIZE = 6; + internal const byte PROTOCOL_VERSION_NUMBER_SIZE = 1; + internal const byte CONNECT_FLAGS_SIZE = 1; + internal const byte KEEP_ALIVE_TIME_SIZE = 2; + + internal const byte PROTOCOL_VERSION = 0x03; + 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; + + #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) + { + } + + /// + /// Constructor + /// + /// Client identifier + /// Username + /// Password + /// Will retain flag + /// Will QOS level + /// Will flag + /// Will topic + /// Will message + /// Clean sessione flag + /// Keep alive period + public MqttMsgConnect(string clientId, + string username, + string password, + bool willRetain, + byte willQosLevel, + bool willFlag, + string willTopic, + string willMessage, + bool cleanSession, + ushort keepAlivePeriod + ) + { + 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; + } + + /// + /// Parse bytes for a CONNECT message + /// + /// First fixed header byte + /// Channel connected to the broker + /// CONNECT message instance + public static MqttMsgConnect Parse(byte fixedHeaderFirstByte, 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)); + + // protocol version + msg.protocolVersion = buffer[index]; + index += PROTOCOL_VERSION_NUMBER_SIZE; + + // connect flags + 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 + 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)); + + // 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() + { + 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.willTopic != null) ? Encoding.UTF8.GetBytes(this.willTopic) : null; + byte[] willMessageUtf8 = (this.willMessage != null) ? Encoding.UTF8.GetBytes(this.willMessage) : null; + byte[] usernameUtf8 = (this.username != null) ? Encoding.UTF8.GetBytes(this.username) : null; + byte[] passwordUtf8 = (this.password != null) ? Encoding.UTF8.GetBytes(this.password) : null; + + // will flag set but will topic wrong + if (this.willFlag && (willTopicUtf8.Length == 0)) + throw new MqttClientException(MqttClientErrorCode.WillTopicWrong); + if (this.keepAlivePeriod > MAX_KEEP_ALIVE) + throw new MqttClientException(MqttClientErrorCode.KeepAliveWrong); + + // protocol name field size + varHeaderSize += (PROTOCOL_NAME_LEN_SIZE + PROTOCOL_NAME_SIZE); + // protocol version number field size + varHeaderSize += PROTOCOL_VERSION_NUMBER_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); + + // encode remaining length + index = this.encodeRemainingLength(remainingLength, buffer, index); + + // protocol name + buffer[index++] = 0; // MSB protocol name size + buffer[index++] = PROTOCOL_NAME_SIZE; // LSB protocol name size + buffer[index++] = (byte)'M'; + buffer[index++] = (byte)'Q'; + buffer[index++] = (byte)'I'; + buffer[index++] = (byte)'s'; + buffer[index++] = (byte)'d'; + buffer[index++] = (byte)'p'; + + // protocol version + buffer[index++] = PROTOCOL_VERSION; + + // connect flags + byte connectFlags = 0x00; + connectFlags |= (this.username != null) ? (byte)(1 << USERNAME_FLAG_OFFSET) : (byte)0x00; + connectFlags |= (this.password != null) ? (byte)(1 << PASSWORD_FLAG_OFFSET) : (byte)0x00; + connectFlags |= (this.willRetain) ? (byte)(1 << WILL_RETAIN_FLAG_OFFSET) : (byte)0x00; + 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 && (this.willTopic != 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 && (this.willMessage != 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 (this.username != 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 (this.password != 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgConnectEventArgs.cs b/M2Mqtt/Messages/MqttMsgConnectEventArgs.cs new file mode 100644 index 0000000..6c901e1 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgConnectEventArgs.cs @@ -0,0 +1,46 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +using System; +#else +using Microsoft.SPOT; +#endif + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Event Args class for CONNECT 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgContext.cs b/M2Mqtt/Messages/MqttMsgContext.cs new file mode 100644 index 0000000..204703f --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgContext.cs @@ -0,0 +1,141 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; +using System.Text; + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Context for 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; } + } + + /// + /// 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, + + /// + /// (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 new file mode 100644 index 0000000..8c4eb7e --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgDisconnect.cs @@ -0,0 +1,64 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Class for DISCONNECT message from client to broker + /// + public class MqttMsgDisconnect : MqttMsgBase + { + /// + /// Constructor + /// + public MqttMsgDisconnect() + { + this.type = MQTT_MSG_DISCONNECT_TYPE; + } + + /// + /// Parse bytes for a DISCONNECT message + /// + /// First fixed header byte + /// Channel connected to the broker + /// DISCONNECT message instance + public static MqttMsgDisconnect Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel) + { + MqttMsgDisconnect msg = new MqttMsgDisconnect(); + + // get remaining length and allocate buffer + int remainingLength = MqttMsgBase.decodeRemainingLength(channel); + // NOTE : remainingLength must be 0 + + return msg; + } + + public override byte[] GetBytes() + { + byte[] buffer = new byte[2]; + int index = 0; + + // first fixed header byte + buffer[index++] = (MQTT_MSG_DISCONNECT_TYPE << MSG_TYPE_OFFSET); + buffer[index++] = 0x00; + + return buffer; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgPingReq.cs b/M2Mqtt/Messages/MqttMsgPingReq.cs new file mode 100644 index 0000000..e8f4efb --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgPingReq.cs @@ -0,0 +1,64 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Class for PINGREQ message from client to broker + /// + public class MqttMsgPingReq : MqttMsgBase + { + /// + /// Constructor + /// + public MqttMsgPingReq() + { + this.type = MQTT_MSG_PINGREQ_TYPE; + } + + public override byte[] GetBytes() + { + byte[] buffer = new byte[2]; + int index = 0; + + // first fixed header byte + buffer[index++] = (MQTT_MSG_PINGREQ_TYPE << MSG_TYPE_OFFSET); + buffer[index++] = 0x00; + + return buffer; + } + + /// + /// Parse bytes for a PINGREQ message + /// + /// First fixed header byte + /// Channel connected to the broker + /// PINGREQ message instance + public static MqttMsgPingReq Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel) + { + MqttMsgPingReq msg = new MqttMsgPingReq(); + + // 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgPingResp.cs b/M2Mqtt/Messages/MqttMsgPingResp.cs new file mode 100644 index 0000000..988edcf --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgPingResp.cs @@ -0,0 +1,65 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Class for PINGRESP message from client to broker + /// + public class MqttMsgPingResp : MqttMsgBase + { + /// + /// Constructor + /// + public MqttMsgPingResp() + { + this.type = MQTT_MSG_PINGRESP_TYPE; + } + + /// + /// Parse bytes for a PINGRESP message + /// + /// First fixed header byte + /// Channel connected to the broker + /// PINGRESP message instance + public static MqttMsgPingResp Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel) + { + MqttMsgPingResp msg = new MqttMsgPingResp(); + + // 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[] buffer = new byte[2]; + int index = 0; + + // first fixed header byte + buffer[index++] = (MQTT_MSG_PINGRESP_TYPE << MSG_TYPE_OFFSET); + buffer[index++] = 0x00; + + return buffer; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgPuback.cs b/M2Mqtt/Messages/MqttMsgPuback.cs new file mode 100644 index 0000000..646746c --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgPuback.cs @@ -0,0 +1,119 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Class for PUBACK message from broker to client + /// + public class MqttMsgPuback : MqttMsgBase + { + #region Properties... + + /// + /// Message identifier for the publish message + /// that is acknowledged + /// + public ushort MessageId + { + get { return this.messageId; } + set { this.messageId = value; } + } + + #endregion + + // message identifier + private ushort messageId; + + /// + /// Constructor + /// + public MqttMsgPuback() + { + this.type = MQTT_MSG_PUBACK_TYPE; + } + + public override byte[] GetBytes() + { + 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 + 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 + /// Channel connected to the broker + /// PUBACK message instance + public static MqttMsgPuback Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel) + { + byte[] buffer; + int index = 0; + MqttMsgPuback msg = new MqttMsgPuback(); + + // 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgPubcomp.cs b/M2Mqtt/Messages/MqttMsgPubcomp.cs new file mode 100644 index 0000000..ca53667 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgPubcomp.cs @@ -0,0 +1,118 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Class for PUBCOMP message from broker to client + /// + public class MqttMsgPubcomp : MqttMsgBase + { + #region Properties... + + /// + /// Message identifier for the acknowledged publish message + /// + public ushort MessageId + { + get { return this.messageId; } + set { this.messageId = value; } + } + + #endregion + + // message identifier + private ushort messageId; + + /// + /// Constructor + /// + public MqttMsgPubcomp() + { + this.type = MQTT_MSG_PUBCOMP_TYPE; + } + + public override byte[] GetBytes() + { + 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 + 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 + /// Channel connected to the broker + /// PUBCOMP message instance + public static MqttMsgPubcomp Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel) + { + byte[] buffer; + int index = 0; + MqttMsgPubcomp msg = new MqttMsgPubcomp(); + + // 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgPublish.cs b/M2Mqtt/Messages/MqttMsgPublish.cs new file mode 100644 index 0000000..8ee80b5 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgPublish.cs @@ -0,0 +1,270 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; +using System.Text; +using uPLibrary.Networking.M2Mqtt.Exceptions; + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Class for PUBLISH message from client to broker + /// + public class MqttMsgPublish : MqttMsgBase + { + #region Properties... + + /// + /// Message topic + /// + public string Topic + { + get { return this.topic; } + set { this.topic = value; } + } + + /// + /// Message data + /// + public byte[] Message + { + get { return this.message; } + set { this.message = value; } + } + + /// + /// Message identifier + /// + public ushort MessageId + { + get { return this.messageId; } + set { this.messageId = value; } + } + + #endregion + + // message topic + private string topic; + // message data + private byte[] message; + // message identifier + ushort messageId; + + /// + /// Constructor + /// + public MqttMsgPublish() + { + this.type = MQTT_MSG_PUBLISH_TYPE; + } + + /// + /// Constructor + /// + /// Message topic + /// Message data + public MqttMsgPublish(string topic, byte[] message) : + this(topic, message, false, QOS_LEVEL_AT_MOST_ONCE, false) + { + } + + /// + /// Constructor + /// + /// Message topic + /// Message data + /// Duplicate flag + /// Quality of Service level + /// Retain flag + public MqttMsgPublish(string topic, + byte[] message, + bool dupFlag, + byte qosLevel, + bool retain) : base() + { + this.type = MQTT_MSG_PUBLISH_TYPE; + + this.topic = topic; + this.message = message; + this.dupFlag = dupFlag; + this.qosLevel = qosLevel; + this.retain = retain; + this.messageId = 0; + } + + public override byte[] GetBytes() + { + int fixedHeaderSize = 0; + int varHeaderSize = 0; + int payloadSize = 0; + int remainingLength = 0; + byte[] buffer; + int index = 0; + + // topic can't contain wildcards + 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)) + throw new MqttClientException(MqttClientErrorCode.TopicLength); + + byte[] topicUtf8 = Encoding.UTF8.GetBytes(this.topic); + + // topic name + 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)) + { + varHeaderSize += MESSAGE_ID_SIZE; + } + + // check on message with zero length + if (this.message != null) + // message data + payloadSize += this.message.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 + 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); + + // topic name + buffer[index++] = (byte)((topicUtf8.Length >> 8) & 0x00FF); // MSB + buffer[index++] = (byte)(topicUtf8.Length & 0x00FF); // LSB + Array.Copy(topicUtf8, 0, buffer, index, topicUtf8.Length); + 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)) + { + // 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 + } + + // 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; + } + + return buffer; + } + + /// + /// Parse bytes for a PUBLISH message + /// + /// First fixed header byte + /// Channel connected to the broker + /// PUBLISH message instance + public static MqttMsgPublish Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel) + { + byte[] buffer; + int index = 0; + byte[] topicUtf8; + int topicUtf8Length; + MqttMsgPublish msg = new MqttMsgPublish(); + + // get remaining length and allocate buffer + int remainingLength = MqttMsgBase.decodeRemainingLength(channel); + buffer = new byte[remainingLength]; + + // read bytes from socket... + int received = channel.Receive(buffer); + + // topic name + topicUtf8Length = ((buffer[index++] << 8) & 0xFF00); + topicUtf8Length |= buffer[index++]; + topicUtf8 = new byte[topicUtf8Length]; + Array.Copy(buffer, index, topicUtf8, 0, topicUtf8Length); + index += topicUtf8Length; + msg.topic = new String(Encoding.UTF8.GetChars(topicUtf8)); + + // 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); + // read retain flag from fixed header + 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)) + { + // message id + msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00); + msg.messageId |= (buffer[index++]); + } + + // get payload with message data + int messageSize = remainingLength - index; + int remaining = messageSize; + int messageOffset = 0; + msg.message = new byte[messageSize]; + + // BUG FIX 26/07/2013 : receiving large payload + + // copy first part of payload data received + Array.Copy(buffer, index, msg.message, messageOffset, received - index); + remaining -= (received - index); + messageOffset += (received - index); + + // if payload isn't finished + while (remaining > 0) + { + // receive other payload data + received = channel.Receive(buffer); + Array.Copy(buffer, 0, msg.message, messageOffset, received); + remaining -= received; + messageOffset += received; + } + + return msg; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgPublishEventArgs.cs b/M2Mqtt/Messages/MqttMsgPublishEventArgs.cs new file mode 100644 index 0000000..5954d91 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgPublishEventArgs.cs @@ -0,0 +1,113 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +using System; +#else +using Microsoft.SPOT; +#endif + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Event Args class for PUBLISH message received from broker + /// + 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs b/M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs new file mode 100644 index 0000000..811d946 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs @@ -0,0 +1,57 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +using System; +#else +using Microsoft.SPOT; +#endif + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Event Args class for published message + /// + public class MqttMsgPublishedEventArgs : 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 published + public MqttMsgPublishedEventArgs(ushort messageId) + { + this.messageId = messageId; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgPubrec.cs b/M2Mqtt/Messages/MqttMsgPubrec.cs new file mode 100644 index 0000000..05ce84a --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgPubrec.cs @@ -0,0 +1,118 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Class for PUBREC message from broker to client + /// + public class MqttMsgPubrec : MqttMsgBase + { + #region Properties... + + /// + /// Message identifier for the acknowledged publish message + /// + public ushort MessageId + { + get { return this.messageId; } + set { this.messageId = value; } + } + + #endregion + + // message identifier + private ushort messageId; + + /// + /// Constructor + /// + public MqttMsgPubrec() + { + this.type = MQTT_MSG_PUBREC_TYPE; + } + + public override byte[] GetBytes() + { + 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 + 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 + /// Channel connected to the broker + /// PUBREC message instance + public static MqttMsgPubrec Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel) + { + byte[] buffer; + int index = 0; + MqttMsgPubrec msg = new MqttMsgPubrec(); + + // 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgPubrel.cs b/M2Mqtt/Messages/MqttMsgPubrel.cs new file mode 100644 index 0000000..25c10a2 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgPubrel.cs @@ -0,0 +1,128 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Class for PUBREL message from client top broker + /// + public class MqttMsgPubrel : MqttMsgBase + { + #region Properties... + + /// + /// Message identifier for the acknowledged publish message + /// + public ushort MessageId + { + get { return this.messageId; } + set { this.messageId = value; } + } + + #endregion + + // message identifier + private ushort messageId; + + /// + /// Constructor + /// + public MqttMsgPubrel() + { + this.type = MQTT_MSG_PUBREL_TYPE; + // PUBREL message use QoS Level 1 + this.qosLevel = QOS_LEVEL_AT_LEAST_ONCE; + } + + public override byte[] GetBytes() + { + 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 + 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 + /// Channel connected to the broker + /// PUBREL message instance + public static MqttMsgPubrel Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel) + { + byte[] buffer; + int index = 0; + MqttMsgPubrel msg = new MqttMsgPubrel(); + + // get remaining length and allocate buffer + int remainingLength = MqttMsgBase.decodeRemainingLength(channel); + buffer = new byte[remainingLength]; + + // read bytes from socket... + channel.Receive(buffer); + + // 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgSuback.cs b/M2Mqtt/Messages/MqttMsgSuback.cs new file mode 100644 index 0000000..5b847ff --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgSuback.cs @@ -0,0 +1,153 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Class for SUBACK message from broker to client + /// + public class MqttMsgSuback : MqttMsgBase + { + #region Properties... + + /// + /// Message identifier for the subscribe message + /// that is acknowledged + /// + public ushort MessageId + { + get { return this.messageId; } + set { this.messageId = value; } + } + + /// + /// List of granted QOS Levels + /// + public byte[] GrantedQoSLevels + { + get { return this.grantedQosLevels; } + set { this.grantedQosLevels = value; } + } + + #endregion + + // message identifier + private ushort messageId; + // granted QOS levels + byte[] grantedQosLevels; + + /// + /// Constructor + /// + public MqttMsgSuback() + { + this.type = MQTT_MSG_SUBACK_TYPE; + } + + /// + /// Parse bytes for a SUBACK message + /// + /// First fixed header byte + /// Channel connected to the broker + /// SUBACK message instance + public static MqttMsgSuback Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel) + { + byte[] buffer; + int index = 0; + MqttMsgSuback msg = new MqttMsgSuback(); + + // 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() + { + 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 + buffer[index] = (byte)(MQTT_MSG_SUBACK_TYPE << MSG_TYPE_OFFSET); + index++; + + // 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgSubscribe.cs b/M2Mqtt/Messages/MqttMsgSubscribe.cs new file mode 100644 index 0000000..478ee2e --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgSubscribe.cs @@ -0,0 +1,255 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; +// if NOT .Net Micro Framework +#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 +{ + /// + /// Class for SUBSCRIBE message from client to broker + /// + 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; } + } + + /// + /// Message identifier + /// + public ushort MessageId + { + get { return this.messageId; } + set { this.messageId = value; } + } + + #endregion + + // topics to subscribe + string[] topics; + // QOS levels related to topics + byte[] qosLevels; + // message identifier + ushort messageId; + + /// + /// 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 + this.qosLevel = QOS_LEVEL_AT_LEAST_ONCE; + } + + /// + /// Parse bytes for a SUBSCRIBE message + /// + /// First fixed header byte + /// Channel connected to the broker + /// SUBSCRIBE message instance + public static MqttMsgSubscribe Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel) + { + byte[] buffer; + int index = 0; + byte[] topicUtf8; + int topicUtf8Length; + MqttMsgSubscribe msg = new MqttMsgSubscribe(); + + // get remaining length and allocate buffer + int remainingLength = MqttMsgBase.decodeRemainingLength(channel); + buffer = new byte[remainingLength]; + + // read bytes from socket... + int received = channel.Receive(buffer); + + // 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) + 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(); +#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() + { + 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 + 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs b/M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs new file mode 100644 index 0000000..92a5824 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs @@ -0,0 +1,83 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +using System; +#else +using Microsoft.SPOT; +#endif + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Event Args class for subscribe request on topics + /// + 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs b/M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs new file mode 100644 index 0000000..3b6daab --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs @@ -0,0 +1,70 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +using System; +#else +using Microsoft.SPOT; +#endif + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Event Args class for subscribed topics + /// + 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgUnsuback.cs b/M2Mqtt/Messages/MqttMsgUnsuback.cs new file mode 100644 index 0000000..7c1a810 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgUnsuback.cs @@ -0,0 +1,122 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Class for UNSUBACK message from broker to client + /// + public class MqttMsgUnsuback : MqttMsgBase + { + #region Properties... + + /// + /// Message identifier for the unsubscribe message + /// that is acknowledged + /// + public ushort MessageId + { + get { return this.messageId; } + set { this.messageId = value; } + } + + #endregion + + // message identifier + private ushort messageId; + + /// + /// Constructor + /// + public MqttMsgUnsuback() + { + this.type = MQTT_MSG_UNSUBACK_TYPE; + } + + /// + /// Parse bytes for a UNSUBACK message + /// + /// First fixed header byte + /// Channel connected to the broker + /// UNSUBACK message instance + public static MqttMsgUnsuback Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel) + { + byte[] buffer; + int index = 0; + MqttMsgUnsuback msg = new MqttMsgUnsuback(); + + // 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() + { + 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 + buffer[index] = (byte)(MQTT_MSG_UNSUBACK_TYPE << MSG_TYPE_OFFSET); + index++; + + // 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgUnsubscribe.cs b/M2Mqtt/Messages/MqttMsgUnsubscribe.cs new file mode 100644 index 0000000..b2f53c2 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgUnsubscribe.cs @@ -0,0 +1,222 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; +// if NOT .Net Micro Framework +#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 +{ + /// + /// Class for UNSUBSCRIBE message from client to broker + /// + public class MqttMsgUnsubscribe : MqttMsgBase + { + #region Properties... + + /// + /// List of topics to unsubscribe + /// + public string[] Topics + { + get { return this.topics; } + set { this.topics = value; } + } + + /// + /// Message identifier + /// + public ushort MessageId + { + get { return this.messageId; } + set { this.messageId = value; } + } + + #endregion + + // topics to unsubscribe + string[] topics; + // message identifier + ushort messageId; + + /// + /// 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 + this.qosLevel = QOS_LEVEL_AT_LEAST_ONCE; + } + + /// + /// Parse bytes for a UNSUBSCRIBE message + /// + /// First fixed header byte + /// Channel connected to the broker + /// UNSUBSCRIBE message instance + public static MqttMsgUnsubscribe Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel) + { + byte[] buffer; + int index = 0; + byte[] topicUtf8; + int topicUtf8Length; + MqttMsgUnsubscribe msg = new MqttMsgUnsubscribe(); + + // get remaining length and allocate buffer + int remainingLength = MqttMsgBase.decodeRemainingLength(channel); + buffer = new byte[remainingLength]; + + // read bytes from socket... + int received = channel.Receive(buffer); + + // 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) + IList tmpTopics = new ArrayList(); +// else other frameworks (.Net, .Net Compact, Mono, Windows Phone) +#else + 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() + { + 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 + 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgUnsubscribeEventArgs.cs b/M2Mqtt/Messages/MqttMsgUnsubscribeEventArgs.cs new file mode 100644 index 0000000..5f1dd74 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgUnsubscribeEventArgs.cs @@ -0,0 +1,70 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +using System; +#else +using Microsoft.SPOT; +#endif + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Event Args class for unsubscribe request on topics + /// + 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; + } + } +} diff --git a/M2Mqtt/Messages/MqttMsgUnsubscribedEventArgs.cs b/M2Mqtt/Messages/MqttMsgUnsubscribedEventArgs.cs new file mode 100644 index 0000000..99d8791 --- /dev/null +++ b/M2Mqtt/Messages/MqttMsgUnsubscribedEventArgs.cs @@ -0,0 +1,57 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3) +using System; +#else +using Microsoft.SPOT; +#endif + +namespace uPLibrary.Networking.M2Mqtt.Messages +{ + /// + /// Event Args class for unsubscribed topic + /// + 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; + } + } +} diff --git a/M2Mqtt/MqttClient.cs b/M2Mqtt/MqttClient.cs new file mode 100644 index 0000000..29a6696 --- /dev/null +++ b/M2Mqtt/MqttClient.cs @@ -0,0 +1,1999 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using uPLibrary.Networking.M2Mqtt.Exceptions; +using uPLibrary.Networking.M2Mqtt.Messages; +using System.Collections; +// if .Net Micro Framework +#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3) +using Microsoft.SPOT; +#if SSL +using Microsoft.SPOT.Net.Security; +#endif +// else other frameworks (.Net, .Net Compact, Mono, Windows Phone) +#else + +#if (SSL && !WINDOWS_PHONE) +using System.Security.Authentication; +using System.Net.Security; +#endif +#endif + +using System.Security.Cryptography.X509Certificates; + +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 = "ReceiveEventThread"; + private const string PROCESS_INFLIGHT_THREAD_NAME = "ProcessInflightThread"; + private const string KEEP_ALIVE_THREAD = "KeepAliveThread"; + + #endregion +#endif + + /// + /// Delagate that defines event handler for PUBLISH message received + /// + public delegate void MqttMsgPublishEventHandler(object sender, MqttMsgPublishEventArgs e); + + /// + /// Delegate that defines event handler for published message + /// + public delegate void MqttMsgPublishedEventHandler(object sender, MqttMsgPublishedEventArgs e); + + /// + /// Delagate that defines event handler for subscribed topic + /// + public delegate void MqttMsgSubscribedEventHandler(object sender, MqttMsgSubscribedEventArgs e); + + /// + /// Delagate that defines event handler for unsubscribed topic + /// + public delegate void MqttMsgUnsubscribedEventHandler(object sender, MqttMsgUnsubscribedEventArgs 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 UNSUBSCRIBE message received + /// + public delegate void MqttMsgUnsubscribeEventHandler(object sender, MqttMsgUnsubscribeEventArgs e); + + /// + /// Delagate that defines event handler for CONNECT message received + /// + public delegate void MqttMsgConnectEventHandler(object sender, MqttMsgConnectEventArgs e); +#endif + + /// + /// Delegate that defines event handler for client disconnection (DISCONNECT message or not) + /// + public delegate void MqttMsgDisconnectEventHandler(object sender, EventArgs e); + + // CA certificate + private X509Certificate caCert; + + // broker hostname, ip address and port + private string brokerHostName; + private IPAddress brokerIpAddress; + private int brokerPort; + // using SSL + private bool secure; + + // thread for receiving incoming message + private Thread receiveThread; + // thread for raising received message event + private Thread receiveEventThread; + private bool isRunning; + // event for raising received message event + private AutoResetEvent receiveEventWaitHandle; + + // thread for handling inflight messages queue asynchronously + private Thread processInflightThread; + // event for starting process inflight queue asynchronously + private AutoResetEvent inflightWaitHandle; + + // event for signaling synchronous receive + AutoResetEvent syncEndReceiving; + // message received + MqttMsgBase msgReceived; + + // exeption thrown during receiving + Exception exReceiving; + + // keep alive period (in ms) + private int keepAlivePeriod; + // thread for sending keep alive message + private Thread keepAliveThread; + private AutoResetEvent keepAliveEvent; + // keep alive timeout expired + private bool isKeepAliveTimeout; + // last communication time in ticks + private long lastCommTime; + + // 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; +#endif + // event for client disconnection (DISCONNECT message or not) + public event MqttMsgDisconnectEventHandler MqttMsgDisconnected; + + // channel to communicate over the network + private IMqttNetworkChannel channel; + + // inflight messages queue + private Queue inflightQueue; + // internal queue for received messages about inflight messages + private Queue internalQueue; + // receive queue for received messages + private Queue receiveQueue; + + // reference to avoid access to singleton via property + private MqttSettings settings; + + // current message identifier generated + private ushort messageIdCounter = 0; + + /// + /// Connection status between client and broker + /// + public bool IsConnected { get; private set; } + + /// + /// Client identifier + /// + public string ClientId { get; private set; } + + /// + /// Clean session flag + /// + public bool CleanSession { get; private set; } + + /// + /// Will flag + /// + public bool WillFlag { 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; } + + /// + /// Constructor + /// + /// Broker IP address + public MqttClient(IPAddress brokerIpAddress) : + this(brokerIpAddress, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, null) + { + } + + /// + /// Constructor + /// + /// Broker IP address + /// Broker port + /// Using secure connection + /// CA certificate for secure connection + public MqttClient(IPAddress brokerIpAddress, int brokerPort, bool secure, X509Certificate caCert) + { + this.Init(null, brokerIpAddress, brokerPort, secure, caCert); + } + + /// + /// Constructor + /// + /// Broker Host Name + public MqttClient(string brokerHostName) : + this(brokerHostName, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, null) + { + } + + /// + /// Constructor + /// + /// Broker Host Name + /// Broker port + /// Using secure connection + /// CA certificate for secure connection + public MqttClient(string brokerHostName, int brokerPort = MqttSettings.MQTT_BROKER_DEFAULT_PORT, bool secure = false, X509Certificate caCert = null, bool skipIdAdressResolution = false) + { + if (skipIdAdressResolution) + { + this.Init(brokerHostName, null, brokerPort, secure, caCert); + return; + } + // throw exceptions to the caller + IPHostEntry hostEntry = Dns.GetHostEntry(brokerHostName); + + 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++; + this.Init(brokerHostName, hostEntry.AddressList[i], brokerPort, secure, caCert); + } + else + throw new ApplicationException("No address found for the broker"); + } + +#if BROKER + /// + /// Constructor + /// + /// Raw socket for communication + public MqttClient(Socket socket) + { + this.channel = new MqttNetworkChannel(socket); + + // reference to MQTT settings + this.settings = MqttSettings.Instance; + + // client not connected yet (CONNACK not send from client), some default values + this.IsConnected = false; + this.ClientId = null; + this.CleanSession = true; + + this.keepAliveEvent = new AutoResetEvent(false); + + // 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.receiveQueue = new Queue(); + this.internalQueue = new Queue(); + } +#endif + + /// + /// MqttClient initialization + /// + /// Broker host name + /// Broker IP address + /// Broker port + /// >Using secure connection + /// CA certificate for secure connection + private void Init(string brokerHostName, IPAddress brokerIpAddress, int brokerPort, bool secure, X509Certificate caCert) + { +#if SSL + // check security parameters + if ((secure) && (caCert == null)) + throw new ArgumentException("Secure requested but CA certificate is null !"); +#else + if (secure) + throw new ArgumentException("Library compiled without SSL support"); +#endif + + this.brokerHostName = brokerHostName; + // if broker hostname is null, set ip address + if (this.brokerHostName == null) + this.brokerHostName = brokerIpAddress.ToString(); + + this.brokerIpAddress = brokerIpAddress; + this.brokerPort = brokerPort; + this.secure = secure; + +#if SSL + // if secure, load CA certificate + if (this.secure) + { + this.caCert = caCert; + } +#endif + + // reference to MQTT settings + this.settings = MqttSettings.Instance; + + 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 received message + this.receiveEventWaitHandle = new AutoResetEvent(false); + this.receiveQueue = new Queue(); + this.internalQueue = new Queue(); + } + + /// + /// 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); + } + + /// + /// 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); + } + + /// + /// 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); + } + + /// + /// 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); + + try + { + // create network channel and connect to broker +#if WINDOWS_PHONE + this.channel = new WPMqttNetworkChannel(this.brokerHostName, this.brokerIpAddress, this.brokerPort, this.secure, this.caCert); +#else + this.channel = new MqttNetworkChannel(this.brokerHostName, this.brokerIpAddress, this.brokerPort, this.secure, this.caCert); +#endif + this.channel.Connect(); + } + catch (Exception ex) + { + throw new MqttConnectionException("Exception connecting to the broker", ex); + } + + this.lastCommTime = 0; + this.isRunning = true; + // start thread for receiving messages from broker + this.receiveThread = new Thread(this.ReceiveThread); + this.receiveThread.Start(); + + MqttMsgConnack connack = (MqttMsgConnack)this.SendReceive(connect.GetBytes()); + // 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 + + // start thread for sending keep alive message to the broker + this.keepAliveThread = new Thread(this.KeepAliveThread); + this.keepAliveThread.Start(); + + // start thread for raising received message event from broker + this.receiveEventThread = new Thread(this.ReceiveEventThread); + this.receiveEventThread.Start(); + + // start thread for handling inflight messages queue to broker asynchronously (publish and acknowledge) + this.processInflightThread = new Thread(this.ProcessInflightThread); + this.processInflightThread.Start(); + + this.IsConnected = true; + } + return connack.ReturnCode; + } + + /// + /// Disconnect from broker + /// + public void Disconnect() + { + MqttMsgDisconnect disconnect = new MqttMsgDisconnect(); + this.Send(disconnect.GetBytes()); + + // close client + this.Close(); + } + +#if BROKER + /// + /// Open client communication + /// + public void Open() + { + this.isRunning = true; + + // start thread for receiving messages from client + this.receiveThread = new Thread(this.ReceiveThread); + this.receiveThread.Name = RECEIVE_THREAD_NAME; + this.receiveThread.Start(); + + // start thread for raising received message event from client + this.receiveEventThread = new Thread(this.ReceiveEventThread); + this.receiveEventThread.Name = RECEIVE_EVENT_THREAD_NAME; + this.receiveEventThread.Start(); + + // start thread for handling inflight messages queue to client asynchronously (publish and acknowledge) + this.processInflightThread = new Thread(this.ProcessInflightThread); + this.processInflightThread.Name = PROCESS_INFLIGHT_THREAD_NAME; + this.processInflightThread.Start(); + } +#endif + + /// + /// Close client + /// +#if BROKER + public void Close() +#else + private void Close() +#endif + { + // stop receiving thread + this.isRunning = false; + + // wait end receive thread + //if (this.receiveThread != null) + // this.receiveThread.Join(); + + // wait end receive event thread + if (this.receiveEventThread != null) + { + this.receiveEventWaitHandle.Set(); + // NOTE : no join because Close() could be called inside ReceiveEventThread + // so we have to avoid deadlock + //this.receiveEventThread.Join(); + } + + // waint end process inflight thread + if (this.processInflightThread != null) + { + this.inflightWaitHandle.Set(); + // NOTE : no join because Close() could be called inside ProcessInflightThread + // so we have to avoid deadlock + //this.processInflightThread.Join(); + } + + // avoid deadlock if keep alive timeout expired + if (!this.isKeepAliveTimeout) + { +#if BROKER + // unlock keep alive thread and wait + if (this.keepAliveThread != null) + this.keepAliveEvent.Set(); +#else + // unlock keep alive thread and wait + this.keepAliveEvent.Set(); + + if (this.keepAliveThread != null) + this.keepAliveThread.Join(); +#endif + } + + // close network channel + this.channel.Close(); + + // keep alive thread will set it gracefully + if (!this.isKeepAliveTimeout) + 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.GetBytes(), this.keepAlivePeriod); + } + catch (Exception) + { + this.isKeepAliveTimeout = true; + // client must close connection + this.Close(); + return null; + } + } + +#if BROKER + /// + /// Send CONNACK message to the client (connection accepted or not) + /// + /// Return code for CONNACK message + /// CONNECT message with all client information + public void Connack(byte returnCode, MqttMsgConnect connect) + { + this.lastCommTime = 0; + + // create CONNACK message and ... + MqttMsgConnack connack = new MqttMsgConnack(); + connack.ReturnCode = returnCode; + // ... send it to the client + this.Send(connack.GetBytes()); + + // connection accepted, start keep alive thread checking + if (connack.ReturnCode == MqttMsgConnack.CONN_ACCEPTED) + { + this.ClientId = connect.ClientId; + this.CleanSession = connect.CleanSession; + this.WillFlag = connect.WillFlag; + this.WillTopic = connect.WillTopic; + this.WillMessage = connect.WillMessage; + this.WillQosLevel = connect.WillQosLevel; + + this.keepAlivePeriod = connect.KeepAlivePeriod * 1000; // convert in ms + // broker has a tolerance of 1.5 specified keep alive period + this.keepAlivePeriod += (this.keepAlivePeriod / 2); + + // start thread for checking keep alive period timeout + this.keepAliveThread = new Thread(this.KeepAliveThread); + this.keepAliveThread.Name = KEEP_ALIVE_THREAD; + this.keepAliveThread.Start(); + + this.IsConnected = true; + } + // connection refused, close TCP/IP channel + else + { + this.Close(); + } + } + + /// + /// Send SUBACK message to the client + /// + /// Message Id for the SUBSCRIBE message that is being acknowledged + /// Granted QoS Levels + public void Suback(ushort messageId, byte[] grantedQosLevels) + { + MqttMsgSuback suback = new MqttMsgSuback(); + suback.MessageId = messageId; + suback.GrantedQoSLevels = grantedQosLevels; + + this.Send(suback.GetBytes()); + } + + /// + /// Send UNSUBACK message to the client + /// + /// Message Id for the UNSUBSCRIBE message that is being acknowledged + public void Unsuback(ushort messageId) + { + MqttMsgUnsuback unsuback = new MqttMsgUnsuback(); + unsuback.MessageId = messageId; + + this.Send(unsuback.GetBytes()); + } +#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(); + + // enqueue subscribe request into the inflight queue + this.EnqueueInflight(subscribe, MqttMsgFlow.ToPublish); + + return subscribe.MessageId; + } + + public ushort Subscribe(string topic, byte qos) + { + return Subscribe(new[] { topic }, new[] { qos }); + } + + /// + /// 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(); + + // enqueue unsubscribe request into the inflight queue + this.EnqueueInflight(unsubscribe, MqttMsgFlow.ToPublish); + + 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 + /// + /// 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(); + + // enqueue message to publish into the inflight queue + this.EnqueueInflight(publish, MqttMsgFlow.ToPublish); + + return publish.MessageId; + } + + /// + /// Wrapper method for raising message received event + /// + /// Message received + private void OnMqttMsgReceived(MqttMsgBase msg) + { + lock (this.receiveQueue) + { + this.receiveQueue.Enqueue(msg); + } + + 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 published message event + /// + /// Message identifier for published message + private void OnMqttMsgPublished(ushort messageId) + { + if (this.MqttMsgPublished != null) + { + this.MqttMsgPublished(this, + new MqttMsgPublishedEventArgs(messageId)); + } + } + + /// + /// 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 unsubscribed topic event + /// + /// Message identifier for unsubscribed topic + private void OnMqttMsgUnsubscribed(ushort messageId) + { + if (this.MqttMsgUnsubscribed != null) + { + this.MqttMsgUnsubscribed(this, + new MqttMsgUnsubscribedEventArgs(messageId)); + } + } + +#if BROKER + /// + /// Wrapper method for raising SUBSCRIBE message event + /// + /// Message identifier for subscribe topics request + /// Topics requested to subscribe + /// List of QOS Levels requested + private void OnMqttMsgSubscribeReceived(ushort messageId, string[] topics, byte[] qosLevels) + { + if (this.MqttMsgSubscribeReceived != null) + { + this.MqttMsgSubscribeReceived(this, + new MqttMsgSubscribeEventArgs(messageId, topics, qosLevels)); + } + } + + /// + /// Wrapper method for raising UNSUBSCRIBE message event + /// + /// Message identifier for unsubscribe topics request + /// Topics requested to unsubscribe + private void OnMqttMsgUnsubscribeReceived(ushort messageId, string[] topics) + { + if (this.MqttMsgUnsubscribeReceived != null) + { + this.MqttMsgUnsubscribeReceived(this, + new MqttMsgUnsubscribeEventArgs(messageId, topics)); + } + } + + /// + /// Wrapper method for client connection event + /// + private void OnMqttMsgConnected(MqttMsgConnect connect) + { + if (this.MqttMsgConnected != null) + { + this.MqttMsgConnected(this, new MqttMsgConnectEventArgs(connect)); + } + } +#endif + + /// + /// Wrapper method for client disconnection event + /// + private void OnMqttMsgDisconnected() + { + if (this.MqttMsgDisconnected != null) + { + this.MqttMsgDisconnected(this, EventArgs.Empty); + } + } + + /// + /// 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; +#endif + } + catch (Exception e) + { + throw new MqttCommunicationException(e); + } + } + + /// + /// 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 + /// 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); + + // update last message sent ticks + this.lastCommTime = Environment.TickCount; + } + catch (SocketException e) + { +#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 && !COMPACT_FRAMEWORK) + // connection reset by broker + if (e.SocketErrorCode == SocketError.ConnectionReset) + this.IsConnected = false; +#endif + + throw new MqttCommunicationException(e); + } + +#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)) +#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 MqttTimeoutException(); + throw new MqttCommunicationException(); + } + } + + /// + /// Enqueue a message into the inflight queue + /// + /// Message to enqueue + /// Message flow (publish, acknowledge) + private void EnqueueInflight(MqttMsgBase msg, MqttMsgFlow flow) + { + // enqueue is needed (or not) + bool 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(((MqttMsgPublish)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; + } + + // queue message context + MqttMsgContext msgContext = new MqttMsgContext() + { + Message = msg, + State = state, + Flow = flow, + Attempt = 0 + }; + + lock (this.inflightQueue) + { + // enqueue message and unlock send thread + this.inflightQueue.Enqueue(msgContext); + } + } + + this.inflightWaitHandle.Set(); + } + + /// + /// Enqueue a message into the internal queue + /// + /// Message to enqueue + private void EnqueueInternal(MqttMsgBase msg) + { + // enqueue is needed (or not) + bool enqueue = true; + + // 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. + + // 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(((MqttMsgPubrel)msg).MessageId, MqttMsgFlow.ToAcknowledge); + MqttMsgContext msgCtx = (MqttMsgContext)QueueExtension.Get(this.inflightQueue, msgCtxFinder.Find); + + // 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 = ((MqttMsgPubrel)msg).MessageId; + + this.Send(pubcomp.GetBytes()); + + enqueue = false; + } + } + } + + if (enqueue) + { + lock (this.internalQueue) + { + this.internalQueue.Enqueue(msg); + this.inflightWaitHandle.Set(); + } + } + } + + /// + /// Thread for receiving messages + /// + private void ReceiveThread() + { + int readBytes = 0; + byte[] fixedHeaderFirstByte = new byte[1]; + byte msgType; + +#if BROKER + long now = 0; + + // receive thread started, broker need to receive the first message + // (CONNECT) within a reasonable amount of time after TCP/IP connection + long connectTime = Environment.TickCount; +#endif + + while (this.isRunning) + { + try + { + if (this.channel.DataAvailable) + // read first byte (fixed header) + readBytes = this.channel.Receive(fixedHeaderFirstByte); + else + { +#if BROKER + // client not connected (client didn't send CONNECT yet) + if (!this.IsConnected) + { + now = Environment.TickCount; + + // if connect timeout exceeded ... + if ((now - connectTime) >= this.settings.TimeoutOnConnection) + { + // client must close connection + this.Close(); + + // client raw disconnection + this.OnMqttMsgDisconnected(); + } + } +#endif + // no bytes available, sleep before retry + readBytes = 0; + Thread.Sleep(10); + } + + 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); + + switch (msgType) + { + // CONNECT message received + case MqttMsgBase.MQTT_MSG_CONNECT_TYPE: + +#if BROKER + MqttMsgConnect connect = MqttMsgConnect.Parse(fixedHeaderFirstByte[0], this.channel); + + // raise message received event + this.OnMqttMsgReceived(connect); + break; +#else + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); +#endif + + // CONNACK message received + case MqttMsgBase.MQTT_MSG_CONNACK_TYPE: + +#if BROKER + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); +#else + this.msgReceived = MqttMsgConnack.Parse(fixedHeaderFirstByte[0], this.channel); + this.syncEndReceiving.Set(); + break; +#endif + + // PINGREQ message received + case MqttMsgBase.MQTT_MSG_PINGREQ_TYPE: + +#if BROKER + this.msgReceived = MqttMsgPingReq.Parse(fixedHeaderFirstByte[0], this.channel); + + MqttMsgPingResp pingresp = new MqttMsgPingResp(); + this.Send(pingresp.GetBytes()); + + // raise message received event + //this.OnMqttMsgReceived(this.msgReceived); + break; +#else + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); +#endif + + // PINGRESP message received + case MqttMsgBase.MQTT_MSG_PINGRESP_TYPE: + +#if BROKER + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); +#else + this.msgReceived = MqttMsgPingResp.Parse(fixedHeaderFirstByte[0], this.channel); + this.syncEndReceiving.Set(); + break; +#endif + + // SUBSCRIBE message received + case MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE: + +#if BROKER + MqttMsgSubscribe subscribe = MqttMsgSubscribe.Parse(fixedHeaderFirstByte[0], this.channel); + + // raise message received event + this.OnMqttMsgReceived(subscribe); + + break; +#else + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); +#endif + + // 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], this.channel); + + // enqueue SUBACK message into the internal queue + this.EnqueueInternal(suback); + + break; +#endif + + // PUBLISH message received + case MqttMsgBase.MQTT_MSG_PUBLISH_TYPE: + + MqttMsgPublish publish = MqttMsgPublish.Parse(fixedHeaderFirstByte[0], this.channel); + + // enqueue PUBLISH message to acknowledge into the inflight queue + this.EnqueueInflight(publish, MqttMsgFlow.ToAcknowledge); + + break; + + // 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], this.channel); + + // enqueue PUBACK message into the internal queue + this.EnqueueInternal(puback); + + break; + + // 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], this.channel); + + // enqueue PUBREC message into the internal queue + this.EnqueueInternal(pubrec); + + break; + + // 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], this.channel); + + // enqueue PUBREL message into the internal queue + this.EnqueueInternal(pubrel); + + break; + + // 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], this.channel); + + // enqueue PUBCOMP message into the internal queue + this.EnqueueInternal(pubcomp); + + break; + + // UNSUBSCRIBE message received + case MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE: + +#if BROKER + MqttMsgUnsubscribe unsubscribe = MqttMsgUnsubscribe.Parse(fixedHeaderFirstByte[0], this.channel); + + // raise message received event + this.OnMqttMsgReceived(unsubscribe); + + break; +#else + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); +#endif + + // 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], this.channel); + + // enqueue UNSUBACK message into the internal queue + this.EnqueueInternal(unsuback); + + break; +#endif + + // DISCONNECT message received + case MqttMsgDisconnect.MQTT_MSG_DISCONNECT_TYPE: + +#if BROKER + MqttMsgDisconnect disconnect = MqttMsgDisconnect.Parse(fixedHeaderFirstByte[0], this.channel); + + // raise message received event + this.OnMqttMsgReceived(disconnect); + + break; +#else + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); +#endif + + default: + + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); + } + + this.exReceiving = null; + } + + } + catch (Exception e) + { + this.exReceiving = new MqttCommunicationException(e); + } + } + } + + /// + /// Thread for handling keep alive message + /// + private void KeepAliveThread() + { + long now = 0; + int wait = this.keepAlivePeriod; + this.isKeepAliveTimeout = 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); +#endif + + if (this.isRunning) + { + now = Environment.TickCount; + + // if timeout exceeded ... + if ((now - this.lastCommTime) >= this.keepAlivePeriod) + { +#if BROKER + this.isKeepAliveTimeout = true; + // client must close connection + this.Close(); +#else + // ... send keep alive + this.Ping(); + wait = this.keepAlivePeriod; +#endif + } + else + { + // update waiting time + wait = (int)(this.keepAlivePeriod - (now - this.lastCommTime)); + } + } + } + + if (this.isKeepAliveTimeout) + { + this.IsConnected = false; + // raise disconnection client event + this.OnMqttMsgDisconnected(); + } + } + + /// + /// Thread for raising received message event + /// + private void ReceiveEventThread() + { + while (this.isRunning) + { + if (this.receiveQueue.Count == 0) + // wait on receiving message from client + this.receiveEventWaitHandle.WaitOne(); + + // check if it is running or we are closing client + if (this.isRunning) + { + // get message from queue + MqttMsgBase msg = null; + lock (this.receiveQueue) + { + if (this.receiveQueue.Count > 0) + msg = (MqttMsgBase)this.receiveQueue.Dequeue(); + } + + 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); +#endif + + // SUBSCRIBE message received + case MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE: + +#if BROKER + MqttMsgSubscribe subscribe = (MqttMsgSubscribe)msg; + // raise subscribe topic event (SUBSCRIBE message received) + this.OnMqttMsgSubscribeReceived(subscribe.MessageId, subscribe.Topics, subscribe.QoSLevels); +#else + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); +#endif + + // SUBACK message received + case MqttMsgBase.MQTT_MSG_SUBACK_TYPE: + + // raise subscribed topic event (SUBACK message received) + this.OnMqttMsgSubscribed((MqttMsgSuback)msg); + break; + + // PUBLISH message received + case MqttMsgBase.MQTT_MSG_PUBLISH_TYPE: + + // raise PUBLISH message received event + this.OnMqttMsgPublishReceived((MqttMsgPublish)msg); + break; + + // PUBACK message received + case MqttMsgBase.MQTT_MSG_PUBACK_TYPE: + + // raise published message event + // (PUBACK received for QoS Level 1) + this.OnMqttMsgPublished(((MqttMsgPuback)msg).MessageId); + break; + + // PUBREL message received + case MqttMsgBase.MQTT_MSG_PUBREL_TYPE: + + // raise message received event + // (PUBREL received for QoS Level 2) + this.OnMqttMsgPublishReceived((MqttMsgPublish)msg); + break; + + // PUBCOMP message received + case MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE: + + // raise published message event + // (PUBCOMP received for QoS Level 2) + this.OnMqttMsgPublished(((MqttMsgPubcomp)msg).MessageId); + break; + + // UNSUBSCRIBE message received from client + case MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE: + +#if BROKER + MqttMsgUnsubscribe unsubscribe = (MqttMsgUnsubscribe)msg; + // raise unsubscribe topic event (UNSUBSCRIBE message received) + this.OnMqttMsgUnsubscribeReceived(unsubscribe.MessageId, unsubscribe.Topics); + break; +#else + throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); +#endif + + // UNSUBACK message received + case MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE: + + // raise unsubscribed topic event + this.OnMqttMsgUnsubscribed(((MqttMsgUnsuback)msg).MessageId); + break; + + // 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); +#endif + } + } + } + } + } + + /// + /// Process inflight messages queue + /// + private void ProcessInflightThread() + { + MqttMsgContext msgContext = null; + MqttMsgBase msgInflight = null; + MqttMsgBase msgReceived = null; + bool acknowledge = false; + int timeout = Timeout.Infinite; + + 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); +#endif + + // it could be unblocked because Close() method is joining + if (this.isRunning) + { + lock (this.inflightQueue) + { + // 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; + + // 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.GetBytes()); + } + // QoS 0, no need acknowledge + else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) + { + // notify published message from broker (no need acknowledged) + this.OnMqttMsgReceived(msgInflight); + } + break; + + case MqttMsgState.QueuedQos1: + + // QoS 1, PUBLISH or SUBSCRIBE/UNSUBSCRIBE message to send to broker, state change to wait PUBACK or SUBACK/UNSUBACK + if (msgContext.Flow == MqttMsgFlow.ToPublish) + { + if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) + // PUBLISH message to send, wait for PUBACK + msgContext.State = MqttMsgState.WaitForPuback; + 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; + + msgContext.Timestamp = Environment.TickCount; + msgContext.Attempt++; + // retry ? set dup flag + if (msgContext.Attempt > 1) + msgInflight.DupFlag = true; + + this.Send(msgInflight.GetBytes()); + + // update timeout + int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp)); + timeout = (msgTimeout < timeout) ? msgTimeout : 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 = ((MqttMsgPublish)msgInflight).MessageId; + + this.Send(puback.GetBytes()); + + // notify published message from broker and acknowledged + this.OnMqttMsgReceived(msgInflight); + } + break; + + case MqttMsgState.QueuedQos2: + + // QoS 2, PUBLISH message to send to broker, state change to wait PUBREC + if (msgContext.Flow == MqttMsgFlow.ToPublish) + { + msgContext.State = MqttMsgState.WaitForPubrec; + msgContext.Timestamp = Environment.TickCount; + msgContext.Attempt++; + // retry ? set dup flag + if (msgContext.Attempt > 1) + msgInflight.DupFlag = true; + + this.Send(msgInflight.GetBytes()); + + // update timeout + int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp)); + timeout = (msgTimeout < timeout) ? msgTimeout : 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 = ((MqttMsgPublish)msgInflight).MessageId; + + msgContext.State = MqttMsgState.WaitForPubrel; + + this.Send(pubrec.GetBytes()); + + // 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) && ((msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBACK_TYPE) || + (msgReceived.Type == MqttMsgBase.MQTT_MSG_SUBACK_TYPE) || + (msgReceived.Type == MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE))) + { + // PUBACK message or SUBACK message for the current message + if (((msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) && (((MqttMsgPuback)msgReceived).MessageId == ((MqttMsgPublish)msgInflight).MessageId)) || + ((msgInflight.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE) && (((MqttMsgSuback)msgReceived).MessageId == ((MqttMsgSubscribe)msgInflight).MessageId)) || + ((msgInflight.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE) && (((MqttMsgUnsuback)msgReceived).MessageId == ((MqttMsgUnsubscribe)msgInflight).MessageId))) + { + lock (this.internalQueue) + { + // received message processed + this.internalQueue.Dequeue(); + acknowledge = true; + } + + // notify received acknowledge from broker of a published message or subscribe/unsubscribe message + this.OnMqttMsgReceived(msgReceived); + } + } + + // current message not acknowledged, no PUBACK or SUBACK/UNSUBACK or not equal messageid + if (!acknowledge) + { + // 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 ((Environment.TickCount - msgContext.Timestamp) >= 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 + { + // 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 - (Environment.TickCount - msgContext.Timestamp)); + 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 (((MqttMsgPubrec)msgReceived).MessageId == ((MqttMsgPublish)msgInflight).MessageId) + { + lock (this.internalQueue) + { + // received message processed + this.internalQueue.Dequeue(); + acknowledge = true; + } + + MqttMsgPubrel pubrel = new MqttMsgPubrel(); + pubrel.MessageId = ((MqttMsgPublish)msgInflight).MessageId; + + msgContext.State = MqttMsgState.WaitForPubcomp; + msgContext.Timestamp = Environment.TickCount; + msgContext.Attempt = 1; + + this.Send(pubrel.GetBytes()); + + // update timeout + int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp)); + timeout = (msgTimeout < timeout) ? msgTimeout : timeout; + + // re-enqueue message + this.inflightQueue.Enqueue(msgContext); + } + } + + // current message not acknowledged + if (!acknowledge) + { + // check timeout for receiving PUBREC since PUBLISH was sent + if ((Environment.TickCount - msgContext.Timestamp) >= 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 + { + // re-enqueue message + this.inflightQueue.Enqueue(msgContext); + + // update timeout + int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp)); + 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 (((MqttMsgPubrel)msgReceived).MessageId == ((MqttMsgPublish)msgInflight).MessageId) + { + lock (this.internalQueue) + { + // received message processed + this.internalQueue.Dequeue(); + } + + MqttMsgPubcomp pubcomp = new MqttMsgPubcomp(); + pubcomp.MessageId = ((MqttMsgPublish)msgInflight).MessageId; + + this.Send(pubcomp.GetBytes()); + + // notify published message from broker and acknowledged + this.OnMqttMsgReceived(msgInflight); + } + 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 (((MqttMsgPubcomp)msgReceived).MessageId == ((MqttMsgPublish)msgInflight).MessageId) + { + lock (this.internalQueue) + { + // received message processed + this.internalQueue.Dequeue(); + acknowledge = true; + } + + // notify received acknowledge from broker of a published message + this.OnMqttMsgReceived(msgReceived); + } + } + + // current message not acknowledged + if (!acknowledge) + { + // check timeout for receiving PUBCOMP since PUBREL was sent + if ((Environment.TickCount - msgContext.Timestamp) >= 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 + { + // re-enqueue message + this.inflightQueue.Enqueue(msgContext); + + // update timeout + int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp)); + 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 = ((MqttMsgPublish)msgInflight).MessageId; + + msgContext.State = MqttMsgState.WaitForPubcomp; + msgContext.Timestamp = Environment.TickCount; + msgContext.Attempt++; + // retry ? set dup flag + if (msgContext.Attempt > 1) + pubrel.DupFlag = true; + + this.Send(pubrel.GetBytes()); + + // update timeout + int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp)); + timeout = (msgTimeout < timeout) ? msgTimeout : 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; + } + } + } + } + catch (MqttCommunicationException) + { + this.Close(); + + // raise disconnection client event + this.OnMqttMsgDisconnected(); + } + } + + /// + /// Generate the next message identifier + /// + /// Message identifier + private ushort GetMessageId() + { + if (this.messageIdCounter == 0) + this.messageIdCounter++; + else + this.messageIdCounter = ((this.messageIdCounter % UInt16.MaxValue) != 0) ? (ushort)(this.messageIdCounter + 1) : (ushort)0; + 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) && + (((MqttMsgPublish)msgCtx.Message).MessageId == this.MessageId) && + msgCtx.Flow == this.Flow); + + } + } + } +} diff --git a/M2Mqtt/MqttNetworkChannel.cs b/M2Mqtt/MqttNetworkChannel.cs new file mode 100644 index 0000000..4455496 --- /dev/null +++ b/M2Mqtt/MqttNetworkChannel.cs @@ -0,0 +1,272 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +#if SSL +#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; +#endif +#endif +using System.Net.Sockets; +using System.Net; +using System.Security.Cryptography.X509Certificates; + +namespace uPLibrary.Networking.M2Mqtt +{ + /// + /// Channel to communicate over the network + /// + public class MqttNetworkChannel : IMqttNetworkChannel + { + // remote host information + private string remoteHostName; + private IPAddress remoteIpAddress; + private int remotePort; + + // socket for communication + private Socket socket; + // using SSL + private bool secure; + + // CA certificate + private X509Certificate caCert; + + /// + /// Remote host name + /// + public string RemoteHostName { get { return this.remoteHostName; } } + + /// + /// Remote IP address + /// + public IPAddress RemoteIpAddress { get { return this.remoteIpAddress; } } + + /// + /// Remote port + /// + public int RemotePort { get { return this.remotePort; } } + +#if SSL + // 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 + { +#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); +#else + if (secure) + return this.netStream.DataAvailable; + else + return (this.socket.Available > 0); +#endif +#else + return (this.socket.Available > 0); +#endif + } + } + + /// + /// Constructor + /// + /// Socket opened with the client + public MqttNetworkChannel(Socket socket) + { + this.socket = socket; + } + + /// + /// Constructor + /// + /// Remote Host name + /// Remote IP address + /// Remote port + public MqttNetworkChannel(string remoteHostName, IPAddress remoteIpAddress, int remotePort) : + this(remoteHostName, remoteIpAddress, remotePort, false, null) + { + } + + /// + /// Constructor + /// + /// Remote Host name + /// Remote IP address + /// Remote port + /// Using SSL + /// CA certificate + public MqttNetworkChannel(string remoteHostName, IPAddress remoteIpAddress, int remotePort, bool secure, X509Certificate caCert) + { + this.remoteHostName = remoteHostName; + this.remoteIpAddress = remoteIpAddress; + this.remotePort = remotePort; + this.secure = secure; + this.caCert = caCert; + } + + /// + /// 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) + this.sslStream = new SslStream(this.socket); +#else + this.netStream = new NetworkStream(this.socket); + this.sslStream = new SslStream(this.netStream); +#endif + + // server authentication (SSL/TLS handshake) +#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3) + this.sslStream.AuthenticateAsClient(this.remoteHostName, + null, + new X509Certificate[] { this.caCert }, + SslVerification.CertificateRequired, + SslProtocols.TLSv1); +#else + this.sslStream.AuthenticateAsClient(this.remoteHostName, + new X509CertificateCollection(new X509Certificate[] { this.caCert }), + SslProtocols.Tls, false); +#endif + } +#endif + } + + /// + /// Send data on the network channel + /// + /// Data buffer to send + /// Number of byte sent + public int Send(byte[] buffer) + { +#if SSL + if (this.secure) + { + this.sslStream.Write(buffer, 0, buffer.Length); + 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) + { +#if SSL + if (this.secure) + { + // read all data needed (until fill buffer) + int idx = 0; + while (idx < buffer.Length) + { + idx += this.sslStream.Read(buffer, idx, buffer.Length - idx); + } + return buffer.Length; + } + else + { + // read all data needed (until fill buffer) + int idx = 0; + while (idx < buffer.Length) + { + idx += this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None); + } + return buffer.Length; + } +#else + // read all data needed (until fill buffer) + int idx = 0; + while (idx < buffer.Length) + { + idx += this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None); + } + return buffer.Length; +#endif + } + + /// + /// 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 + } + } + + /// + /// 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) + return ipAddress.AddressFamily; +#else + return (ipAddress.ToString().IndexOf(':') != -1) ? + AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork; +#endif + } + } +} diff --git a/M2Mqtt/MqttSettings.cs b/M2Mqtt/MqttSettings.cs new file mode 100644 index 0000000..9db3351 --- /dev/null +++ b/M2Mqtt/MqttSettings.cs @@ -0,0 +1,98 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +namespace uPLibrary.Networking.M2Mqtt +{ + /// + /// Settings class for the MQTT broker + /// + 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 = 25000; + // 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 = 25000; + + /// + /// 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; } + + /// + /// 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; + } + } +} diff --git a/M2Mqtt/Properties/AssemblyInfo.cs b/M2Mqtt/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f99a6c1 --- /dev/null +++ b/M2Mqtt/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("M2Mqtt")] +[assembly: AssemblyDescription("MQTT Client Library for M2M communication")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Paolo Patierno")] +[assembly: AssemblyProduct("M2Mqtt")] +[assembly: AssemblyCopyright("Copyright © Paolo Patierno 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("3.3.0.0")] +// to avoid compilation error (AssemblyFileVersionAttribute doesn't exist) under .Net CF 3.5 +#if !WindowsCE +[assembly: AssemblyFileVersion("3.3.0.0")] +#endif \ No newline at end of file diff --git a/M2Mqtt/Utility/QueueExtension.cs b/M2Mqtt/Utility/QueueExtension.cs new file mode 100644 index 0000000..988198f --- /dev/null +++ b/M2Mqtt/Utility/QueueExtension.cs @@ -0,0 +1,52 @@ +/* +M2Mqtt - MQTT Client Library for .Net +Copyright (c) 2014, Paolo Patierno, All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. +*/ + +using System; +using System.Collections; + +namespace uPLibrary.Networking.M2Mqtt +{ + /// + /// Extension class for 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; + } + } +} diff --git a/M2Mqtt/bin/Release/M2Mqtt.dll b/M2Mqtt/bin/Release/M2Mqtt.dll new file mode 100644 index 0000000000000000000000000000000000000000..b8d32e3acc28453567491c168dc5b3ff002233b8 GIT binary patch literal 38912 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 literal 0 HcmV?d00001 diff --git a/M2Mqtt/uM2MqttNetMf42.csproj b/M2Mqtt/uM2MqttNetMf42.csproj new file mode 100644 index 0000000..62c26ae --- /dev/null +++ b/M2Mqtt/uM2MqttNetMf42.csproj @@ -0,0 +1,75 @@ + + + + M2Mqtt + Library + uPLibrary.Networking.M2Mqtt + {b69e3092-b931-443c-abe7-7e7b65f2a37f};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 9.0.21022 + 2.0 + {F733523A-F14E-4F5A-9E7C-085CA80F52B1} + v4.2 + $(MSBuildExtensionsPath32)\Microsoft\.NET Micro Framework\ + + + true + full + false + bin\Debug\NetMf42\ + DEBUG;TRACE,MF_FRAMEWORK_VERSION_V4_2,SSL + prompt + 4 + + + pdbonly + true + bin\Release\NetMf42\ + TRACE,MF_FRAMEWORK_VERSION_V4_2,SSL + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/M2Mqtt/uM2MqttNetMf43.csproj b/M2Mqtt/uM2MqttNetMf43.csproj new file mode 100644 index 0000000..7acbd06 --- /dev/null +++ b/M2Mqtt/uM2MqttNetMf43.csproj @@ -0,0 +1,75 @@ + + + + M2Mqtt + Library + uPLibrary.Networking.M2Mqtt + {b69e3092-b931-443c-abe7-7e7b65f2a37f};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 9.0.21022 + 2.0 + {6A6D540B-8554-4FFD-8884-8BEFCCD9AD41} + v4.3 + $(MSBuildExtensionsPath32)\Microsoft\.NET Micro Framework\ + + + true + full + false + bin\Debug\NetMf43\ + DEBUG;TRACE,MF_FRAMEWORK_VERSION_V4_3,SSL + prompt + 4 + + + pdbonly + true + bin\Release\NetMf43\ + TRACE,MF_FRAMEWORK_VERSION_V4_3,SSL + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file