diff --git a/M2Mqtt.sln b/M2Mqtt.sln
index cd45efc..f3d48da 100644
--- a/M2Mqtt.sln
+++ b/M2Mqtt.sln
@@ -20,6 +20,6 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {E4CE8710-7B51-439F-A427-9AEFFE71E682}
+ SolutionGuid = {135814CF-A469-4436-9C51-3CFE4B93D2FE}
EndGlobalSection
EndGlobal
diff --git a/M2Mqtt/Exceptions/MqttClientException.cs b/M2Mqtt/Exceptions/MqttClientException.cs
index bd58745..cd8303f 100644
--- a/M2Mqtt/Exceptions/MqttClientException.cs
+++ b/M2Mqtt/Exceptions/MqttClientException.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
@@ -23,7 +21,7 @@ namespace uPLibrary.Networking.M2Mqtt.Exceptions
///
/// MQTT client exception
///
- public class MqttClientException : ApplicationException
+ public class MqttClientException : Exception
{
///
/// Constructor
@@ -53,9 +51,9 @@ namespace uPLibrary.Networking.M2Mqtt.Exceptions
public enum MqttClientErrorCode
{
///
- /// Will topic length error
+ /// Will error (topic, message or QoS level)
///
- WillTopicWrong = 1,
+ WillWrong = 1,
///
/// Keep alive period too large
@@ -100,6 +98,35 @@ namespace uPLibrary.Networking.M2Mqtt.Exceptions
///
/// Wrong Message Id
///
- WrongMessageId
+ WrongMessageId,
+
+ ///
+ /// Inflight queue is full
+ ///
+ InflightQueueFull,
+
+ // [v3.1.1]
+ ///
+ /// Invalid flag bits received
+ ///
+ InvalidFlagBits,
+
+ // [v3.1.1]
+ ///
+ /// Invalid connect flags received
+ ///
+ InvalidConnectFlags,
+
+ // [v3.1.1]
+ ///
+ /// Invalid client id
+ ///
+ InvalidClientId,
+
+ // [v3.1.1]
+ ///
+ /// Invalid protocol name
+ ///
+ InvalidProtocolName
}
}
diff --git a/M2Mqtt/Exceptions/MqttCommunicationException.cs b/M2Mqtt/Exceptions/MqttCommunicationException.cs
index 9d2591c..6b916cc 100644
--- a/M2Mqtt/Exceptions/MqttCommunicationException.cs
+++ b/M2Mqtt/Exceptions/MqttCommunicationException.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
@@ -23,7 +21,7 @@ namespace uPLibrary.Networking.M2Mqtt.Exceptions
///
/// Exception due to error communication with broker on socket
///
- public class MqttCommunicationException : ApplicationException
+ public class MqttCommunicationException : Exception
{
///
/// Default constructor
diff --git a/M2Mqtt/Exceptions/MqttConnectionException.cs b/M2Mqtt/Exceptions/MqttConnectionException.cs
index 9d178c2..3203c7e 100644
--- a/M2Mqtt/Exceptions/MqttConnectionException.cs
+++ b/M2Mqtt/Exceptions/MqttConnectionException.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
@@ -23,7 +21,7 @@ namespace uPLibrary.Networking.M2Mqtt.Exceptions
///
/// Connection to the broker exception
///
- public class MqttConnectionException : ApplicationException
+ public class MqttConnectionException : Exception
{
public MqttConnectionException(string message, Exception innerException)
: base(message, innerException)
diff --git a/M2Mqtt/Exceptions/MqttTimeoutException.cs b/M2Mqtt/Exceptions/MqttTimeoutException.cs
index a5b825e..b15e69e 100644
--- a/M2Mqtt/Exceptions/MqttTimeoutException.cs
+++ b/M2Mqtt/Exceptions/MqttTimeoutException.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
@@ -23,7 +21,7 @@ namespace uPLibrary.Networking.M2Mqtt.Exceptions
///
/// Timeout on receiving from broker exception
///
- public class MqttTimeoutException : ApplicationException
+ public class MqttTimeoutException : Exception
{
}
}
diff --git a/M2Mqtt/IMqttNetworkChannel.cs b/M2Mqtt/IMqttNetworkChannel.cs
index bf01880..f4ff90f 100644
--- a/M2Mqtt/IMqttNetworkChannel.cs
+++ b/M2Mqtt/IMqttNetworkChannel.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
@@ -38,6 +36,14 @@ namespace uPLibrary.Networking.M2Mqtt
/// Number of bytes received
int Receive(byte[] buffer);
+ ///
+ /// Receive data from the network channel with a specified timeout
+ ///
+ /// Data buffer for receiving data
+ /// Timeout on receiving (in milliseconds)
+ /// Number of bytes received
+ int Receive(byte[] buffer, int timeout);
+
///
/// Send data on the network channel to the broker
///
@@ -54,5 +60,10 @@ namespace uPLibrary.Networking.M2Mqtt
/// Connect to remote server
///
void Connect();
+
+ ///
+ /// Accept client connection
+ ///
+ void Accept();
}
}
diff --git a/M2Mqtt/Internal/InternalEvent.cs b/M2Mqtt/Internal/InternalEvent.cs
new file mode 100644
index 0000000..7f298c7
--- /dev/null
+++ b/M2Mqtt/Internal/InternalEvent.cs
@@ -0,0 +1,25 @@
+/*
+Copyright (c) 2013, 2014 Paolo Patierno
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
+*/
+
+namespace uPLibrary.Networking.M2Mqtt.Internal
+{
+ ///
+ /// Generic internal event for dispatching
+ ///
+ public abstract class InternalEvent
+ {
+ }
+}
diff --git a/M2Mqtt/Internal/MsgInternalEvent.cs b/M2Mqtt/Internal/MsgInternalEvent.cs
new file mode 100644
index 0000000..8cf46a4
--- /dev/null
+++ b/M2Mqtt/Internal/MsgInternalEvent.cs
@@ -0,0 +1,51 @@
+/*
+Copyright (c) 2013, 2014 Paolo Patierno
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
+*/
+
+using uPLibrary.Networking.M2Mqtt.Messages;
+
+namespace uPLibrary.Networking.M2Mqtt.Internal
+{
+ ///
+ /// Internal event with a message
+ ///
+ public class MsgInternalEvent : InternalEvent
+ {
+ #region Properties ...
+
+ ///
+ /// Related message
+ ///
+ public MqttMsgBase Message
+ {
+ get { return this.msg; }
+ set { this.msg = value; }
+ }
+
+ #endregion
+
+ // related message
+ protected MqttMsgBase msg;
+
+ ///
+ /// Constructor
+ ///
+ /// Related message
+ public MsgInternalEvent(MqttMsgBase msg)
+ {
+ this.msg = msg;
+ }
+ }
+}
diff --git a/M2Mqtt/Internal/MsgPublishedInternalEvent.cs b/M2Mqtt/Internal/MsgPublishedInternalEvent.cs
new file mode 100644
index 0000000..5a7f182
--- /dev/null
+++ b/M2Mqtt/Internal/MsgPublishedInternalEvent.cs
@@ -0,0 +1,53 @@
+/*
+Copyright (c) 2013, 2014 Paolo Patierno
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
+*/
+
+using uPLibrary.Networking.M2Mqtt.Messages;
+
+namespace uPLibrary.Networking.M2Mqtt.Internal
+{
+ ///
+ /// Internal event for a published message
+ ///
+ public class MsgPublishedInternalEvent : MsgInternalEvent
+ {
+ #region Properties...
+
+ ///
+ /// Message published (or failed due to retries)
+ ///
+ public bool IsPublished
+ {
+ get { return this.isPublished; }
+ internal set { this.isPublished = value; }
+ }
+
+ #endregion
+
+ // published flag
+ bool isPublished;
+
+ ///
+ /// Constructor
+ ///
+ /// Message published
+ /// Publish flag
+ public MsgPublishedInternalEvent(MqttMsgBase msg, bool isPublished)
+ : base(msg)
+ {
+ this.isPublished = isPublished;
+ }
+ }
+}
diff --git a/M2Mqtt/M2Mqtt.csproj b/M2Mqtt/M2Mqtt.csproj
index 5e1ab26..24464d3 100644
--- a/M2Mqtt/M2Mqtt.csproj
+++ b/M2Mqtt/M2Mqtt.csproj
@@ -19,17 +19,19 @@
full
false
bin\Debug\
- TRACE;DEBUG;SSL
+ TRACE;DEBUG
prompt
4
+ false
pdbonly
true
bin\Release\
- TRACE;SSL
+ TRACE
prompt
4
+ false
@@ -42,6 +44,9 @@
+
+
+
@@ -66,9 +71,15 @@
-
+
+
+
+
+
+
+
diff --git a/M2Mqtt/M2MqttMono.csproj b/M2Mqtt/M2MqttMono.csproj
deleted file mode 100644
index da48297..0000000
--- a/M2Mqtt/M2MqttMono.csproj
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
- 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
deleted file mode 100644
index bf64adf..0000000
--- a/M2Mqtt/M2MqttNetCf35.csproj
+++ /dev/null
@@ -1,106 +0,0 @@
-
-
- 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
deleted file mode 100644
index 0ca164d..0000000
--- a/M2Mqtt/M2MqttNetCf39.csproj
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
-
-
- 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
index f8459e0..870fe5f 100644
--- a/M2Mqtt/Messages/MqttMsgBase.cs
+++ b/M2Mqtt/Messages/MqttMsgBase.cs
@@ -1,29 +1,21 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
-#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;
+using System.Text;
namespace uPLibrary.Networking.M2Mqtt.Messages
{
@@ -38,6 +30,9 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
internal const byte MSG_TYPE_MASK = 0xF0;
internal const byte MSG_TYPE_OFFSET = 0x04;
internal const byte MSG_TYPE_SIZE = 0x04;
+ internal const byte MSG_FLAG_BITS_MASK = 0x0F; // [v3.1.1]
+ internal const byte MSG_FLAG_BITS_OFFSET = 0x00; // [v3.1.1]
+ internal const byte MSG_FLAG_BITS_SIZE = 0x04; // [v3.1.1]
internal const byte DUP_FLAG_MASK = 0x08;
internal const byte DUP_FLAG_OFFSET = 0x03;
internal const byte DUP_FLAG_SIZE = 0x01;
@@ -64,11 +59,30 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
internal const byte MQTT_MSG_PINGRESP_TYPE = 0x0D;
internal const byte MQTT_MSG_DISCONNECT_TYPE = 0x0E;
+ // [v3.1.1] MQTT flag bits
+ internal const byte MQTT_MSG_CONNECT_FLAG_BITS = 0x00;
+ internal const byte MQTT_MSG_CONNACK_FLAG_BITS = 0x00;
+ internal const byte MQTT_MSG_PUBLISH_FLAG_BITS = 0x00; // just defined as 0x00 but depends on publish props (dup, qos, retain)
+ internal const byte MQTT_MSG_PUBACK_FLAG_BITS = 0x00;
+ internal const byte MQTT_MSG_PUBREC_FLAG_BITS = 0x00;
+ internal const byte MQTT_MSG_PUBREL_FLAG_BITS = 0x02;
+ internal const byte MQTT_MSG_PUBCOMP_FLAG_BITS = 0x00;
+ internal const byte MQTT_MSG_SUBSCRIBE_FLAG_BITS = 0x02;
+ internal const byte MQTT_MSG_SUBACK_FLAG_BITS = 0x00;
+ internal const byte MQTT_MSG_UNSUBSCRIBE_FLAG_BITS = 0x02;
+ internal const byte MQTT_MSG_UNSUBACK_FLAG_BITS = 0x00;
+ internal const byte MQTT_MSG_PINGREQ_FLAG_BITS = 0x00;
+ internal const byte MQTT_MSG_PINGRESP_FLAG_BITS = 0x00;
+ internal const byte MQTT_MSG_DISCONNECT_FLAG_BITS = 0x00;
+
// QOS levels
public const byte QOS_LEVEL_AT_MOST_ONCE = 0x00;
public const byte QOS_LEVEL_AT_LEAST_ONCE = 0x01;
public const byte QOS_LEVEL_EXACTLY_ONCE = 0x02;
+ // SUBSCRIBE QoS level granted failure [v3.1.1]
+ public const byte QOS_LEVEL_GRANTED_FAILURE = 0x80;
+
internal const ushort MAX_TOPIC_LENGTH = 65535;
internal const ushort MIN_TOPIC_LENGTH = 1;
internal const byte MESSAGE_ID_SIZE = 2;
@@ -113,6 +127,15 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
set { this.retain = value; }
}
+ ///
+ /// Message identifier for the message
+ ///
+ public ushort MessageId
+ {
+ get { return this.messageId; }
+ set { this.messageId = value; }
+ }
+
#endregion
// message type
@@ -123,12 +146,15 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
protected byte qosLevel;
// retain flag
protected bool retain;
+ // message identifier
+ protected ushort messageId;
///
/// Returns message bytes rapresentation
///
+ /// Protocol version
/// Bytes rapresentation
- public abstract byte[] GetBytes();
+ public abstract byte[] GetBytes(byte protocolVersion);
///
/// Encode remaining length and insert it into message buffer
@@ -172,5 +198,78 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
} while ((digit & 128) != 0);
return value;
}
+
+#if TRACE
+ ///
+ /// Returns a string representation of the message for tracing
+ ///
+ /// Message name
+ /// Message fields name
+ /// Message fields value
+ /// String representation of the message
+ protected string GetTraceString(string name, object[] fieldNames, object[] fieldValues)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append(name);
+
+ if ((fieldNames != null) && (fieldValues != null))
+ {
+ sb.Append("(");
+ bool addComma = false;
+ for (int i = 0; i < fieldValues.Length; i++)
+ {
+ if (fieldValues[i] != null)
+ {
+ if (addComma)
+ {
+ sb.Append(",");
+ }
+
+ sb.Append(fieldNames[i]);
+ sb.Append(":");
+ sb.Append(GetStringObject(fieldValues[i]));
+ addComma = true;
+ }
+ }
+ sb.Append(")");
+ }
+
+ return sb.ToString();
+ }
+
+ object GetStringObject(object value)
+ {
+ byte[] binary = value as byte[];
+ if (binary != null)
+ {
+ string hexChars = "0123456789ABCDEF";
+ StringBuilder sb = new StringBuilder(binary.Length * 2);
+ for (int i = 0; i < binary.Length; ++i)
+ {
+ sb.Append(hexChars[binary[i] >> 4]);
+ sb.Append(hexChars[binary[i] & 0x0F]);
+ }
+
+ return sb.ToString();
+ }
+
+ object[] list = value as object[];
+ if (list != null)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append('[');
+ for (int i = 0; i < list.Length; ++i)
+ {
+ if (i > 0) sb.Append(',');
+ sb.Append(list[i]);
+ }
+ sb.Append(']');
+
+ return sb.ToString();
+ }
+
+ return value;
+ }
+#endif
}
}
diff --git a/M2Mqtt/Messages/MqttMsgConnack.cs b/M2Mqtt/Messages/MqttMsgConnack.cs
index d6ca107..ae5a342 100644
--- a/M2Mqtt/Messages/MqttMsgConnack.cs
+++ b/M2Mqtt/Messages/MqttMsgConnack.cs
@@ -1,22 +1,21 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
+using uPLibrary.Networking.M2Mqtt.Exceptions;
namespace uPLibrary.Networking.M2Mqtt.Messages
{
@@ -37,6 +36,13 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
private const byte TOPIC_NAME_COMP_RESP_BYTE_OFFSET = 0;
private const byte TOPIC_NAME_COMP_RESP_BYTE_SIZE = 1;
+ // [v3.1.1] connect acknowledge flags replace "old" topic name compression respone (not used in 3.1)
+ private const byte CONN_ACK_FLAGS_BYTE_OFFSET = 0;
+ private const byte CONN_ACK_FLAGS_BYTE_SIZE = 1;
+ // [v3.1.1] session present flag
+ private const byte SESSION_PRESENT_FLAG_MASK = 0x01;
+ private const byte SESSION_PRESENT_FLAG_OFFSET = 0x00;
+ private const byte SESSION_PRESENT_FLAG_SIZE = 0x01;
private const byte CONN_RETURN_CODE_BYTE_OFFSET = 1;
private const byte CONN_RETURN_CODE_BYTE_SIZE = 1;
@@ -44,6 +50,16 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
#region Properties...
+ // [v3.1.1] session present flag
+ ///
+ /// Session present flag
+ ///
+ public bool SessionPresent
+ {
+ get { return this.sessionPresent; }
+ set { this.sessionPresent = value; }
+ }
+
///
/// Return Code
///
@@ -55,6 +71,9 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
#endregion
+ // [v3.1.1] session present flag
+ private bool sessionPresent;
+
// return code for CONNACK message
private byte returnCode;
@@ -70,26 +89,39 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Parse bytes for a CONNACK message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// CONNACK message instance
- public static MqttMsgConnack Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgConnack Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
byte[] buffer;
MqttMsgConnack msg = new MqttMsgConnack();
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ {
+ // [v3.1.1] check flag bits
+ if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_CONNACK_FLAG_BITS)
+ throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
+ }
+
// get remaining length and allocate buffer
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
buffer = new byte[remainingLength];
// read bytes from socket...
channel.Receive(buffer);
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ {
+ // [v3.1.1] ... set session present flag ...
+ msg.sessionPresent = (buffer[CONN_ACK_FLAGS_BYTE_OFFSET] & SESSION_PRESENT_FLAG_MASK) != 0x00;
+ }
// ...and set return code from broker
msg.returnCode = buffer[CONN_RETURN_CODE_BYTE_OFFSET];
return msg;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte ProtocolVersion)
{
int fixedHeaderSize = 0;
int varHeaderSize = 0;
@@ -98,8 +130,12 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
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);
+ if (ProtocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ // flags byte and connect return code
+ varHeaderSize += (CONN_ACK_FLAGS_BYTE_SIZE + CONN_RETURN_CODE_BYTE_SIZE);
+ else
+ // topic name compression response and connect return code
+ varHeaderSize += (TOPIC_NAME_COMP_RESP_BYTE_SIZE + CONN_RETURN_CODE_BYTE_SIZE);
remainingLength += (varHeaderSize + payloadSize);
@@ -119,19 +155,37 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
// first fixed header byte
- buffer[index] = (byte)(MQTT_MSG_CONNACK_TYPE << MSG_TYPE_OFFSET);
- index++;
-
+ if (ProtocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ buffer[index++] = (MQTT_MSG_CONNACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_CONNACK_FLAG_BITS; // [v.3.1.1]
+ else
+ buffer[index++] = (byte)(MQTT_MSG_CONNACK_TYPE << MSG_TYPE_OFFSET);
+
// encode remaining length
index = this.encodeRemainingLength(remainingLength, buffer, index);
- // topic name compression response (reserved values. not used);
- buffer[index++] = 0x00;
+ if (ProtocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ // [v3.1.1] session present flag
+ buffer[index++] = this.sessionPresent ? (byte)(1 << SESSION_PRESENT_FLAG_OFFSET) : (byte)0x00;
+ else
+ // topic name compression response (reserved values. not used);
+ buffer[index++] = 0x00;
// connect return code
buffer[index++] = this.returnCode;
return buffer;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "CONNACK",
+ new object[] { "returnCode" },
+ new object[] { this.returnCode });
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgConnect.cs b/M2Mqtt/Messages/MqttMsgConnect.cs
index 67e0835..382cf83 100644
--- a/M2Mqtt/Messages/MqttMsgConnect.cs
+++ b/M2Mqtt/Messages/MqttMsgConnect.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
@@ -30,19 +28,22 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
#region Constants...
// protocol name supported
- internal const string PROTOCOL_NAME = "MQIsdp";
-
- // max length for client id
+ internal const string PROTOCOL_NAME_V3_1 = "MQIsdp";
+ internal const string PROTOCOL_NAME_V3_1_1 = "MQTT"; // [v.3.1.1]
+
+ // max length for client id (removed in 3.1.1)
internal const int CLIENT_ID_MAX_LENGTH = 23;
// variable header fields
internal const byte PROTOCOL_NAME_LEN_SIZE = 2;
- internal const byte PROTOCOL_NAME_SIZE = 6;
- internal const byte PROTOCOL_VERSION_NUMBER_SIZE = 1;
+ internal const byte PROTOCOL_NAME_V3_1_SIZE = 6;
+ internal const byte PROTOCOL_NAME_V3_1_1_SIZE = 4; // [v.3.1.1]
+ internal const byte PROTOCOL_VERSION_SIZE = 1;
internal const byte CONNECT_FLAGS_SIZE = 1;
internal const byte KEEP_ALIVE_TIME_SIZE = 2;
- internal const byte PROTOCOL_VERSION = 0x03;
+ internal const byte PROTOCOL_VERSION_V3_1 = 0x03;
+ internal const byte PROTOCOL_VERSION_V3_1_1 = 0x04; // [v.3.1.1]
internal const ushort KEEP_ALIVE_PERIOD_DEFAULT = 60; // seconds
internal const ushort MAX_KEEP_ALIVE = 65535; // 16 bit
@@ -65,6 +66,10 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
internal const byte CLEAN_SESSION_FLAG_MASK = 0x02;
internal const byte CLEAN_SESSION_FLAG_OFFSET = 0x01;
internal const byte CLEAN_SESSION_FLAG_SIZE = 0x01;
+ // [v.3.1.1] lsb (reserved) must be now 0
+ internal const byte RESERVED_FLAG_MASK = 0x01;
+ internal const byte RESERVED_FLAG_OFFSET = 0x00;
+ internal const byte RESERVED_FLAG_SIZE = 0x01;
#endregion
@@ -218,7 +223,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
///
/// Client identifier
public MqttMsgConnect(string clientId) :
- this(clientId, null, null, false, QOS_LEVEL_AT_LEAST_ONCE, false, null, null, true, KEEP_ALIVE_PERIOD_DEFAULT)
+ this(clientId, null, null, false, QOS_LEVEL_AT_LEAST_ONCE, false, null, null, true, KEEP_ALIVE_PERIOD_DEFAULT, PROTOCOL_VERSION_V3_1_1)
{
}
@@ -235,6 +240,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Will message
/// Clean sessione flag
/// Keep alive period
+ /// Protocol version
public MqttMsgConnect(string clientId,
string username,
string password,
@@ -244,7 +250,8 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
string willTopic,
string willMessage,
bool cleanSession,
- ushort keepAlivePeriod
+ ushort keepAlivePeriod,
+ byte protocolVersion
)
{
this.type = MQTT_MSG_CONNECT_TYPE;
@@ -259,15 +266,19 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
this.willMessage = willMessage;
this.cleanSession = cleanSession;
this.keepAlivePeriod = keepAlivePeriod;
+ // [v.3.1.1] added new protocol name and version
+ this.protocolVersion = protocolVersion;
+ this.protocolName = (this.protocolVersion == PROTOCOL_VERSION_V3_1_1) ? PROTOCOL_NAME_V3_1_1 : PROTOCOL_NAME_V3_1;
}
///
/// Parse bytes for a CONNECT message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// CONNECT message instance
- public static MqttMsgConnect Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgConnect Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
byte[] buffer;
int index = 0;
@@ -302,11 +313,20 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
index += protNameUtf8Length;
msg.protocolName = new String(Encoding.UTF8.GetChars(protNameUtf8));
+ // [v3.1.1] wrong protocol name
+ if (!msg.protocolName.Equals(PROTOCOL_NAME_V3_1) && !msg.protocolName.Equals(PROTOCOL_NAME_V3_1_1))
+ throw new MqttClientException(MqttClientErrorCode.InvalidProtocolName);
+
// protocol version
msg.protocolVersion = buffer[index];
- index += PROTOCOL_VERSION_NUMBER_SIZE;
+ index += PROTOCOL_VERSION_SIZE;
// connect flags
+ // [v3.1.1] check lsb (reserved) must be 0
+ if ((msg.protocolVersion == PROTOCOL_VERSION_V3_1_1) &&
+ ((buffer[index] & RESERVED_FLAG_MASK) != 0x00))
+ throw new MqttClientException(MqttClientErrorCode.InvalidConnectFlags);
+
isUsernameFlag = (buffer[index] & USERNAME_FLAG_MASK) != 0x00;
isPasswordFlag = (buffer[index] & PASSWORD_FLAG_MASK) != 0x00;
msg.willRetain = (buffer[index] & WILL_RETAIN_FLAG_MASK) != 0x00;
@@ -319,13 +339,16 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
msg.keepAlivePeriod = (ushort)((buffer[index++] << 8) & 0xFF00);
msg.keepAlivePeriod |= buffer[index++];
- // client identifier
+ // client identifier [v3.1.1] it may be zero bytes long (empty string)
clientIdUtf8Length = ((buffer[index++] << 8) & 0xFF00);
clientIdUtf8Length |= buffer[index++];
clientIdUtf8 = new byte[clientIdUtf8Length];
Array.Copy(buffer, index, clientIdUtf8, 0, clientIdUtf8Length);
index += clientIdUtf8Length;
msg.clientId = new String(Encoding.UTF8.GetChars(clientIdUtf8));
+ // [v3.1.1] if client identifier is zero bytes long, clean session must be true
+ if ((msg.protocolVersion == PROTOCOL_VERSION_V3_1_1) && (clientIdUtf8Length == 0) && (!msg.cleanSession))
+ throw new MqttClientException(MqttClientErrorCode.InvalidClientId);
// will topic and will message
if (msg.willFlag)
@@ -370,7 +393,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return msg;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte protocolVersion)
{
int fixedHeaderSize = 0;
int varHeaderSize = 0;
@@ -380,21 +403,49 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
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;
+ byte[] willTopicUtf8 = (this.willFlag && (this.willTopic != null)) ? Encoding.UTF8.GetBytes(this.willTopic) : null;
+ byte[] willMessageUtf8 = (this.willFlag && (this.willMessage != null)) ? Encoding.UTF8.GetBytes(this.willMessage) : null;
+ byte[] usernameUtf8 = ((this.username != null) && (this.username.Length > 0)) ? Encoding.UTF8.GetBytes(this.username) : null;
+ byte[] passwordUtf8 = ((this.password != null) && (this.password.Length > 0)) ? Encoding.UTF8.GetBytes(this.password) : null;
+
+ // [v3.1.1]
+ if (this.protocolVersion == PROTOCOL_VERSION_V3_1_1)
+ {
+ // will flag set, will topic and will message MUST be present
+ if (this.willFlag && ((this.willQosLevel >= 0x03) ||
+ (willTopicUtf8 == null) || (willMessageUtf8 == null) ||
+ ((willTopicUtf8 != null) && (willTopicUtf8.Length == 0)) ||
+ ((willMessageUtf8 != null) && (willMessageUtf8.Length == 0))))
+ throw new MqttClientException(MqttClientErrorCode.WillWrong);
+ // willflag not set, retain must be 0 and will topic and message MUST NOT be present
+ else if (!this.willFlag && ((this.willRetain) ||
+ (willTopicUtf8 != null) || (willMessageUtf8 != null) ||
+ ((willTopicUtf8 != null) && (willTopicUtf8.Length != 0)) ||
+ ((willMessageUtf8 != null) && (willMessageUtf8.Length != 0))))
+ throw new MqttClientException(MqttClientErrorCode.WillWrong);
+ }
- // 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);
+ // check on will QoS Level
+ if ((this.willQosLevel < MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE) ||
+ (this.willQosLevel > MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE))
+ throw new MqttClientException(MqttClientErrorCode.WillWrong);
+
// protocol name field size
- varHeaderSize += (PROTOCOL_NAME_LEN_SIZE + PROTOCOL_NAME_SIZE);
- // protocol version number field size
- varHeaderSize += PROTOCOL_VERSION_NUMBER_SIZE;
+ // MQTT version 3.1
+ if (this.protocolVersion == PROTOCOL_VERSION_V3_1)
+ {
+ varHeaderSize += (PROTOCOL_NAME_LEN_SIZE + PROTOCOL_NAME_V3_1_SIZE);
+ }
+ // MQTT version 3.1.1
+ else
+ {
+ varHeaderSize += (PROTOCOL_NAME_LEN_SIZE + PROTOCOL_NAME_V3_1_1_SIZE);
+ }
+ // protocol level field size
+ varHeaderSize += PROTOCOL_VERSION_SIZE;
// connect flags field size
varHeaderSize += CONNECT_FLAGS_SIZE;
// keep alive timer field size
@@ -429,30 +480,40 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
// first fixed header byte
- buffer[index++] = (MQTT_MSG_CONNECT_TYPE << MSG_TYPE_OFFSET);
+ buffer[index++] = (MQTT_MSG_CONNECT_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_CONNECT_FLAG_BITS; // [v.3.1.1]
// encode remaining length
index = this.encodeRemainingLength(remainingLength, buffer, index);
// protocol name
buffer[index++] = 0; // MSB protocol name size
- 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;
-
+ // MQTT version 3.1
+ if (this.protocolVersion == PROTOCOL_VERSION_V3_1)
+ {
+ buffer[index++] = PROTOCOL_NAME_V3_1_SIZE; // LSB protocol name size
+ Array.Copy(Encoding.UTF8.GetBytes(PROTOCOL_NAME_V3_1), 0, buffer, index, PROTOCOL_NAME_V3_1_SIZE);
+ index += PROTOCOL_NAME_V3_1_SIZE;
+ // protocol version
+ buffer[index++] = PROTOCOL_VERSION_V3_1;
+ }
+ // MQTT version 3.1.1
+ else
+ {
+ buffer[index++] = PROTOCOL_NAME_V3_1_1_SIZE; // LSB protocol name size
+ Array.Copy(Encoding.UTF8.GetBytes(PROTOCOL_NAME_V3_1_1), 0, buffer, index, PROTOCOL_NAME_V3_1_1_SIZE);
+ index += PROTOCOL_NAME_V3_1_1_SIZE;
+ // protocol version
+ buffer[index++] = PROTOCOL_VERSION_V3_1_1;
+ }
+
// connect flags
byte connectFlags = 0x00;
- connectFlags |= (this.username != null) ? (byte)(1 << USERNAME_FLAG_OFFSET) : (byte)0x00;
- connectFlags |= (this.password != null) ? (byte)(1 << PASSWORD_FLAG_OFFSET) : (byte)0x00;
+ connectFlags |= (usernameUtf8 != null) ? (byte)(1 << USERNAME_FLAG_OFFSET) : (byte)0x00;
+ connectFlags |= (passwordUtf8 != null) ? (byte)(1 << PASSWORD_FLAG_OFFSET) : (byte)0x00;
connectFlags |= (this.willRetain) ? (byte)(1 << WILL_RETAIN_FLAG_OFFSET) : (byte)0x00;
- connectFlags |= (byte)(this.willQosLevel << WILL_QOS_FLAG_OFFSET);
+ // only if will flag is set, we have to use will QoS level (otherwise is MUST be 0)
+ if (this.willFlag)
+ connectFlags |= (byte)(this.willQosLevel << WILL_QOS_FLAG_OFFSET);
connectFlags |= (this.willFlag) ? (byte)(1 << WILL_FLAG_OFFSET) : (byte)0x00;
connectFlags |= (this.cleanSession) ? (byte)(1 << CLEAN_SESSION_FLAG_OFFSET) : (byte)0x00;
buffer[index++] = connectFlags;
@@ -468,7 +529,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
index += clientIdUtf8.Length;
// will topic
- if (this.willFlag && (this.willTopic != null))
+ if (this.willFlag && (willTopicUtf8 != null))
{
buffer[index++] = (byte)((willTopicUtf8.Length >> 8) & 0x00FF); // MSB
buffer[index++] = (byte)(willTopicUtf8.Length & 0x00FF); // LSB
@@ -477,7 +538,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
}
// will message
- if (this.willFlag && (this.willMessage != null))
+ if (this.willFlag && (willMessageUtf8 != null))
{
buffer[index++] = (byte)((willMessageUtf8.Length >> 8) & 0x00FF); // MSB
buffer[index++] = (byte)(willMessageUtf8.Length & 0x00FF); // LSB
@@ -486,7 +547,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
}
// username
- if (this.username != null)
+ if (usernameUtf8 != null)
{
buffer[index++] = (byte)((usernameUtf8.Length >> 8) & 0x00FF); // MSB
buffer[index++] = (byte)(usernameUtf8.Length & 0x00FF); // LSB
@@ -495,7 +556,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
}
// password
- if (this.password != null)
+ if (passwordUtf8 != null)
{
buffer[index++] = (byte)((passwordUtf8.Length >> 8) & 0x00FF); // MSB
buffer[index++] = (byte)(passwordUtf8.Length & 0x00FF); // LSB
@@ -505,5 +566,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return buffer;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "CONNECT",
+ new object[] { "protocolName", "protocolVersion", "clientId", "willFlag", "willRetain", "willQosLevel", "willTopic", "willMessage", "username", "password", "cleanSession", "keepAlivePeriod" },
+ new object[] { this.protocolName, this.protocolVersion, this.clientId, this.willFlag, this.willRetain, this.willQosLevel, this.willTopic, this.willMessage, this.username, this.password, this.cleanSession, this.keepAlivePeriod });
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgConnectEventArgs.cs b/M2Mqtt/Messages/MqttMsgConnectEventArgs.cs
index 6c901e1..ce0b3fe 100644
--- a/M2Mqtt/Messages/MqttMsgConnectEventArgs.cs
+++ b/M2Mqtt/Messages/MqttMsgConnectEventArgs.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
diff --git a/M2Mqtt/Messages/MqttMsgContext.cs b/M2Mqtt/Messages/MqttMsgContext.cs
index 204703f..a2850d7 100644
--- a/M2Mqtt/Messages/MqttMsgContext.cs
+++ b/M2Mqtt/Messages/MqttMsgContext.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
@@ -50,6 +48,14 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Attempt (for retry)
///
public int Attempt { get; set; }
+
+ ///
+ /// Unique key
+ ///
+ public string Key
+ {
+ get { return this.Flow + "_" + this.Message.MessageId; }
+ }
}
///
@@ -128,6 +134,18 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
///
SendPuback,
+ // [v3.1.1] SUBSCRIBE isn't "officially" QOS = 1
+ ///
+ /// Send SUBSCRIBE message
+ ///
+ SendSubscribe,
+
+ // [v3.1.1] UNSUBSCRIBE isn't "officially" QOS = 1
+ ///
+ /// Send UNSUBSCRIBE message
+ ///
+ SendUnsubscribe,
+
///
/// (QOS = 1), SUBSCRIBE sent, wait for SUBACK
///
diff --git a/M2Mqtt/Messages/MqttMsgDisconnect.cs b/M2Mqtt/Messages/MqttMsgDisconnect.cs
index 8c4eb7e..d918473 100644
--- a/M2Mqtt/Messages/MqttMsgDisconnect.cs
+++ b/M2Mqtt/Messages/MqttMsgDisconnect.cs
@@ -1,21 +1,20 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
+using uPLibrary.Networking.M2Mqtt.Exceptions;
namespace uPLibrary.Networking.M2Mqtt.Messages
{
@@ -36,12 +35,20 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Parse bytes for a DISCONNECT message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// DISCONNECT message instance
- public static MqttMsgDisconnect Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgDisconnect Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
MqttMsgDisconnect msg = new MqttMsgDisconnect();
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ {
+ // [v3.1.1] check flag bits
+ if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_DISCONNECT_FLAG_BITS)
+ throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
+ }
+
// get remaining length and allocate buffer
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
// NOTE : remainingLength must be 0
@@ -49,16 +56,31 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return msg;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte protocolVersion)
{
byte[] buffer = new byte[2];
int index = 0;
// first fixed header byte
- buffer[index++] = (MQTT_MSG_DISCONNECT_TYPE << MSG_TYPE_OFFSET);
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ buffer[index++] = (MQTT_MSG_DISCONNECT_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_DISCONNECT_FLAG_BITS; // [v.3.1.1]
+ else
+ buffer[index++] = (MQTT_MSG_DISCONNECT_TYPE << MSG_TYPE_OFFSET);
buffer[index++] = 0x00;
return buffer;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "DISCONNECT",
+ null,
+ null);
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgPingReq.cs b/M2Mqtt/Messages/MqttMsgPingReq.cs
index e8f4efb..b46bdb6 100644
--- a/M2Mqtt/Messages/MqttMsgPingReq.cs
+++ b/M2Mqtt/Messages/MqttMsgPingReq.cs
@@ -1,21 +1,20 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
+using uPLibrary.Networking.M2Mqtt.Exceptions;
namespace uPLibrary.Networking.M2Mqtt.Messages
{
@@ -32,13 +31,16 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
this.type = MQTT_MSG_PINGREQ_TYPE;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte protocolVersion)
{
byte[] buffer = new byte[2];
int index = 0;
// first fixed header byte
- buffer[index++] = (MQTT_MSG_PINGREQ_TYPE << MSG_TYPE_OFFSET);
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ buffer[index++] = (MQTT_MSG_PINGREQ_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PINGREQ_FLAG_BITS; // [v.3.1.1]
+ else
+ buffer[index++] = (MQTT_MSG_PINGREQ_TYPE << MSG_TYPE_OFFSET);
buffer[index++] = 0x00;
return buffer;
@@ -48,17 +50,37 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Parse bytes for a PINGREQ message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// PINGREQ message instance
- public static MqttMsgPingReq Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgPingReq Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
MqttMsgPingReq msg = new MqttMsgPingReq();
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ {
+ // [v3.1.1] check flag bits
+ if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PINGREQ_FLAG_BITS)
+ throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
+ }
+
// already know remaininglength is zero (MQTT specification),
// so it isn't necessary to read other data from socket
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
return msg;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "PINGREQ",
+ null,
+ null);
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgPingResp.cs b/M2Mqtt/Messages/MqttMsgPingResp.cs
index 988edcf..2b028b9 100644
--- a/M2Mqtt/Messages/MqttMsgPingResp.cs
+++ b/M2Mqtt/Messages/MqttMsgPingResp.cs
@@ -1,22 +1,21 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
+using uPLibrary.Networking.M2Mqtt.Exceptions;
namespace uPLibrary.Networking.M2Mqtt.Messages
{
@@ -37,12 +36,20 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Parse bytes for a PINGRESP message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// PINGRESP message instance
- public static MqttMsgPingResp Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgPingResp Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
MqttMsgPingResp msg = new MqttMsgPingResp();
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ {
+ // [v3.1.1] check flag bits
+ if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PINGRESP_FLAG_BITS)
+ throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
+ }
+
// already know remaininglength is zero (MQTT specification),
// so it isn't necessary to read other data from socket
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
@@ -50,16 +57,31 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return msg;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte protocolVersion)
{
byte[] buffer = new byte[2];
int index = 0;
// first fixed header byte
- buffer[index++] = (MQTT_MSG_PINGRESP_TYPE << MSG_TYPE_OFFSET);
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ buffer[index++] = (MQTT_MSG_PINGRESP_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PINGRESP_FLAG_BITS; // [v.3.1.1]
+ else
+ buffer[index++] = (MQTT_MSG_PINGRESP_TYPE << MSG_TYPE_OFFSET);
buffer[index++] = 0x00;
return buffer;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "PINGRESP",
+ null,
+ null);
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgPuback.cs b/M2Mqtt/Messages/MqttMsgPuback.cs
index 646746c..e00e37f 100644
--- a/M2Mqtt/Messages/MqttMsgPuback.cs
+++ b/M2Mqtt/Messages/MqttMsgPuback.cs
@@ -1,21 +1,21 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
+using uPLibrary.Networking.M2Mqtt.Exceptions;
+
namespace uPLibrary.Networking.M2Mqtt.Messages
{
///
@@ -23,23 +23,6 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
///
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
///
@@ -48,7 +31,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
this.type = MQTT_MSG_PUBACK_TYPE;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte protocolVersion)
{
int fixedHeaderSize = 0;
int varHeaderSize = 0;
@@ -78,7 +61,10 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
// first fixed header byte
- buffer[index++] = (MQTT_MSG_PUBACK_TYPE << MSG_TYPE_OFFSET);
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ buffer[index++] = (MQTT_MSG_PUBACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBACK_FLAG_BITS; // [v.3.1.1]
+ else
+ buffer[index++] = (MQTT_MSG_PUBACK_TYPE << MSG_TYPE_OFFSET);
// encode remaining length
index = this.encodeRemainingLength(remainingLength, buffer, index);
@@ -94,14 +80,22 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Parse bytes for a PUBACK message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// PUBACK message instance
- public static MqttMsgPuback Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgPuback Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
byte[] buffer;
int index = 0;
MqttMsgPuback msg = new MqttMsgPuback();
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ {
+ // [v3.1.1] check flag bits
+ if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBACK_FLAG_BITS)
+ throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
+ }
+
// get remaining length and allocate buffer
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
buffer = new byte[remainingLength];
@@ -115,5 +109,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return msg;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "PUBACK",
+ new object[] { "messageId" },
+ new object[] { this.messageId });
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgPubcomp.cs b/M2Mqtt/Messages/MqttMsgPubcomp.cs
index ca53667..eeabd3b 100644
--- a/M2Mqtt/Messages/MqttMsgPubcomp.cs
+++ b/M2Mqtt/Messages/MqttMsgPubcomp.cs
@@ -1,21 +1,21 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
+using uPLibrary.Networking.M2Mqtt.Exceptions;
+
namespace uPLibrary.Networking.M2Mqtt.Messages
{
///
@@ -23,22 +23,6 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
///
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
///
@@ -47,7 +31,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
this.type = MQTT_MSG_PUBCOMP_TYPE;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte protocolVersion)
{
int fixedHeaderSize = 0;
int varHeaderSize = 0;
@@ -77,7 +61,10 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
// first fixed header byte
- buffer[index++] = (MQTT_MSG_PUBCOMP_TYPE << MSG_TYPE_OFFSET);
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ buffer[index++] = (MQTT_MSG_PUBCOMP_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBCOMP_FLAG_BITS; // [v.3.1.1]
+ else
+ buffer[index++] = (MQTT_MSG_PUBCOMP_TYPE << MSG_TYPE_OFFSET);
// encode remaining length
index = this.encodeRemainingLength(remainingLength, buffer, index);
@@ -93,14 +80,22 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Parse bytes for a PUBCOMP message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// PUBCOMP message instance
- public static MqttMsgPubcomp Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgPubcomp Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
byte[] buffer;
int index = 0;
MqttMsgPubcomp msg = new MqttMsgPubcomp();
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ {
+ // [v3.1.1] check flag bits
+ if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBCOMP_FLAG_BITS)
+ throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
+ }
+
// get remaining length and allocate buffer
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
buffer = new byte[remainingLength];
@@ -114,5 +109,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return msg;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "PUBCOMP",
+ new object[] { "messageId" },
+ new object[] { this.messageId });
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgPublish.cs b/M2Mqtt/Messages/MqttMsgPublish.cs
index 8ee80b5..6bbf207 100644
--- a/M2Mqtt/Messages/MqttMsgPublish.cs
+++ b/M2Mqtt/Messages/MqttMsgPublish.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
@@ -47,24 +45,13 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
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
///
@@ -107,7 +94,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
this.messageId = 0;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte protocolVersion)
{
int fixedHeaderSize = 0;
int varHeaderSize = 0;
@@ -124,6 +111,10 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
if ((this.topic.Length < MIN_TOPIC_LENGTH) || (this.topic.Length > MAX_TOPIC_LENGTH))
throw new MqttClientException(MqttClientErrorCode.TopicLength);
+ // check wrong QoS level (both bits can't be set 1)
+ if (this.qosLevel > QOS_LEVEL_EXACTLY_ONCE)
+ throw new MqttClientException(MqttClientErrorCode.QosNotAllowed);
+
byte[] topicUtf8 = Encoding.UTF8.GetBytes(this.topic);
// topic name
@@ -200,9 +191,10 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Parse bytes for a PUBLISH message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// PUBLISH message instance
- public static MqttMsgPublish Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgPublish Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
byte[] buffer;
int index = 0;
@@ -227,6 +219,9 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
// read QoS level from fixed header
msg.qosLevel = (byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET);
+ // check wrong QoS level (both bits can't be set 1)
+ if (msg.qosLevel > QOS_LEVEL_EXACTLY_ONCE)
+ throw new MqttClientException(MqttClientErrorCode.QosNotAllowed);
// read DUP flag from fixed header
msg.dupFlag = (((fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET) == 0x01);
// read retain flag from fixed header
@@ -266,5 +261,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return msg;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "PUBLISH",
+ new object[] { "messageId", "topic", "message" },
+ new object[] { this.messageId, this.topic, this.message });
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgPublishEventArgs.cs b/M2Mqtt/Messages/MqttMsgPublishEventArgs.cs
index 5954d91..efb2333 100644
--- a/M2Mqtt/Messages/MqttMsgPublishEventArgs.cs
+++ b/M2Mqtt/Messages/MqttMsgPublishEventArgs.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
diff --git a/M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs b/M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs
index 811d946..e507c38 100644
--- a/M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs
+++ b/M2Mqtt/Messages/MqttMsgPublishedEventArgs.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
@@ -40,18 +38,41 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
internal set { this.messageId = value; }
}
+ ///
+ /// Message published (or failed due to retries)
+ ///
+ public bool IsPublished
+ {
+ get { return this.isPublished; }
+ internal set { this.isPublished = value; }
+ }
+
#endregion
// message identifier
ushort messageId;
+ // published flag
+ bool isPublished;
+
+ ///
+ /// Constructor (published message)
+ ///
+ /// Message identifier published
+ public MqttMsgPublishedEventArgs(ushort messageId)
+ : this(messageId, true)
+ {
+ }
+
///
/// Constructor
///
- /// Message identifier published
- public MqttMsgPublishedEventArgs(ushort messageId)
+ /// Message identifier
+ /// Publish flag
+ public MqttMsgPublishedEventArgs(ushort messageId, bool isPublished)
{
this.messageId = messageId;
+ this.isPublished = isPublished;
}
}
}
diff --git a/M2Mqtt/Messages/MqttMsgPubrec.cs b/M2Mqtt/Messages/MqttMsgPubrec.cs
index 05ce84a..1855bf9 100644
--- a/M2Mqtt/Messages/MqttMsgPubrec.cs
+++ b/M2Mqtt/Messages/MqttMsgPubrec.cs
@@ -1,21 +1,21 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
+using uPLibrary.Networking.M2Mqtt.Exceptions;
+
namespace uPLibrary.Networking.M2Mqtt.Messages
{
///
@@ -23,22 +23,6 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
///
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
///
@@ -47,7 +31,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
this.type = MQTT_MSG_PUBREC_TYPE;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte protocolVersion)
{
int fixedHeaderSize = 0;
int varHeaderSize = 0;
@@ -77,7 +61,10 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
// first fixed header byte
- buffer[index++] = (MQTT_MSG_PUBREC_TYPE << MSG_TYPE_OFFSET);
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ buffer[index++] = (MQTT_MSG_PUBREC_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBREC_FLAG_BITS; // [v.3.1.1]
+ else
+ buffer[index++] = (MQTT_MSG_PUBREC_TYPE << MSG_TYPE_OFFSET);
// encode remaining length
index = this.encodeRemainingLength(remainingLength, buffer, index);
@@ -93,14 +80,22 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Parse bytes for a PUBREC message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// PUBREC message instance
- public static MqttMsgPubrec Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgPubrec Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
byte[] buffer;
int index = 0;
MqttMsgPubrec msg = new MqttMsgPubrec();
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ {
+ // [v3.1.1] check flag bits
+ if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBREC_FLAG_BITS)
+ throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
+ }
+
// get remaining length and allocate buffer
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
buffer = new byte[remainingLength];
@@ -114,5 +109,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return msg;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "PUBREC",
+ new object[] { "messageId" },
+ new object[] { this.messageId });
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgPubrel.cs b/M2Mqtt/Messages/MqttMsgPubrel.cs
index 25c10a2..e43ca91 100644
--- a/M2Mqtt/Messages/MqttMsgPubrel.cs
+++ b/M2Mqtt/Messages/MqttMsgPubrel.cs
@@ -1,21 +1,21 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
+using uPLibrary.Networking.M2Mqtt.Exceptions;
+
namespace uPLibrary.Networking.M2Mqtt.Messages
{
///
@@ -23,33 +23,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
///
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
+ // PUBREL message use QoS Level 1 (not "officially" in 3.1.1)
this.qosLevel = QOS_LEVEL_AT_LEAST_ONCE;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte protocolVersion)
{
int fixedHeaderSize = 0;
int varHeaderSize = 0;
@@ -79,10 +63,15 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
// first fixed header byte
- buffer[index] = (byte)((MQTT_MSG_PUBREL_TYPE << MSG_TYPE_OFFSET) |
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ buffer[index++] = (MQTT_MSG_PUBREL_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_PUBREL_FLAG_BITS; // [v.3.1.1]
+ else
+ {
+ buffer[index] = (byte)((MQTT_MSG_PUBREL_TYPE << MSG_TYPE_OFFSET) |
(this.qosLevel << QOS_LEVEL_OFFSET));
- buffer[index] |= this.dupFlag ? (byte)(1 << DUP_FLAG_OFFSET) : (byte)0x00;
- index++;
+ buffer[index] |= this.dupFlag ? (byte)(1 << DUP_FLAG_OFFSET) : (byte)0x00;
+ index++;
+ }
// encode remaining length
index = this.encodeRemainingLength(remainingLength, buffer, index);
@@ -98,14 +87,22 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Parse bytes for a PUBREL message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// PUBREL message instance
- public static MqttMsgPubrel Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgPubrel Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
byte[] buffer;
int index = 0;
MqttMsgPubrel msg = new MqttMsgPubrel();
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ {
+ // [v3.1.1] check flag bits
+ if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_PUBREL_FLAG_BITS)
+ throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
+ }
+
// get remaining length and allocate buffer
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
buffer = new byte[remainingLength];
@@ -113,10 +110,15 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
// 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);
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1)
+ {
+ // only 3.1.0
+
+ // read QoS level from fixed header (would be QoS Level 1)
+ msg.qosLevel = (byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET);
+ // read DUP flag from fixed header
+ msg.dupFlag = (((fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET) == 0x01);
+ }
// message id
msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00);
@@ -124,5 +126,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return msg;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "PUBREL",
+ new object[] { "messageId" },
+ new object[] { this.messageId });
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgSuback.cs b/M2Mqtt/Messages/MqttMsgSuback.cs
index 5b847ff..6ecfd0d 100644
--- a/M2Mqtt/Messages/MqttMsgSuback.cs
+++ b/M2Mqtt/Messages/MqttMsgSuback.cs
@@ -1,22 +1,21 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
+using uPLibrary.Networking.M2Mqtt.Exceptions;
namespace uPLibrary.Networking.M2Mqtt.Messages
{
@@ -27,16 +26,6 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
{
#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
///
@@ -48,8 +37,6 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
#endregion
- // message identifier
- private ushort messageId;
// granted QOS levels
byte[] grantedQosLevels;
@@ -65,14 +52,22 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Parse bytes for a SUBACK message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// SUBACK message instance
- public static MqttMsgSuback Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgSuback Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
byte[] buffer;
int index = 0;
MqttMsgSuback msg = new MqttMsgSuback();
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ {
+ // [v3.1.1] check flag bits
+ if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_SUBACK_FLAG_BITS)
+ throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
+ }
+
// get remaining length and allocate buffer
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
buffer = new byte[remainingLength];
@@ -95,7 +90,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return msg;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte protocolVersion)
{
int fixedHeaderSize = 0;
int varHeaderSize = 0;
@@ -131,9 +126,11 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
// first fixed header byte
- buffer[index] = (byte)(MQTT_MSG_SUBACK_TYPE << MSG_TYPE_OFFSET);
- index++;
-
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ buffer[index++] = (MQTT_MSG_SUBACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_SUBACK_FLAG_BITS; // [v.3.1.1]
+ else
+ buffer[index++] = (byte)(MQTT_MSG_SUBACK_TYPE << MSG_TYPE_OFFSET);
+
// encode remaining length
index = this.encodeRemainingLength(remainingLength, buffer, index);
@@ -149,5 +146,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return buffer;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "SUBACK",
+ new object[] { "messageId", "grantedQosLevels" },
+ new object[] { this.messageId, this.grantedQosLevels });
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgSubscribe.cs b/M2Mqtt/Messages/MqttMsgSubscribe.cs
index 478ee2e..0e3927e 100644
--- a/M2Mqtt/Messages/MqttMsgSubscribe.cs
+++ b/M2Mqtt/Messages/MqttMsgSubscribe.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
@@ -52,24 +50,13 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
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
///
@@ -90,7 +77,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
this.topics = topics;
this.qosLevels = qosLevels;
- // SUBSCRIBE message uses QoS Level 1
+ // SUBSCRIBE message uses QoS Level 1 (not "officially" in 3.1.1)
this.qosLevel = QOS_LEVEL_AT_LEAST_ONCE;
}
@@ -98,9 +85,10 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Parse bytes for a SUBSCRIBE message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// SUBSCRIBE message instance
- public static MqttMsgSubscribe Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgSubscribe Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
byte[] buffer;
int index = 0;
@@ -108,6 +96,13 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
int topicUtf8Length;
MqttMsgSubscribe msg = new MqttMsgSubscribe();
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ {
+ // [v3.1.1] check flag bits
+ if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_SUBSCRIBE_FLAG_BITS)
+ throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
+ }
+
// get remaining length and allocate buffer
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
buffer = new byte[remainingLength];
@@ -115,12 +110,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
// 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;
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1)
+ {
+ // only 3.1.0
+
+ // read QoS level from fixed header
+ msg.qosLevel = (byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET);
+ // read DUP flag from fixed header
+ msg.dupFlag = (((fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET) == 0x01);
+ // retain flag not used
+ msg.retain = false;
+ }
// message id
msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00);
@@ -165,7 +165,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return msg;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte protocolVersion)
{
int fixedHeaderSize = 0;
int varHeaderSize = 0;
@@ -222,11 +222,16 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
// first fixed header byte
- buffer[index] = (byte)((MQTT_MSG_SUBSCRIBE_TYPE << MSG_TYPE_OFFSET) |
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ buffer[index++] = (MQTT_MSG_SUBSCRIBE_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_SUBSCRIBE_FLAG_BITS; // [v.3.1.1]
+ else
+ {
+ buffer[index] = (byte)((MQTT_MSG_SUBSCRIBE_TYPE << MSG_TYPE_OFFSET) |
(this.qosLevel << QOS_LEVEL_OFFSET));
- buffer[index] |= this.dupFlag ? (byte)(1 << DUP_FLAG_OFFSET) : (byte)0x00;
- index++;
-
+ buffer[index] |= this.dupFlag ? (byte)(1 << DUP_FLAG_OFFSET) : (byte)0x00;
+ index++;
+ }
+
// encode remaining length
index = this.encodeRemainingLength(remainingLength, buffer, index);
@@ -251,5 +256,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return buffer;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "SUBSCRIBE",
+ new object[] { "messageId", "topics", "qosLevels" },
+ new object[] { this.messageId, this.topics, this.qosLevels });
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs b/M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs
index 92a5824..91c85f9 100644
--- a/M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs
+++ b/M2Mqtt/Messages/MqttMsgSubscribeEventArgs.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
diff --git a/M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs b/M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs
index 3b6daab..58bbb22 100644
--- a/M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs
+++ b/M2Mqtt/Messages/MqttMsgSubscribedEventArgs.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
diff --git a/M2Mqtt/Messages/MqttMsgUnsuback.cs b/M2Mqtt/Messages/MqttMsgUnsuback.cs
index 7c1a810..8e49a06 100644
--- a/M2Mqtt/Messages/MqttMsgUnsuback.cs
+++ b/M2Mqtt/Messages/MqttMsgUnsuback.cs
@@ -1,22 +1,21 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
+using uPLibrary.Networking.M2Mqtt.Exceptions;
namespace uPLibrary.Networking.M2Mqtt.Messages
{
@@ -25,23 +24,6 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
///
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
///
@@ -54,14 +36,22 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Parse bytes for a UNSUBACK message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// UNSUBACK message instance
- public static MqttMsgUnsuback Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgUnsuback Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
byte[] buffer;
int index = 0;
MqttMsgUnsuback msg = new MqttMsgUnsuback();
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ {
+ // [v3.1.1] check flag bits
+ if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_UNSUBACK_FLAG_BITS)
+ throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
+ }
+
// get remaining length and allocate buffer
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
buffer = new byte[remainingLength];
@@ -76,7 +66,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return msg;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte protocolVersion)
{
int fixedHeaderSize = 0;
int varHeaderSize = 0;
@@ -106,9 +96,11 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
// first fixed header byte
- buffer[index] = (byte)(MQTT_MSG_UNSUBACK_TYPE << MSG_TYPE_OFFSET);
- index++;
-
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ buffer[index++] = (MQTT_MSG_UNSUBACK_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_UNSUBACK_FLAG_BITS; // [v.3.1.1]
+ else
+ buffer[index++] = (byte)(MQTT_MSG_UNSUBACK_TYPE << MSG_TYPE_OFFSET);
+
// encode remaining length
index = this.encodeRemainingLength(remainingLength, buffer, index);
@@ -118,5 +110,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return buffer;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "UNSUBACK",
+ new object[] { "messageId" },
+ new object[] { this.messageId });
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgUnsubscribe.cs b/M2Mqtt/Messages/MqttMsgUnsubscribe.cs
index b2f53c2..e485133 100644
--- a/M2Mqtt/Messages/MqttMsgUnsubscribe.cs
+++ b/M2Mqtt/Messages/MqttMsgUnsubscribe.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
@@ -43,22 +41,11 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
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
///
@@ -77,7 +64,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
this.topics = topics;
- // UNSUBSCRIBE message uses QoS Level 1
+ // UNSUBSCRIBE message uses QoS Level 1 (not "officially" in 3.1.1)
this.qosLevel = QOS_LEVEL_AT_LEAST_ONCE;
}
@@ -85,9 +72,10 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
/// Parse bytes for a UNSUBSCRIBE message
///
/// First fixed header byte
+ /// Protocol Version
/// Channel connected to the broker
/// UNSUBSCRIBE message instance
- public static MqttMsgUnsubscribe Parse(byte fixedHeaderFirstByte, IMqttNetworkChannel channel)
+ public static MqttMsgUnsubscribe Parse(byte fixedHeaderFirstByte, byte protocolVersion, IMqttNetworkChannel channel)
{
byte[] buffer;
int index = 0;
@@ -95,6 +83,13 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
int topicUtf8Length;
MqttMsgUnsubscribe msg = new MqttMsgUnsubscribe();
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ {
+ // [v3.1.1] check flag bits
+ if ((fixedHeaderFirstByte & MSG_FLAG_BITS_MASK) != MQTT_MSG_UNSUBSCRIBE_FLAG_BITS)
+ throw new MqttClientException(MqttClientErrorCode.InvalidFlagBits);
+ }
+
// get remaining length and allocate buffer
int remainingLength = MqttMsgBase.decodeRemainingLength(channel);
buffer = new byte[remainingLength];
@@ -102,12 +97,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
// 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;
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1)
+ {
+ // only 3.1.0
+
+ // read QoS level from fixed header
+ msg.qosLevel = (byte)((fixedHeaderFirstByte & QOS_LEVEL_MASK) >> QOS_LEVEL_OFFSET);
+ // read DUP flag from fixed header
+ msg.dupFlag = (((fixedHeaderFirstByte & DUP_FLAG_MASK) >> DUP_FLAG_OFFSET) == 0x01);
+ // retain flag not used
+ msg.retain = false;
+ }
// message id
msg.messageId = (ushort)((buffer[index++] << 8) & 0xFF00);
@@ -144,7 +144,7 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return msg;
}
- public override byte[] GetBytes()
+ public override byte[] GetBytes(byte protocolVersion)
{
int fixedHeaderSize = 0;
int varHeaderSize = 0;
@@ -192,11 +192,16 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
buffer = new byte[fixedHeaderSize + varHeaderSize + payloadSize];
// first fixed header byte
- buffer[index] = (byte)((MQTT_MSG_UNSUBSCRIBE_TYPE << MSG_TYPE_OFFSET) |
+ if (protocolVersion == MqttMsgConnect.PROTOCOL_VERSION_V3_1_1)
+ buffer[index++] = (MQTT_MSG_UNSUBSCRIBE_TYPE << MSG_TYPE_OFFSET) | MQTT_MSG_UNSUBSCRIBE_FLAG_BITS; // [v.3.1.1]
+ else
+ {
+ buffer[index] = (byte)((MQTT_MSG_UNSUBSCRIBE_TYPE << MSG_TYPE_OFFSET) |
(this.qosLevel << QOS_LEVEL_OFFSET));
- buffer[index] |= this.dupFlag ? (byte)(1 << DUP_FLAG_OFFSET) : (byte)0x00;
- index++;
-
+ buffer[index] |= this.dupFlag ? (byte)(1 << DUP_FLAG_OFFSET) : (byte)0x00;
+ index++;
+ }
+
// encode remaining length
index = this.encodeRemainingLength(remainingLength, buffer, index);
@@ -218,5 +223,17 @@ namespace uPLibrary.Networking.M2Mqtt.Messages
return buffer;
}
+
+ public override string ToString()
+ {
+#if TRACE
+ return this.GetTraceString(
+ "UNSUBSCRIBE",
+ new object[] { "messageId", "topics" },
+ new object[] { this.messageId, this.topics });
+#else
+ return base.ToString();
+#endif
+ }
}
}
diff --git a/M2Mqtt/Messages/MqttMsgUnsubscribeEventArgs.cs b/M2Mqtt/Messages/MqttMsgUnsubscribeEventArgs.cs
index 5f1dd74..e158783 100644
--- a/M2Mqtt/Messages/MqttMsgUnsubscribeEventArgs.cs
+++ b/M2Mqtt/Messages/MqttMsgUnsubscribeEventArgs.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
diff --git a/M2Mqtt/Messages/MqttMsgUnsubscribedEventArgs.cs b/M2Mqtt/Messages/MqttMsgUnsubscribedEventArgs.cs
index 99d8791..e65c383 100644
--- a/M2Mqtt/Messages/MqttMsgUnsubscribedEventArgs.cs
+++ b/M2Mqtt/Messages/MqttMsgUnsubscribedEventArgs.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
diff --git a/M2Mqtt/MqttClient.cs b/M2Mqtt/MqttClient.cs
index 29a6696..e5a2415 100644
--- a/M2Mqtt/MqttClient.cs
+++ b/M2Mqtt/MqttClient.cs
@@ -1,28 +1,31 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
using System.Net;
+#if !(WINDOWS_APP || WINDOWS_PHONE_APP)
using System.Net.Sockets;
+using System.Security.Cryptography.X509Certificates;
+#endif
using System.Threading;
using uPLibrary.Networking.M2Mqtt.Exceptions;
using uPLibrary.Networking.M2Mqtt.Messages;
-using System.Collections;
+using uPLibrary.Networking.M2Mqtt.Session;
+using uPLibrary.Networking.M2Mqtt.Utility;
+using uPLibrary.Networking.M2Mqtt.Internal;
// if .Net Micro Framework
#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
using Microsoft.SPOT;
@@ -31,14 +34,24 @@ using Microsoft.SPOT.Net.Security;
#endif
// else other frameworks (.Net, .Net Compact, Mono, Windows Phone)
#else
-
-#if (SSL && !WINDOWS_PHONE)
+using System.Collections.Generic;
+#if (SSL && !(WINDOWS_APP || WINDOWS_PHONE_APP))
using System.Security.Authentication;
using System.Net.Security;
#endif
#endif
-using System.Security.Cryptography.X509Certificates;
+#if (WINDOWS_APP || WINDOWS_PHONE_APP)
+using Windows.Networking.Sockets;
+#endif
+
+using System.Collections;
+
+// alias needed due to Microsoft.SPOT.Trace in .Net Micro Framework
+// (it's ambiguos with uPLibrary.Networking.M2Mqtt.Utility.Trace)
+using MqttUtility = uPLibrary.Networking.M2Mqtt.Utility;
+using System.IO;
+using System.Net.Security;
namespace uPLibrary.Networking.M2Mqtt
{
@@ -52,7 +65,7 @@ namespace uPLibrary.Networking.M2Mqtt
// thread names
private const string RECEIVE_THREAD_NAME = "ReceiveThread";
- private const string RECEIVE_EVENT_THREAD_NAME = "ReceiveEventThread";
+ private const string RECEIVE_EVENT_THREAD_NAME = "DispatchEventThread";
private const string PROCESS_INFLIGHT_THREAD_NAME = "ProcessInflightThread";
private const string KEEP_ALIVE_THREAD = "KeepAliveThread";
@@ -94,33 +107,27 @@ namespace uPLibrary.Networking.M2Mqtt
/// 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);
+#endif
- // CA certificate
- private X509Certificate caCert;
+ ///
+ /// Delegate that defines event handler for cliet/peer disconnection
+ ///
+ public delegate void ConnectionClosedEventHandler(object sender, EventArgs e);
- // broker hostname, ip address and port
+ // broker hostname (or 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;
+ // running status of threads
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;
@@ -134,13 +141,11 @@ namespace uPLibrary.Networking.M2Mqtt
// keep alive period (in ms)
private int keepAlivePeriod;
- // thread for sending keep alive message
- private Thread keepAliveThread;
+ // events for signaling on keep alive thread
private AutoResetEvent keepAliveEvent;
- // keep alive timeout expired
- private bool isKeepAliveTimeout;
+ private AutoResetEvent keepAliveEventEnd;
// last communication time in ticks
- private long lastCommTime;
+ private int lastCommTime;
// event for PUBLISH message received
public event MqttMsgPublishEventHandler MqttMsgPublishReceived;
@@ -157,10 +162,13 @@ namespace uPLibrary.Networking.M2Mqtt
public event MqttMsgUnsubscribeEventHandler MqttMsgUnsubscribeReceived;
// event for CONNECT message received
public event MqttMsgConnectEventHandler MqttMsgConnected;
-#endif
- // event for client disconnection (DISCONNECT message or not)
+ // event for DISCONNECT message received
public event MqttMsgDisconnectEventHandler MqttMsgDisconnected;
+#endif
+ // event for peer/client disconnection
+ public event ConnectionClosedEventHandler ConnectionClosed;
+
// channel to communicate over the network
private IMqttNetworkChannel channel;
@@ -168,8 +176,10 @@ namespace uPLibrary.Networking.M2Mqtt
private Queue inflightQueue;
// internal queue for received messages about inflight messages
private Queue internalQueue;
- // receive queue for received messages
- private Queue receiveQueue;
+ // internal queue for dispatching events
+ private Queue eventQueue;
+ // session
+ private MqttClientSession session;
// reference to avoid access to singleton via property
private MqttSettings settings;
@@ -177,6 +187,9 @@ namespace uPLibrary.Networking.M2Mqtt
// current message identifier generated
private ushort messageIdCounter = 0;
+ // connection is closing due to peer
+ private bool isConnectionClosing;
+
///
/// Connection status between client and broker
///
@@ -212,12 +225,38 @@ namespace uPLibrary.Networking.M2Mqtt
///
public string WillMessage { get; private set; }
+ ///
+ /// MQTT protocol version
+ ///
+ public MqttProtocolVersion ProtocolVersion { get; set; }
+
+#if BROKER
+ ///
+ /// MQTT Client Session
+ ///
+ public MqttClientSession Session
+ {
+ get { return this.session; }
+ set { this.session = value; }
+ }
+#endif
+
+ ///
+ /// MQTT client settings
+ ///
+ public MqttSettings Settings
+ {
+ get { return this.settings; }
+ }
+
+#if !(WINDOWS_APP || WINDOWS_PHONE_APP)
///
/// Constructor
///
/// Broker IP address
+ [Obsolete("Use this ctor MqttClient(string brokerHostName) insted")]
public MqttClient(IPAddress brokerIpAddress) :
- this(brokerIpAddress, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, null)
+ this(brokerIpAddress, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, null, null, MqttSslProtocols.None)
{
}
@@ -228,57 +267,121 @@ namespace uPLibrary.Networking.M2Mqtt
/// Broker port
/// Using secure connection
/// CA certificate for secure connection
- public MqttClient(IPAddress brokerIpAddress, int brokerPort, bool secure, X509Certificate caCert)
+ /// Client certificate
+ /// SSL/TLS protocol version
+ [Obsolete("Use this ctor MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert) insted")]
+ public MqttClient(IPAddress brokerIpAddress, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol)
{
- this.Init(null, brokerIpAddress, brokerPort, secure, caCert);
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ this.Init(brokerIpAddress.ToString(), brokerPort, secure, caCert, clientCert, sslProtocol, null, null);
+#else
+ this.Init(brokerIpAddress.ToString(), brokerPort, secure, caCert, clientCert, sslProtocol);
+#endif
}
+#endif
///
/// Constructor
///
- /// Broker Host Name
+ /// Broker Host Name or IP Address
public MqttClient(string brokerHostName) :
- this(brokerHostName, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, null)
+#if !(WINDOWS_APP || WINDOWS_PHONE_APP)
+ this(brokerHostName, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, null, null, MqttSslProtocols.None)
+#else
+ this(brokerHostName, MqttSettings.MQTT_BROKER_DEFAULT_PORT, false, MqttSslProtocols.None)
+#endif
{
}
///
/// Constructor
///
- /// Broker Host Name
+ /// Broker Host Name or IP Address
+ /// Broker port
+ /// Using secure connection
+ /// SSL/TLS protocol version
+#if !(WINDOWS_APP || WINDOWS_PHONE_APP)
+ /// CA certificate for secure connection
+ /// Client certificate
+ public MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol)
+#else
+ public MqttClient(string brokerHostName, int brokerPort, bool secure, MqttSslProtocols sslProtocol)
+#endif
+ {
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP)
+ this.Init(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol, null, null);
+#elif (WINDOWS_APP || WINDOWS_PHONE_APP)
+ this.Init(brokerHostName, brokerPort, secure, sslProtocol);
+#else
+ this.Init(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol);
+#endif
+ }
+
+
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP)
+
+ ///
+ /// Constructor
+ ///
+ /// Broker Host Name or IP Address
/// Broker port
/// Using secure connection
/// CA certificate for secure connection
- public MqttClient(string brokerHostName, int brokerPort = MqttSettings.MQTT_BROKER_DEFAULT_PORT, bool secure = false, X509Certificate caCert = null, bool skipIdAdressResolution = false)
+ /// Client certificate
+ /// SSL/TLS protocol version
+ /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party
+ public MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol,
+ RemoteCertificateValidationCallback userCertificateValidationCallback)
+ : this(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol, userCertificateValidationCallback, null)
{
- 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");
}
+ ///
+ /// Constructor
+ ///
+ /// Broker Host Name or IP Address
+ /// Broker port
+ /// Using secure connection
+ /// SSL/TLS protocol version
+ /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party
+ /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication
+ public MqttClient(string brokerHostName, int brokerPort, bool secure, MqttSslProtocols sslProtocol,
+ RemoteCertificateValidationCallback userCertificateValidationCallback,
+ LocalCertificateSelectionCallback userCertificateSelectionCallback)
+ : this(brokerHostName, brokerPort, secure, null, null, sslProtocol, userCertificateValidationCallback, userCertificateSelectionCallback)
+ {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Broker Host Name or IP Address
+ /// Broker port
+ /// Using secure connection
+ /// CA certificate for secure connection
+ /// Client certificate
+ /// SSL/TLS protocol version
+ /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party
+ /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication
+ public MqttClient(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol,
+ RemoteCertificateValidationCallback userCertificateValidationCallback,
+ LocalCertificateSelectionCallback userCertificateSelectionCallback)
+ {
+ this.Init(brokerHostName, brokerPort, secure, caCert, clientCert, sslProtocol, userCertificateValidationCallback, userCertificateSelectionCallback);
+ }
+#endif
+
#if BROKER
///
/// Constructor
///
- /// Raw socket for communication
- public MqttClient(Socket socket)
+ /// Network channel for communication
+ public MqttClient(IMqttNetworkChannel channel)
{
- this.channel = new MqttNetworkChannel(socket);
+ // set default MQTT protocol version (default is 3.1.1)
+ this.ProtocolVersion = MqttProtocolVersion.Version_3_1_1;
+
+ this.channel = channel;
// reference to MQTT settings
this.settings = MqttSettings.Instance;
@@ -296,49 +399,53 @@ namespace uPLibrary.Networking.M2Mqtt
// queue for received message
this.receiveEventWaitHandle = new AutoResetEvent(false);
- this.receiveQueue = new Queue();
+ this.eventQueue = new Queue();
this.internalQueue = new Queue();
+
+ // session
+ this.session = null;
}
#endif
///
/// MqttClient initialization
///
- /// Broker host name
- /// Broker IP address
+ /// Broker Host Name or 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 !");
+ /// Client certificate
+ /// SSL/TLS protocol version
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP)
+ /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party
+ /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication
+ private void Init(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol,
+ RemoteCertificateValidationCallback userCertificateValidationCallback,
+ LocalCertificateSelectionCallback userCertificateSelectionCallback)
+#elif (WINDOWS_APP || WINDOWS_PHONE_APP)
+ private void Init(string brokerHostName, int brokerPort, bool secure, MqttSslProtocols sslProtocol)
#else
+ private void Init(string brokerHostName, int brokerPort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol)
+#endif
+ {
+ // set default MQTT protocol version (default is 3.1.1)
+ this.ProtocolVersion = MqttProtocolVersion.Version_3_1_1;
+#if !SSL
+ // check security parameters
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;
+ // set settings port based on secure connection or not
+ if (!secure)
+ this.settings.Port = this.brokerPort;
+ else
+ this.settings.SslPort = this.brokerPort;
this.syncEndReceiving = new AutoResetEvent(false);
this.keepAliveEvent = new AutoResetEvent(false);
@@ -349,8 +456,20 @@ namespace uPLibrary.Networking.M2Mqtt
// queue for received message
this.receiveEventWaitHandle = new AutoResetEvent(false);
- this.receiveQueue = new Queue();
+ this.eventQueue = new Queue();
this.internalQueue = new Queue();
+
+ // session
+ this.session = null;
+
+ // create network channel
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP)
+ this.channel = new MqttNetworkChannel(this.brokerHostName, this.brokerPort, secure, caCert, clientCert, sslProtocol, userCertificateValidationCallback, userCertificateSelectionCallback);
+#elif (WINDOWS_APP || WINDOWS_PHONE_APP)
+ this.channel = new MqttNetworkChannel(this.brokerHostName, this.brokerPort, secure, sslProtocol);
+#else
+ this.channel = new MqttNetworkChannel(this.brokerHostName, this.brokerPort, secure, caCert, clientCert, sslProtocol);
+#endif
}
///
@@ -430,16 +549,12 @@ namespace uPLibrary.Networking.M2Mqtt
willTopic,
willMessage,
cleanSession,
- keepAlivePeriod);
+ keepAlivePeriod,
+ (byte)this.ProtocolVersion);
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
+ // connect to the broker
this.channel.Connect();
}
catch (Exception ex)
@@ -449,11 +564,11 @@ namespace uPLibrary.Networking.M2Mqtt
this.lastCommTime = 0;
this.isRunning = true;
+ this.isConnectionClosing = false;
// start thread for receiving messages from broker
- this.receiveThread = new Thread(this.ReceiveThread);
- this.receiveThread.Start();
-
- MqttMsgConnack connack = (MqttMsgConnack)this.SendReceive(connect.GetBytes());
+ Fx.StartThread(this.ReceiveThread);
+
+ MqttMsgConnack connack = (MqttMsgConnack)this.SendReceive(connect);
// if connection accepted, start keep alive timer and
if (connack.ReturnCode == MqttMsgConnack.CONN_ACCEPTED)
{
@@ -467,17 +582,21 @@ namespace uPLibrary.Networking.M2Mqtt
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();
+ // restore previous session
+ this.RestoreSession();
+
+ // keep alive period equals zero means turning off keep alive mechanism
+ if (this.keepAlivePeriod != 0)
+ {
+ // start thread for sending keep alive message to the broker
+ Fx.StartThread(this.KeepAliveThread);
+ }
// start thread for raising received message event from broker
- this.receiveEventThread = new Thread(this.ReceiveEventThread);
- this.receiveEventThread.Start();
-
+ Fx.StartThread(this.DispatchEventThread);
+
// start thread for handling inflight messages queue to broker asynchronously (publish and acknowledge)
- this.processInflightThread = new Thread(this.ProcessInflightThread);
- this.processInflightThread.Start();
+ Fx.StartThread(this.ProcessInflightThread);
this.IsConnected = true;
}
@@ -490,10 +609,10 @@ namespace uPLibrary.Networking.M2Mqtt
public void Disconnect()
{
MqttMsgDisconnect disconnect = new MqttMsgDisconnect();
- this.Send(disconnect.GetBytes());
+ this.Send(disconnect);
// close client
- this.Close();
+ this.OnConnectionClosing();
}
#if BROKER
@@ -505,19 +624,13 @@ namespace uPLibrary.Networking.M2Mqtt
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();
+ Fx.StartThread(this.ReceiveThread);
// 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();
+ Fx.StartThread(this.DispatchEventThread);
// 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();
+ Fx.StartThread(this.ProcessInflightThread);
}
#endif
@@ -533,50 +646,34 @@ namespace uPLibrary.Networking.M2Mqtt
// 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)
- {
+ if (this.receiveEventWaitHandle != 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)
- {
+ // wait end process inflight thread
+ if (this.inflightWaitHandle != 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();
+ // unlock keep alive thread
+ this.keepAliveEvent.Set();
#else
- // unlock keep alive thread and wait
- this.keepAliveEvent.Set();
+ // unlock keep alive thread and wait
+ this.keepAliveEvent.Set();
- if (this.keepAliveThread != null)
- this.keepAliveThread.Join();
+ if (this.keepAliveEventEnd != null)
+ this.keepAliveEventEnd.WaitOne();
#endif
- }
+
+ // clear all queues
+ this.inflightQueue.Clear();
+ this.internalQueue.Clear();
+ this.eventQueue.Clear();
// close network channel
this.channel.Close();
- // keep alive thread will set it gracefully
- if (!this.isKeepAliveTimeout)
- this.IsConnected = false;
+ this.IsConnected = false;
}
///
@@ -589,13 +686,16 @@ namespace uPLibrary.Networking.M2Mqtt
try
{
// broker must send PINGRESP within timeout equal to keep alive period
- return (MqttMsgPingResp)this.SendReceive(pingreq.GetBytes(), this.keepAlivePeriod);
+ return (MqttMsgPingResp)this.SendReceive(pingreq, this.keepAlivePeriod);
}
- catch (Exception)
+ catch (Exception e)
{
- this.isKeepAliveTimeout = true;
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString());
+#endif
+
// client must close connection
- this.Close();
+ this.OnConnectionClosing();
return null;
}
}
@@ -604,22 +704,29 @@ namespace uPLibrary.Networking.M2Mqtt
///
/// 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)
+ /// Return code for CONNACK message
+ /// If not null, client id assigned by broker
+ /// Session present on the broker
+ public void Connack(MqttMsgConnect connect, byte returnCode, string clientId, bool sessionPresent)
{
this.lastCommTime = 0;
// create CONNACK message and ...
MqttMsgConnack connack = new MqttMsgConnack();
connack.ReturnCode = returnCode;
+ // [v3.1.1] session present flag
+ if (this.ProtocolVersion == MqttProtocolVersion.Version_3_1_1)
+ connack.SessionPresent = sessionPresent;
// ... send it to the client
- this.Send(connack.GetBytes());
+ this.Send(connack);
// connection accepted, start keep alive thread checking
if (connack.ReturnCode == MqttMsgConnack.CONN_ACCEPTED)
{
- this.ClientId = connect.ClientId;
+ // [v3.1.1] if client id isn't null, the CONNECT message has a cliend id with zero bytes length
+ // and broker assigned a unique identifier to the client
+ this.ClientId = (clientId == null) ? connect.ClientId : clientId;
this.CleanSession = connect.CleanSession;
this.WillFlag = connect.WillFlag;
this.WillTopic = connect.WillTopic;
@@ -631,10 +738,9 @@ namespace uPLibrary.Networking.M2Mqtt
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();
+ Fx.StartThread(this.KeepAliveThread);
+ this.isConnectionClosing = false;
this.IsConnected = true;
}
// connection refused, close TCP/IP channel
@@ -655,7 +761,7 @@ namespace uPLibrary.Networking.M2Mqtt
suback.MessageId = messageId;
suback.GrantedQoSLevels = grantedQosLevels;
- this.Send(suback.GetBytes());
+ this.Send(suback);
}
///
@@ -667,7 +773,7 @@ namespace uPLibrary.Networking.M2Mqtt
MqttMsgUnsuback unsuback = new MqttMsgUnsuback();
unsuback.MessageId = messageId;
- this.Send(unsuback.GetBytes());
+ this.Send(unsuback);
}
#endif
@@ -689,11 +795,6 @@ namespace uPLibrary.Networking.M2Mqtt
return subscribe.MessageId;
}
- public ushort Subscribe(string topic, byte qos)
- {
- return Subscribe(new[] { topic }, new[] { qos });
- }
-
///
/// Unsubscribe for message topics
///
@@ -737,25 +838,42 @@ namespace uPLibrary.Networking.M2Mqtt
publish.MessageId = this.GetMessageId();
// enqueue message to publish into the inflight queue
- this.EnqueueInflight(publish, MqttMsgFlow.ToPublish);
+ bool enqueue = this.EnqueueInflight(publish, MqttMsgFlow.ToPublish);
- return publish.MessageId;
+ // message enqueued
+ if (enqueue)
+ return publish.MessageId;
+ // infligh queue full, message not enqueued
+ else
+ throw new MqttClientException(MqttClientErrorCode.InflightQueueFull);
}
///
- /// Wrapper method for raising message received event
+ /// Wrapper method for raising events
///
- /// Message received
- private void OnMqttMsgReceived(MqttMsgBase msg)
+ /// Internal event
+ private void OnInternalEvent(InternalEvent internalEvent)
{
- lock (this.receiveQueue)
+ lock (this.eventQueue)
{
- this.receiveQueue.Enqueue(msg);
+ this.eventQueue.Enqueue(internalEvent);
}
this.receiveEventWaitHandle.Set();
}
+ ///
+ /// Wrapper method for raising closing connection event
+ ///
+ private void OnConnectionClosing()
+ {
+ if (!this.isConnectionClosing)
+ {
+ this.isConnectionClosing = true;
+ this.receiveEventWaitHandle.Set();
+ }
+ }
+
///
/// Wrapper method for raising PUBLISH message received event
///
@@ -773,12 +891,13 @@ namespace uPLibrary.Networking.M2Mqtt
/// Wrapper method for raising published message event
///
/// Message identifier for published message
- private void OnMqttMsgPublished(ushort messageId)
+ /// Publish flag
+ private void OnMqttMsgPublished(ushort messageId, bool isPublished)
{
if (this.MqttMsgPublished != null)
{
this.MqttMsgPublished(this,
- new MqttMsgPublishedEventArgs(messageId));
+ new MqttMsgPublishedEventArgs(messageId, isPublished));
}
}
@@ -839,19 +958,19 @@ namespace uPLibrary.Networking.M2Mqtt
}
///
- /// Wrapper method for client connection event
+ /// Wrapper method for raising CONNECT message event
///
private void OnMqttMsgConnected(MqttMsgConnect connect)
{
if (this.MqttMsgConnected != null)
{
+ this.ProtocolVersion = (MqttProtocolVersion)connect.ProtocolVersion;
this.MqttMsgConnected(this, new MqttMsgConnectEventArgs(connect));
}
}
-#endif
///
- /// Wrapper method for client disconnection event
+ /// Wrapper method for raising DISCONNECT message event
///
private void OnMqttMsgDisconnected()
{
@@ -860,6 +979,18 @@ namespace uPLibrary.Networking.M2Mqtt
this.MqttMsgDisconnected(this, EventArgs.Empty);
}
}
+#endif
+
+ ///
+ /// Wrapper method for peer/client disconnection
+ ///
+ private void OnConnectionClosed()
+ {
+ if (this.ConnectionClosed != null)
+ {
+ this.ConnectionClosed(this, EventArgs.Empty);
+ }
+ }
///
/// Send a message
@@ -879,10 +1010,26 @@ namespace uPLibrary.Networking.M2Mqtt
}
catch (Exception e)
{
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString());
+#endif
+
throw new MqttCommunicationException(e);
}
}
+ ///
+ /// Send a message
+ ///
+ /// Message
+ private void Send(MqttMsgBase msg)
+ {
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Frame, "SEND {0}", msg);
+#endif
+ this.Send(msg.GetBytes((byte)this.ProtocolVersion));
+ }
+
///
/// Send a message to the broker and wait answer
///
@@ -911,12 +1058,18 @@ namespace uPLibrary.Networking.M2Mqtt
// update last message sent ticks
this.lastCommTime = Environment.TickCount;
}
- catch (SocketException e)
+ catch (Exception 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;
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK || WINDOWS_APP || WINDOWS_PHONE_APP)
+ if (typeof(SocketException) == e.GetType())
+ {
+ // connection reset by broker
+ if (((SocketException)e).SocketErrorCode == SocketError.ConnectionReset)
+ this.IsConnected = false;
+ }
+#endif
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString());
#endif
throw new MqttCommunicationException(e);
@@ -940,17 +1093,41 @@ namespace uPLibrary.Networking.M2Mqtt
else
{
// throw timeout exception
- //throw new MqttTimeoutException();
throw new MqttCommunicationException();
}
}
+ ///
+ /// Send a message to the broker and wait answer
+ ///
+ /// Message
+ /// MQTT message response
+ private MqttMsgBase SendReceive(MqttMsgBase msg)
+ {
+ return this.SendReceive(msg, MqttSettings.MQTT_DEFAULT_TIMEOUT);
+ }
+
+ ///
+ /// Send a message to the broker and wait answer
+ ///
+ /// Message
+ /// Timeout for receiving answer
+ /// MQTT message response
+ private MqttMsgBase SendReceive(MqttMsgBase msg, int timeout)
+ {
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Frame, "SEND {0}", msg);
+#endif
+ return this.SendReceive(msg.GetBytes((byte)this.ProtocolVersion), timeout);
+ }
+
///
/// Enqueue a message into the inflight queue
///
/// Message to enqueue
/// Message flow (publish, acknowledge)
- private void EnqueueInflight(MqttMsgBase msg, MqttMsgFlow flow)
+ /// Message enqueued or not
+ private bool EnqueueInflight(MqttMsgBase msg, MqttMsgFlow flow)
{
// enqueue is needed (or not)
bool enqueue = true;
@@ -966,8 +1143,8 @@ namespace uPLibrary.Networking.M2Mqtt
// 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);
+ MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToAcknowledge);
+ MqttMsgContext msgCtx = (MqttMsgContext) QueueExtension.Get(this.inflightQueue, msgCtxFinder.Find);
// the PUBLISH message is alredy in the inflight queue, we don't need to re-enqueue but we need
// to change state to re-send PUBREC
@@ -1007,6 +1184,13 @@ namespace uPLibrary.Networking.M2Mqtt
break;
}
+ // [v3.1.1] SUBSCRIBE and UNSUBSCRIBE aren't "officially" QOS = 1
+ // so QueuedQos1 state isn't valid for them
+ if (msg.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE)
+ state = MqttMsgState.SendSubscribe;
+ else if (msg.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE)
+ state = MqttMsgState.SendUnsubscribe;
+
// queue message context
MqttMsgContext msgContext = new MqttMsgContext()
{
@@ -1018,12 +1202,44 @@ namespace uPLibrary.Networking.M2Mqtt
lock (this.inflightQueue)
{
- // enqueue message and unlock send thread
- this.inflightQueue.Enqueue(msgContext);
+ // check number of messages inside inflight queue
+ enqueue = (this.inflightQueue.Count < this.settings.InflightQueueSize);
+
+ if (enqueue)
+ {
+ // enqueue message and unlock send thread
+ this.inflightQueue.Enqueue(msgContext);
+
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "enqueued {0}", msg);
+#endif
+
+ // PUBLISH message
+ if (msg.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE)
+ {
+ // to publish and QoS level 1 or 2
+ if ((msgContext.Flow == MqttMsgFlow.ToPublish) &&
+ ((msg.QosLevel == MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE) ||
+ (msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE)))
+ {
+ if (this.session != null)
+ this.session.InflightMessages.Add(msgContext.Key, msgContext);
+ }
+ // to acknowledge and QoS level 2
+ else if ((msgContext.Flow == MqttMsgFlow.ToAcknowledge) &&
+ (msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE))
+ {
+ if (this.session != null)
+ this.session.InflightMessages.Add(msgContext.Key, msgContext);
+ }
+ }
+ }
}
}
this.inflightWaitHandle.Set();
+
+ return enqueue;
}
///
@@ -1046,28 +1262,73 @@ namespace uPLibrary.Networking.M2Mqtt
// 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);
+ MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(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;
+ pubcomp.MessageId = msg.MessageId;
- this.Send(pubcomp.GetBytes());
+ this.Send(pubcomp);
enqueue = false;
}
}
}
+ // if it is a PUBCOMP message (for QoS Level 2)
+ else if (msg.Type == MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE)
+ {
+ lock (this.inflightQueue)
+ {
+ // if it is a PUBCOMP but the corresponding PUBLISH isn't in the inflight queue,
+ // it means that we sent PUBLISH message, sent PUBREL (after receiving PUBREC) and already received PUBCOMP
+ // but publisher didn't receive PUBREL so it re-sent PUBCOMP. We need only to ignore this PUBCOMP.
+
+ // NOTE : I need to find on message id and flow because the broker could be publish/received
+ // to/from client and message id could be the same (one tracked by broker and the other by client)
+ MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToPublish);
+ MqttMsgContext msgCtx = (MqttMsgContext) QueueExtension.Get(this.inflightQueue, msgCtxFinder.Find);
+
+ // the PUBLISH message isn't in the inflight queue, it was already sent so we need to ignore this PUBCOMP
+ if (msgCtx == null)
+ {
+ enqueue = false;
+ }
+ }
+ }
+ // if it is a PUBREC message (for QoS Level 2)
+ else if (msg.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE)
+ {
+ lock (this.inflightQueue)
+ {
+ // if it is a PUBREC but the corresponding PUBLISH isn't in the inflight queue,
+ // it means that we sent PUBLISH message more times (retries) but broker didn't send PUBREC in time
+ // the publish is failed and we need only to ignore this PUBREC.
+
+ // NOTE : I need to find on message id and flow because the broker could be publish/received
+ // to/from client and message id could be the same (one tracked by broker and the other by client)
+ MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToPublish);
+ MqttMsgContext msgCtx = (MqttMsgContext) QueueExtension.Get(this.inflightQueue, msgCtxFinder.Find);
+
+ // the PUBLISH message isn't in the inflight queue, it was already sent so we need to ignore this PUBREC
+ if (msgCtx == null)
+ {
+ enqueue = false;
+ }
+ }
+ }
if (enqueue)
{
lock (this.internalQueue)
{
this.internalQueue.Enqueue(msg);
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "enqueued {0}", msg);
+#endif
this.inflightWaitHandle.Set();
}
}
@@ -1082,44 +1343,12 @@ namespace uPLibrary.Networking.M2Mqtt
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);
- }
+ // read first byte (fixed header)
+ readBytes = this.channel.Receive(fixedHeaderFirstByte);
if (readBytes > 0)
{
@@ -1137,22 +1366,28 @@ namespace uPLibrary.Networking.M2Mqtt
case MqttMsgBase.MQTT_MSG_CONNECT_TYPE:
#if BROKER
- MqttMsgConnect connect = MqttMsgConnect.Parse(fixedHeaderFirstByte[0], this.channel);
-
+ MqttMsgConnect connect = MqttMsgConnect.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ Trace.WriteLine(TraceLevel.Frame, "RECV {0}", connect);
+#endif
+
// raise message received event
- this.OnMqttMsgReceived(connect);
+ this.OnInternalEvent(new MsgInternalEvent(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.msgReceived = MqttMsgConnack.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", this.msgReceived);
+#endif
this.syncEndReceiving.Set();
break;
#endif
@@ -1161,13 +1396,14 @@ namespace uPLibrary.Networking.M2Mqtt
case MqttMsgBase.MQTT_MSG_PINGREQ_TYPE:
#if BROKER
- this.msgReceived = MqttMsgPingReq.Parse(fixedHeaderFirstByte[0], this.channel);
+ this.msgReceived = MqttMsgPingReq.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ Trace.WriteLine(TraceLevel.Frame, "RECV {0}", this.msgReceived);
+#endif
MqttMsgPingResp pingresp = new MqttMsgPingResp();
- this.Send(pingresp.GetBytes());
+ this.Send(pingresp);
- // raise message received event
- //this.OnMqttMsgReceived(this.msgReceived);
break;
#else
throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
@@ -1179,7 +1415,10 @@ namespace uPLibrary.Networking.M2Mqtt
#if BROKER
throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
#else
- this.msgReceived = MqttMsgPingResp.Parse(fixedHeaderFirstByte[0], this.channel);
+ this.msgReceived = MqttMsgPingResp.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", this.msgReceived);
+#endif
this.syncEndReceiving.Set();
break;
#endif
@@ -1188,10 +1427,13 @@ namespace uPLibrary.Networking.M2Mqtt
case MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE:
#if BROKER
- MqttMsgSubscribe subscribe = MqttMsgSubscribe.Parse(fixedHeaderFirstByte[0], this.channel);
+ MqttMsgSubscribe subscribe = MqttMsgSubscribe.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ Trace.WriteLine(TraceLevel.Frame, "RECV {0}", subscribe);
+#endif
// raise message received event
- this.OnMqttMsgReceived(subscribe);
+ this.OnInternalEvent(new MsgInternalEvent(subscribe));
break;
#else
@@ -1205,7 +1447,10 @@ namespace uPLibrary.Networking.M2Mqtt
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);
+ MqttMsgSuback suback = MqttMsgSuback.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", suback);
+#endif
// enqueue SUBACK message into the internal queue
this.EnqueueInternal(suback);
@@ -1216,7 +1461,10 @@ namespace uPLibrary.Networking.M2Mqtt
// PUBLISH message received
case MqttMsgBase.MQTT_MSG_PUBLISH_TYPE:
- MqttMsgPublish publish = MqttMsgPublish.Parse(fixedHeaderFirstByte[0], this.channel);
+ MqttMsgPublish publish = MqttMsgPublish.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", publish);
+#endif
// enqueue PUBLISH message to acknowledge into the inflight queue
this.EnqueueInflight(publish, MqttMsgFlow.ToAcknowledge);
@@ -1227,7 +1475,10 @@ namespace uPLibrary.Networking.M2Mqtt
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);
+ MqttMsgPuback puback = MqttMsgPuback.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", puback);
+#endif
// enqueue PUBACK message into the internal queue
this.EnqueueInternal(puback);
@@ -1238,7 +1489,10 @@ namespace uPLibrary.Networking.M2Mqtt
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);
+ MqttMsgPubrec pubrec = MqttMsgPubrec.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", pubrec);
+#endif
// enqueue PUBREC message into the internal queue
this.EnqueueInternal(pubrec);
@@ -1249,18 +1503,24 @@ namespace uPLibrary.Networking.M2Mqtt
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);
+ MqttMsgPubrel pubrel = MqttMsgPubrel.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", pubrel);
+#endif
// 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);
+ MqttMsgPubcomp pubcomp = MqttMsgPubcomp.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", pubcomp);
+#endif
// enqueue PUBCOMP message into the internal queue
this.EnqueueInternal(pubcomp);
@@ -1271,10 +1531,13 @@ namespace uPLibrary.Networking.M2Mqtt
case MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE:
#if BROKER
- MqttMsgUnsubscribe unsubscribe = MqttMsgUnsubscribe.Parse(fixedHeaderFirstByte[0], this.channel);
+ MqttMsgUnsubscribe unsubscribe = MqttMsgUnsubscribe.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ Trace.WriteLine(TraceLevel.Frame, "RECV {0}", unsubscribe);
+#endif
// raise message received event
- this.OnMqttMsgReceived(unsubscribe);
+ this.OnInternalEvent(new MsgInternalEvent(unsubscribe));
break;
#else
@@ -1288,7 +1551,10 @@ namespace uPLibrary.Networking.M2Mqtt
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);
+ MqttMsgUnsuback unsuback = MqttMsgUnsuback.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Frame, "RECV {0}", unsuback);
+#endif
// enqueue UNSUBACK message into the internal queue
this.EnqueueInternal(unsuback);
@@ -1300,10 +1566,13 @@ namespace uPLibrary.Networking.M2Mqtt
case MqttMsgDisconnect.MQTT_MSG_DISCONNECT_TYPE:
#if BROKER
- MqttMsgDisconnect disconnect = MqttMsgDisconnect.Parse(fixedHeaderFirstByte[0], this.channel);
+ MqttMsgDisconnect disconnect = MqttMsgDisconnect.Parse(fixedHeaderFirstByte[0], (byte)this.ProtocolVersion, this.channel);
+#if TRACE
+ Trace.WriteLine(TraceLevel.Frame, "RECV {0}", disconnect);
+#endif
// raise message received event
- this.OnMqttMsgReceived(disconnect);
+ this.OnInternalEvent(new MsgInternalEvent(disconnect));
break;
#else
@@ -1317,11 +1586,42 @@ namespace uPLibrary.Networking.M2Mqtt
this.exReceiving = null;
}
-
+ // zero bytes read, peer gracefully closed socket
+ else
+ {
+ // wake up thread that will notify connection is closing
+ this.OnConnectionClosing();
+ }
}
catch (Exception e)
{
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString());
+#endif
this.exReceiving = new MqttCommunicationException(e);
+
+ bool close = false;
+ if (e.GetType() == typeof(MqttClientException))
+ {
+ // [v3.1.1] scenarios the receiver MUST close the network connection
+ MqttClientException ex = e as MqttClientException;
+ close = ((ex.ErrorCode == MqttClientErrorCode.InvalidFlagBits) ||
+ (ex.ErrorCode == MqttClientErrorCode.InvalidProtocolName) ||
+ (ex.ErrorCode == MqttClientErrorCode.InvalidConnectFlags));
+ }
+#if !(WINDOWS_APP || WINDOWS_PHONE_APP)
+ else if ((e.GetType() == typeof(IOException)) || (e.GetType() == typeof(SocketException)) ||
+ ((e.InnerException != null) && (e.InnerException.GetType() == typeof(SocketException)))) // added for SSL/TLS incoming connection that use SslStream that wraps SocketException
+ {
+ close = true;
+ }
+#endif
+
+ if (close)
+ {
+ // wake up thread that will notify connection is closing
+ this.OnConnectionClosing();
+ }
}
}
}
@@ -1331,9 +1631,11 @@ namespace uPLibrary.Networking.M2Mqtt
///
private void KeepAliveThread()
{
- long now = 0;
+ int delta = 0;
int wait = this.keepAlivePeriod;
- this.isKeepAliveTimeout = false;
+
+ // create event to signal that current thread is end
+ this.keepAliveEventEnd = new AutoResetEvent(false);
while (this.isRunning)
{
@@ -1347,154 +1649,195 @@ namespace uPLibrary.Networking.M2Mqtt
if (this.isRunning)
{
- now = Environment.TickCount;
+ delta = Environment.TickCount - this.lastCommTime;
// if timeout exceeded ...
- if ((now - this.lastCommTime) >= this.keepAlivePeriod)
+ if (delta >= this.keepAlivePeriod)
{
#if BROKER
- this.isKeepAliveTimeout = true;
// client must close connection
- this.Close();
+ this.OnConnectionClosing();
#else
// ... send keep alive
- this.Ping();
- wait = this.keepAlivePeriod;
+ this.Ping();
+ wait = this.keepAlivePeriod;
#endif
}
else
{
// update waiting time
- wait = (int)(this.keepAlivePeriod - (now - this.lastCommTime));
+ wait = this.keepAlivePeriod - delta;
}
}
}
- if (this.isKeepAliveTimeout)
- {
- this.IsConnected = false;
- // raise disconnection client event
- this.OnMqttMsgDisconnected();
- }
+ // signal thread end
+ this.keepAliveEventEnd.Set();
}
///
- /// Thread for raising received message event
+ /// Thread for raising event
///
- private void ReceiveEventThread()
+ private void DispatchEventThread()
{
while (this.isRunning)
{
- if (this.receiveQueue.Count == 0)
+#if BROKER
+ if ((this.eventQueue.Count == 0) && !this.isConnectionClosing)
+ {
+ // broker need to receive the first message (CONNECT)
+ // within a reasonable amount of time after TCP/IP connection
+ if (!this.IsConnected)
+ {
+ // wait on receiving message from client with a connection timeout
+ if (!this.receiveEventWaitHandle.WaitOne(this.settings.TimeoutOnConnection))
+ {
+ // client must close connection
+ this.Close();
+
+ // client raw disconnection
+ this.OnConnectionClosed();
+ }
+ }
+ else
+ {
+ // wait on receiving message from client
+ this.receiveEventWaitHandle.WaitOne();
+ }
+ }
+#else
+ if ((this.eventQueue.Count == 0) && !this.isConnectionClosing)
// wait on receiving message from client
this.receiveEventWaitHandle.WaitOne();
+#endif
// check if it is running or we are closing client
if (this.isRunning)
{
- // get message from queue
- MqttMsgBase msg = null;
- lock (this.receiveQueue)
+ // get event from queue
+ InternalEvent internalEvent = null;
+ lock (this.eventQueue)
{
- if (this.receiveQueue.Count > 0)
- msg = (MqttMsgBase)this.receiveQueue.Dequeue();
+ if (this.eventQueue.Count > 0)
+ internalEvent = (InternalEvent)this.eventQueue.Dequeue();
}
- if (msg != null)
+ // it's an event with a message inside
+ if (internalEvent != null)
{
- switch (msg.Type)
+ MqttMsgBase msg = ((MsgInternalEvent)internalEvent).Message;
+
+ if (msg != null)
{
- // CONNECT message received
- case MqttMsgBase.MQTT_MSG_CONNECT_TYPE:
+ 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;
+ // raise connected client event (CONNECT message received)
+ this.OnMqttMsgConnected((MqttMsgConnect)msg);
+ break;
#else
- throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
+ throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
#endif
- // SUBSCRIBE message received
- case MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE:
+ // SUBSCRIBE message received
+ case MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE:
#if BROKER
- MqttMsgSubscribe subscribe = (MqttMsgSubscribe)msg;
- // raise subscribe topic event (SUBSCRIBE message received)
- this.OnMqttMsgSubscribeReceived(subscribe.MessageId, subscribe.Topics, subscribe.QoSLevels);
+ MqttMsgSubscribe subscribe = (MqttMsgSubscribe)msg;
+ // raise subscribe topic event (SUBSCRIBE message received)
+ this.OnMqttMsgSubscribeReceived(subscribe.MessageId, subscribe.Topics, subscribe.QoSLevels);
+ break;
#else
- throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
+ throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
#endif
- // SUBACK message received
- case MqttMsgBase.MQTT_MSG_SUBACK_TYPE:
+ // SUBACK message received
+ case MqttMsgBase.MQTT_MSG_SUBACK_TYPE:
- // raise subscribed topic event (SUBACK message received)
- this.OnMqttMsgSubscribed((MqttMsgSuback)msg);
- break;
+ // raise subscribed topic event (SUBACK message received)
+ this.OnMqttMsgSubscribed((MqttMsgSuback)msg);
+ break;
- // PUBLISH message received
- case MqttMsgBase.MQTT_MSG_PUBLISH_TYPE:
+ // PUBLISH message received
+ case MqttMsgBase.MQTT_MSG_PUBLISH_TYPE:
- // raise PUBLISH message received event
- this.OnMqttMsgPublishReceived((MqttMsgPublish)msg);
- break;
+ // PUBLISH message received in a published internal event, no publish succeeded
+ if (internalEvent.GetType() == typeof(MsgPublishedInternalEvent))
+ this.OnMqttMsgPublished(msg.MessageId, false);
+ else
+ // raise PUBLISH message received event
+ this.OnMqttMsgPublishReceived((MqttMsgPublish)msg);
+ break;
- // PUBACK message received
- case MqttMsgBase.MQTT_MSG_PUBACK_TYPE:
+ // 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;
+ // raise published message event
+ // (PUBACK received for QoS Level 1)
+ this.OnMqttMsgPublished(msg.MessageId, true);
+ break;
- // PUBREL message received
- case MqttMsgBase.MQTT_MSG_PUBREL_TYPE:
+ // PUBREL message received
+ case MqttMsgBase.MQTT_MSG_PUBREL_TYPE:
- // raise message received event
- // (PUBREL received for QoS Level 2)
- this.OnMqttMsgPublishReceived((MqttMsgPublish)msg);
- break;
+ // raise message received event
+ // (PUBREL received for QoS Level 2)
+ this.OnMqttMsgPublishReceived((MqttMsgPublish)msg);
+ break;
- // PUBCOMP message received
- case MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE:
+ // 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;
+ // raise published message event
+ // (PUBCOMP received for QoS Level 2)
+ this.OnMqttMsgPublished(msg.MessageId, true);
+ break;
- // UNSUBSCRIBE message received from client
- case MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE:
+ // 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;
+ MqttMsgUnsubscribe unsubscribe = (MqttMsgUnsubscribe)msg;
+ // raise unsubscribe topic event (UNSUBSCRIBE message received)
+ this.OnMqttMsgUnsubscribeReceived(unsubscribe.MessageId, unsubscribe.Topics);
+ break;
#else
- throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
+ throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
#endif
- // UNSUBACK message received
- case MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE:
+ // UNSUBACK message received
+ case MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE:
- // raise unsubscribed topic event
- this.OnMqttMsgUnsubscribed(((MqttMsgUnsuback)msg).MessageId);
- break;
+ // raise unsubscribed topic event
+ this.OnMqttMsgUnsubscribed(msg.MessageId);
+ break;
- // DISCONNECT message received from client
- case MqttMsgDisconnect.MQTT_MSG_DISCONNECT_TYPE:
+ // DISCONNECT message received from client
+ case MqttMsgDisconnect.MQTT_MSG_DISCONNECT_TYPE:
#if BROKER
- // raise disconnected client event (DISCONNECT message received)
- this.OnMqttMsgDisconnected();
- break;
+ // raise disconnected client event (DISCONNECT message received)
+ this.OnMqttMsgDisconnected();
+ break;
#else
- throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
+ throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
#endif
+ }
}
}
+
+ // all events for received messages dispatched, check if there is closing connection
+ if ((this.eventQueue.Count == 0) && this.isConnectionClosing)
+ {
+ // client must close connection
+ this.Close();
+
+ // client raw disconnection
+ this.OnConnectionClosed();
+ }
}
}
}
@@ -1507,8 +1850,11 @@ namespace uPLibrary.Networking.M2Mqtt
MqttMsgContext msgContext = null;
MqttMsgBase msgInflight = null;
MqttMsgBase msgReceived = null;
+ InternalEvent internalEvent = null;
bool acknowledge = false;
int timeout = Timeout.Infinite;
+ int delta;
+ bool msgReceivedProcessed = false;
try
{
@@ -1527,6 +1873,14 @@ namespace uPLibrary.Networking.M2Mqtt
{
lock (this.inflightQueue)
{
+ // message received and peeked from internal queue is processed
+ // NOTE : it has the corresponding message in inflight queue based on messageId
+ // (ex. a PUBREC for a PUBLISH, a SUBACK for a SUBSCRIBE, ...)
+ // if it's orphan we need to remove from internal queue
+ msgReceivedProcessed = false;
+ acknowledge = false;
+ msgReceived = null;
+
// set timeout tu MaxValue instead of Infinte (-1) to perform
// compare with calcultad current msgTimeout
timeout = Int32.MaxValue;
@@ -1541,6 +1895,10 @@ namespace uPLibrary.Networking.M2Mqtt
acknowledge = false;
msgReceived = null;
+ // check to be sure that client isn't closing and all queues are now empty !
+ if (!this.isRunning)
+ break;
+
// dequeue message context from queue
msgContext = (MqttMsgContext)this.inflightQueue.Dequeue();
@@ -1554,24 +1912,40 @@ namespace uPLibrary.Networking.M2Mqtt
// QoS 0, PUBLISH message to send to broker, no state change, no acknowledge
if (msgContext.Flow == MqttMsgFlow.ToPublish)
{
- this.Send(msgInflight.GetBytes());
+ this.Send(msgInflight);
}
// QoS 0, no need acknowledge
else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge)
{
+ internalEvent = new MsgInternalEvent(msgInflight);
// notify published message from broker (no need acknowledged)
- this.OnMqttMsgReceived(msgInflight);
+ this.OnInternalEvent(internalEvent);
}
+
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight);
+#endif
break;
case MqttMsgState.QueuedQos1:
+ // [v3.1.1] SUBSCRIBE and UNSIBSCRIBE aren't "officially" QOS = 1
+ case MqttMsgState.SendSubscribe:
+ case MqttMsgState.SendUnsubscribe:
// QoS 1, PUBLISH or SUBSCRIBE/UNSUBSCRIBE message to send to broker, state change to wait PUBACK or SUBACK/UNSUBACK
if (msgContext.Flow == MqttMsgFlow.ToPublish)
{
+ msgContext.Timestamp = Environment.TickCount;
+ msgContext.Attempt++;
+
if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE)
+ {
// PUBLISH message to send, wait for PUBACK
msgContext.State = MqttMsgState.WaitForPuback;
+ // retry ? set dup flag [v3.1.1] only for PUBLISH message
+ if (msgContext.Attempt > 1)
+ msgInflight.DupFlag = true;
+ }
else if (msgInflight.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE)
// SUBSCRIBE message to send, wait for SUBACK
msgContext.State = MqttMsgState.WaitForSuback;
@@ -1579,17 +1953,10 @@ namespace uPLibrary.Networking.M2Mqtt
// 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);
- this.Send(msgInflight.GetBytes());
-
- // update timeout
- int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp));
- timeout = (msgTimeout < timeout) ? msgTimeout : timeout;
+ // update timeout : minimum between delay (based on current message sent) or current timeout
+ timeout = (this.settings.DelayOnRetry < timeout) ? this.settings.DelayOnRetry : timeout;
// re-enqueue message (I have to re-analyze for receiving PUBACK, SUBACK or UNSUBACK)
this.inflightQueue.Enqueue(msgContext);
@@ -1598,12 +1965,17 @@ namespace uPLibrary.Networking.M2Mqtt
else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge)
{
MqttMsgPuback puback = new MqttMsgPuback();
- puback.MessageId = ((MqttMsgPublish)msgInflight).MessageId;
+ puback.MessageId = msgInflight.MessageId;
- this.Send(puback.GetBytes());
+ this.Send(puback);
+ internalEvent = new MsgInternalEvent(msgInflight);
// notify published message from broker and acknowledged
- this.OnMqttMsgReceived(msgInflight);
+ this.OnInternalEvent(internalEvent);
+
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight);
+#endif
}
break;
@@ -1612,18 +1984,17 @@ namespace uPLibrary.Networking.M2Mqtt
// 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++;
+ msgContext.State = MqttMsgState.WaitForPubrec;
// retry ? set dup flag
if (msgContext.Attempt > 1)
msgInflight.DupFlag = true;
- this.Send(msgInflight.GetBytes());
+ this.Send(msgInflight);
- // update timeout
- int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp));
- timeout = (msgTimeout < timeout) ? msgTimeout : timeout;
+ // update timeout : minimum between delay (based on current message sent) or current timeout
+ timeout = (this.settings.DelayOnRetry < timeout) ? this.settings.DelayOnRetry : timeout;
// re-enqueue message (I have to re-analyze for receiving PUBREC)
this.inflightQueue.Enqueue(msgContext);
@@ -1632,11 +2003,11 @@ namespace uPLibrary.Networking.M2Mqtt
else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge)
{
MqttMsgPubrec pubrec = new MqttMsgPubrec();
- pubrec.MessageId = ((MqttMsgPublish)msgInflight).MessageId;
+ pubrec.MessageId = msgInflight.MessageId;
msgContext.State = MqttMsgState.WaitForPubrel;
- this.Send(pubrec.GetBytes());
+ this.Send(pubrec);
// re-enqueue message (I have to re-analyze for receiving PUBREL)
this.inflightQueue.Enqueue(msgContext);
@@ -1660,37 +2031,62 @@ namespace uPLibrary.Networking.M2Mqtt
}
// 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)))
+ if (msgReceived != null)
{
- // 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)))
+ // PUBACK message or SUBACK/UNSUBACK message for the current message
+ if (((msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBACK_TYPE) && (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) && (msgReceived.MessageId == msgInflight.MessageId)) ||
+ ((msgReceived.Type == MqttMsgBase.MQTT_MSG_SUBACK_TYPE) && (msgInflight.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE) && (msgReceived.MessageId == msgInflight.MessageId)) ||
+ ((msgReceived.Type == MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE) && (msgInflight.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE) && (msgReceived.MessageId == msgInflight.MessageId)))
{
lock (this.internalQueue)
{
// received message processed
this.internalQueue.Dequeue();
acknowledge = true;
+ msgReceivedProcessed = true;
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived);
+#endif
}
+ // if PUBACK received, confirm published with flag
+ if (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBACK_TYPE)
+ internalEvent = new MsgPublishedInternalEvent(msgReceived, true);
+ else
+ internalEvent = new MsgInternalEvent(msgReceived);
+
// notify received acknowledge from broker of a published message or subscribe/unsubscribe message
- this.OnMqttMsgReceived(msgReceived);
+ this.OnInternalEvent(internalEvent);
+
+ // PUBACK received for PUBLISH message with QoS Level 1, remove from session state
+ if ((msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) &&
+ (this.session != null) &&
+#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ (this.session.InflightMessages.Contains(msgContext.Key)))
+#else
+ (this.session.InflightMessages.ContainsKey(msgContext.Key)))
+#endif
+ {
+ this.session.InflightMessages.Remove(msgContext.Key);
+ }
+
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight);
+#endif
}
}
// current message not acknowledged, no PUBACK or SUBACK/UNSUBACK or not equal messageid
if (!acknowledge)
{
+ delta = Environment.TickCount - msgContext.Timestamp;
// check timeout for receiving PUBACK since PUBLISH was sent or
// for receiving SUBACK since SUBSCRIBE was sent or
// for receiving UNSUBACK since UNSUBSCRIBE was sent
- if ((Environment.TickCount - msgContext.Timestamp) >= this.settings.DelayOnRetry)
+ if (delta >= this.settings.DelayOnRetry)
{
// max retry not reached, resend
- if (msgContext.Attempt <= this.settings.AttemptsOnRetry)
+ if (msgContext.Attempt < this.settings.AttemptsOnRetry)
{
msgContext.State = MqttMsgState.QueuedQos1;
@@ -1700,6 +2096,30 @@ namespace uPLibrary.Networking.M2Mqtt
// update timeout (0 -> reanalyze queue immediately)
timeout = 0;
}
+ else
+ {
+ // if PUBACK for a PUBLISH message not received after retries, raise event for not published
+ if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE)
+ {
+ // PUBACK not received in time, PUBLISH retries failed, need to remove from session inflight messages too
+ if ((this.session != null) &&
+#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ (this.session.InflightMessages.Contains(msgContext.Key)))
+#else
+ (this.session.InflightMessages.ContainsKey(msgContext.Key)))
+#endif
+ {
+ this.session.InflightMessages.Remove(msgContext.Key);
+ }
+
+ internalEvent = new MsgPublishedInternalEvent(msgInflight, false);
+
+ // notify not received acknowledge from broker and message not published
+ this.OnInternalEvent(internalEvent);
+ }
+ // NOTE : not raise events for SUBACK or UNSUBACK not received
+ // for the user no event raised means subscribe/unsubscribe failed
+ }
}
else
{
@@ -1707,7 +2127,7 @@ namespace uPLibrary.Networking.M2Mqtt
this.inflightQueue.Enqueue(msgContext);
// update timeout
- int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp));
+ int msgTimeout = (this.settings.DelayOnRetry - delta);
timeout = (msgTimeout < timeout) ? msgTimeout : timeout;
}
}
@@ -1730,27 +2150,30 @@ namespace uPLibrary.Networking.M2Mqtt
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)
+ if (msgReceived.MessageId == msgInflight.MessageId)
{
lock (this.internalQueue)
{
// received message processed
this.internalQueue.Dequeue();
acknowledge = true;
+ msgReceivedProcessed = true;
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived);
+#endif
}
MqttMsgPubrel pubrel = new MqttMsgPubrel();
- pubrel.MessageId = ((MqttMsgPublish)msgInflight).MessageId;
+ pubrel.MessageId = msgInflight.MessageId;
msgContext.State = MqttMsgState.WaitForPubcomp;
msgContext.Timestamp = Environment.TickCount;
msgContext.Attempt = 1;
- this.Send(pubrel.GetBytes());
+ this.Send(pubrel);
- // update timeout
- int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp));
- timeout = (msgTimeout < timeout) ? msgTimeout : timeout;
+ // update timeout : minimum between delay (based on current message sent) or current timeout
+ timeout = (this.settings.DelayOnRetry < timeout) ? this.settings.DelayOnRetry : timeout;
// re-enqueue message
this.inflightQueue.Enqueue(msgContext);
@@ -1760,11 +2183,12 @@ namespace uPLibrary.Networking.M2Mqtt
// current message not acknowledged
if (!acknowledge)
{
+ delta = Environment.TickCount - msgContext.Timestamp;
// check timeout for receiving PUBREC since PUBLISH was sent
- if ((Environment.TickCount - msgContext.Timestamp) >= this.settings.DelayOnRetry)
+ if (delta >= this.settings.DelayOnRetry)
{
// max retry not reached, resend
- if (msgContext.Attempt <= this.settings.AttemptsOnRetry)
+ if (msgContext.Attempt < this.settings.AttemptsOnRetry)
{
msgContext.State = MqttMsgState.QueuedQos2;
@@ -1774,6 +2198,24 @@ namespace uPLibrary.Networking.M2Mqtt
// update timeout (0 -> reanalyze queue immediately)
timeout = 0;
}
+ else
+ {
+ // PUBREC not received in time, PUBLISH retries failed, need to remove from session inflight messages too
+ if ((this.session != null) &&
+#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ (this.session.InflightMessages.Contains(msgContext.Key)))
+#else
+ (this.session.InflightMessages.ContainsKey(msgContext.Key)))
+#endif
+ {
+ this.session.InflightMessages.Remove(msgContext.Key);
+ }
+
+ // if PUBREC for a PUBLISH message not received after retries, raise event for not published
+ internalEvent = new MsgPublishedInternalEvent(msgInflight, false);
+ // notify not received acknowledge from broker and message not published
+ this.OnInternalEvent(internalEvent);
+ }
}
else
{
@@ -1781,7 +2223,7 @@ namespace uPLibrary.Networking.M2Mqtt
this.inflightQueue.Enqueue(msgContext);
// update timeout
- int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp));
+ int msgTimeout = (this.settings.DelayOnRetry - delta);
timeout = (msgTimeout < timeout) ? msgTimeout : timeout;
}
}
@@ -1803,21 +2245,42 @@ namespace uPLibrary.Networking.M2Mqtt
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)
+ if (msgReceived.MessageId == msgInflight.MessageId)
{
lock (this.internalQueue)
{
// received message processed
this.internalQueue.Dequeue();
+ msgReceivedProcessed = true;
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived);
+#endif
}
MqttMsgPubcomp pubcomp = new MqttMsgPubcomp();
- pubcomp.MessageId = ((MqttMsgPublish)msgInflight).MessageId;
+ pubcomp.MessageId = msgInflight.MessageId;
- this.Send(pubcomp.GetBytes());
+ this.Send(pubcomp);
+ internalEvent = new MsgInternalEvent(msgInflight);
// notify published message from broker and acknowledged
- this.OnMqttMsgReceived(msgInflight);
+ this.OnInternalEvent(internalEvent);
+
+ // PUBREL received (and PUBCOMP sent) for PUBLISH message with QoS Level 2, remove from session state
+ if ((msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) &&
+ (this.session != null) &&
+#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ (this.session.InflightMessages.Contains(msgContext.Key)))
+#else
+ (this.session.InflightMessages.ContainsKey(msgContext.Key)))
+#endif
+ {
+ this.session.InflightMessages.Remove(msgContext.Key);
+ }
+
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight);
+#endif
}
else
{
@@ -1849,25 +2312,69 @@ namespace uPLibrary.Networking.M2Mqtt
if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE))
{
// PUBCOMP message for the current message
- if (((MqttMsgPubcomp)msgReceived).MessageId == ((MqttMsgPublish)msgInflight).MessageId)
+ if (msgReceived.MessageId == msgInflight.MessageId)
{
lock (this.internalQueue)
{
// received message processed
this.internalQueue.Dequeue();
acknowledge = true;
+ msgReceivedProcessed = true;
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived);
+#endif
}
+ internalEvent = new MsgPublishedInternalEvent(msgReceived, true);
// notify received acknowledge from broker of a published message
- this.OnMqttMsgReceived(msgReceived);
+ this.OnInternalEvent(internalEvent);
+
+ // PUBCOMP received for PUBLISH message with QoS Level 2, remove from session state
+ if ((msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) &&
+ (this.session != null) &&
+#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ (this.session.InflightMessages.Contains(msgContext.Key)))
+#else
+ (this.session.InflightMessages.ContainsKey(msgContext.Key)))
+#endif
+ {
+ this.session.InflightMessages.Remove(msgContext.Key);
+ }
+
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "processed {0}", msgInflight);
+#endif
+ }
+ }
+ // it is a PUBREC message
+ else if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE))
+ {
+ // another PUBREC message for the current message due to a retransmitted PUBLISH
+ // I'm in waiting for PUBCOMP, so I can discard this PUBREC
+ if (msgReceived.MessageId == msgInflight.MessageId)
+ {
+ lock (this.internalQueue)
+ {
+ // received message processed
+ this.internalQueue.Dequeue();
+ acknowledge = true;
+ msgReceivedProcessed = true;
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0}", msgReceived);
+#endif
+
+ // re-enqueue message
+ this.inflightQueue.Enqueue(msgContext);
+ }
}
}
// current message not acknowledged
if (!acknowledge)
{
+ delta = Environment.TickCount - msgContext.Timestamp;
// check timeout for receiving PUBCOMP since PUBREL was sent
- if ((Environment.TickCount - msgContext.Timestamp) >= this.settings.DelayOnRetry)
+ if (delta >= this.settings.DelayOnRetry)
{
// max retry not reached, resend
if (msgContext.Attempt < this.settings.AttemptsOnRetry)
@@ -1880,6 +2387,24 @@ namespace uPLibrary.Networking.M2Mqtt
// update timeout (0 -> reanalyze queue immediately)
timeout = 0;
}
+ else
+ {
+ // PUBCOMP not received, PUBREL retries failed, need to remove from session inflight messages too
+ if ((this.session != null) &&
+#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ (this.session.InflightMessages.Contains(msgContext.Key)))
+#else
+ (this.session.InflightMessages.ContainsKey(msgContext.Key)))
+#endif
+ {
+ this.session.InflightMessages.Remove(msgContext.Key);
+ }
+
+ // if PUBCOMP for a PUBLISH message not received after retries, raise event for not published
+ internalEvent = new MsgPublishedInternalEvent(msgInflight, false);
+ // notify not received acknowledge from broker and message not published
+ this.OnInternalEvent(internalEvent);
+ }
}
else
{
@@ -1887,7 +2412,7 @@ namespace uPLibrary.Networking.M2Mqtt
this.inflightQueue.Enqueue(msgContext);
// update timeout
- int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp));
+ int msgTimeout = (this.settings.DelayOnRetry - delta);
timeout = (msgTimeout < timeout) ? msgTimeout : timeout;
}
}
@@ -1905,20 +2430,22 @@ namespace uPLibrary.Networking.M2Mqtt
if (msgContext.Flow == MqttMsgFlow.ToPublish)
{
MqttMsgPubrel pubrel = new MqttMsgPubrel();
- pubrel.MessageId = ((MqttMsgPublish)msgInflight).MessageId;
+ pubrel.MessageId = msgInflight.MessageId;
msgContext.State = MqttMsgState.WaitForPubcomp;
msgContext.Timestamp = Environment.TickCount;
msgContext.Attempt++;
- // retry ? set dup flag
- if (msgContext.Attempt > 1)
- pubrel.DupFlag = true;
+ // retry ? set dup flag [v3.1.1] no needed
+ if (this.ProtocolVersion == MqttProtocolVersion.Version_3_1)
+ {
+ if (msgContext.Attempt > 1)
+ pubrel.DupFlag = true;
+ }
+
+ this.Send(pubrel);
- this.Send(pubrel.GetBytes());
-
- // update timeout
- int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp));
- timeout = (msgTimeout < timeout) ? msgTimeout : timeout;
+ // update timeout : minimum between delay (based on current message sent) or current timeout
+ timeout = (this.settings.DelayOnRetry < timeout) ? this.settings.DelayOnRetry : timeout;
// re-enqueue message
this.inflightQueue.Enqueue(msgContext);
@@ -1939,29 +2466,126 @@ namespace uPLibrary.Networking.M2Mqtt
// if calculated timeout is MaxValue, it means that must be Infinite (-1)
if (timeout == Int32.MaxValue)
timeout = Timeout.Infinite;
+
+ // if message received is orphan, no corresponding message in inflight queue
+ // based on messageId, we need to remove from the queue
+ if ((msgReceived != null) && !msgReceivedProcessed)
+ {
+ this.internalQueue.Dequeue();
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Queuing, "dequeued {0} orphan", msgReceived);
+#endif
+ }
}
}
}
}
- catch (MqttCommunicationException)
+ catch (MqttCommunicationException e)
{
- this.Close();
+ // possible exception on Send, I need to re-enqueue not sent message
+ if (msgContext != null)
+ // re-enqueue message
+ this.inflightQueue.Enqueue(msgContext);
+
+#if TRACE
+ MqttUtility.Trace.WriteLine(TraceLevel.Error, "Exception occurred: {0}", e.ToString());
+#endif
// raise disconnection client event
- this.OnMqttMsgDisconnected();
+ this.OnConnectionClosing();
}
}
+ ///
+ /// Restore session
+ ///
+ private void RestoreSession()
+ {
+ // if not clean session
+ if (!this.CleanSession)
+ {
+ // there is a previous session
+ if (this.session != null)
+ {
+ lock (this.inflightQueue)
+ {
+ foreach (MqttMsgContext msgContext in this.session.InflightMessages.Values)
+ {
+ this.inflightQueue.Enqueue(msgContext);
+
+ // if it is a PUBLISH message to publish
+ if ((msgContext.Message.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) &&
+ (msgContext.Flow == MqttMsgFlow.ToPublish))
+ {
+ // it's QoS 1 and we haven't received PUBACK
+ if ((msgContext.Message.QosLevel == MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE) &&
+ (msgContext.State == MqttMsgState.WaitForPuback))
+ {
+ // we haven't received PUBACK, we need to resend PUBLISH message
+ msgContext.State = MqttMsgState.QueuedQos1;
+ }
+ // it's QoS 2
+ else if (msgContext.Message.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE)
+ {
+ // we haven't received PUBREC, we need to resend PUBLISH message
+ if (msgContext.State == MqttMsgState.WaitForPubrec)
+ {
+ msgContext.State = MqttMsgState.QueuedQos2;
+ }
+ // we haven't received PUBCOMP, we need to resend PUBREL for it
+ else if (msgContext.State == MqttMsgState.WaitForPubcomp)
+ {
+ msgContext.State = MqttMsgState.SendPubrel;
+ }
+ }
+ }
+ }
+ }
+
+ // unlock process inflight queue
+ this.inflightWaitHandle.Set();
+ }
+ else
+ {
+ // create new session
+ this.session = new MqttClientSession(this.ClientId);
+ }
+ }
+ // clean any previous session
+ else
+ {
+ if (this.session != null)
+ this.session.Clear();
+ }
+ }
+
+#if BROKER
+
+ ///
+ /// Load a given session
+ ///
+ /// MQTT Client session to load
+ public void LoadSession(MqttClientSession session)
+ {
+ // if not clean session
+ if (!this.CleanSession)
+ {
+ // set the session ...
+ this.session = session;
+ // ... and restore it
+ this.RestoreSession();
+ }
+ }
+#endif
+
///
/// 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;
+ // if 0 or max UInt16, it becomes 1 (first valid messageId)
+ this.messageIdCounter = ((this.messageIdCounter % UInt16.MaxValue) != 0) ? (ushort)(this.messageIdCounter + 1) : (ushort)1;
return this.messageIdCounter;
}
@@ -1990,10 +2614,19 @@ namespace uPLibrary.Networking.M2Mqtt
{
MqttMsgContext msgCtx = (MqttMsgContext)item;
return ((msgCtx.Message.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) &&
- (((MqttMsgPublish)msgCtx.Message).MessageId == this.MessageId) &&
+ (msgCtx.Message.MessageId == this.MessageId) &&
msgCtx.Flow == this.Flow);
}
}
}
+
+ ///
+ /// MQTT protocol version
+ ///
+ public enum MqttProtocolVersion
+ {
+ Version_3_1 = MqttMsgConnect.PROTOCOL_VERSION_V3_1,
+ Version_3_1_1 = MqttMsgConnect.PROTOCOL_VERSION_V3_1_1
+ }
}
diff --git a/M2Mqtt/MqttNetworkChannel.cs b/M2Mqtt/MqttNetworkChannel.cs
deleted file mode 100644
index 4455496..0000000
--- a/M2Mqtt/MqttNetworkChannel.cs
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
-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/MqttSecurity.cs b/M2Mqtt/MqttSecurity.cs
new file mode 100644
index 0000000..b19f108
--- /dev/null
+++ b/M2Mqtt/MqttSecurity.cs
@@ -0,0 +1,30 @@
+/*
+Copyright (c) 2013, 2014 Paolo Patierno
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
+*/
+
+namespace uPLibrary.Networking.M2Mqtt
+{
+ ///
+ /// Supported SSL/TLS protocol versions
+ ///
+ public enum MqttSslProtocols
+ {
+ None,
+ SSLv3,
+ TLSv1_0,
+ TLSv1_1,
+ TLSv1_2
+ }
+}
diff --git a/M2Mqtt/MqttSettings.cs b/M2Mqtt/MqttSettings.cs
index 9db3351..994f622 100644
--- a/M2Mqtt/MqttSettings.cs
+++ b/M2Mqtt/MqttSettings.cs
@@ -1,19 +1,17 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
namespace uPLibrary.Networking.M2Mqtt
@@ -27,14 +25,16 @@ namespace uPLibrary.Networking.M2Mqtt
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;
+ public const int MQTT_DEFAULT_TIMEOUT = 30000;
// max publish, subscribe and unsubscribe retry for QoS Level 1 or 2
public const int MQTT_ATTEMPTS_RETRY = 3;
// delay for retry publish, subscribe and unsubscribe for QoS Level 1 or 2
public const int MQTT_DELAY_RETRY = 10000;
// broker need to receive the first message (CONNECT)
// within a reasonable amount of time after TCP/IP connection
- public const int MQTT_CONNECT_TIMEOUT = 25000;
+ public const int MQTT_CONNECT_TIMEOUT = 30000;
+ // default inflight queue size
+ public const int MQTT_MAX_INFLIGHT_QUEUE_SIZE = int.MaxValue;
///
/// Listening connection port
@@ -66,6 +66,11 @@ namespace uPLibrary.Networking.M2Mqtt
///
public int DelayOnRetry { get; internal set; }
+ ///
+ /// Inflight queue size
+ ///
+ public int InflightQueueSize { get; set; }
+
///
/// Singleton instance of settings
///
@@ -93,6 +98,7 @@ namespace uPLibrary.Networking.M2Mqtt
this.AttemptsOnRetry = MQTT_ATTEMPTS_RETRY;
this.DelayOnRetry = MQTT_DELAY_RETRY;
this.TimeoutOnConnection = MQTT_CONNECT_TIMEOUT;
+ this.InflightQueueSize = MQTT_MAX_INFLIGHT_QUEUE_SIZE;
}
}
}
diff --git a/M2Mqtt/Net/Fx.cs b/M2Mqtt/Net/Fx.cs
new file mode 100644
index 0000000..3fcc505
--- /dev/null
+++ b/M2Mqtt/Net/Fx.cs
@@ -0,0 +1,36 @@
+/*
+Copyright (c) 2013, 2014 Paolo Patierno
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
+*/
+
+using System.Threading;
+
+namespace uPLibrary.Networking.M2Mqtt
+{
+ ///
+ /// Support methods fos specific framework
+ ///
+ public class Fx
+ {
+ public static void StartThread(ThreadStart threadStart)
+ {
+ new Thread(threadStart).Start();
+ }
+
+ public static void SleepThread(int millisecondsTimeout)
+ {
+ Thread.Sleep(millisecondsTimeout);
+ }
+ }
+}
diff --git a/M2Mqtt/Net/MqttNetworkChannel.cs b/M2Mqtt/Net/MqttNetworkChannel.cs
new file mode 100644
index 0000000..445bcc6
--- /dev/null
+++ b/M2Mqtt/Net/MqttNetworkChannel.cs
@@ -0,0 +1,472 @@
+/*
+Copyright (c) 2013, 2014 Paolo Patierno
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
+*/
+
+#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;
+using System;
+using System.Security.Authentication;
+using System.Net.Security;
+
+namespace uPLibrary.Networking.M2Mqtt
+{
+ ///
+ /// Channel to communicate over the network
+ ///
+ public class MqttNetworkChannel : IMqttNetworkChannel
+ {
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ private readonly RemoteCertificateValidationCallback userCertificateValidationCallback;
+ private readonly LocalCertificateSelectionCallback userCertificateSelectionCallback;
+#endif
+ // 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 (on client)
+ private X509Certificate caCert;
+ // Server certificate (on broker)
+ private X509Certificate serverCert;
+ // client certificate (on client)
+ private X509Certificate clientCert;
+
+ // SSL/TLS protocol version
+ private MqttSslProtocols sslProtocol;
+
+ ///
+ /// 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)
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ : this(socket, false, null, MqttSslProtocols.None, null, null)
+#else
+ : this(socket, false, null, MqttSslProtocols.None)
+#endif
+ {
+
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Socket opened with the client
+ /// Secure connection (SSL/TLS)
+ /// Server X509 certificate for secure connection
+ /// SSL/TLS protocol version
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party
+ /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication
+ public MqttNetworkChannel(Socket socket, bool secure, X509Certificate serverCert, MqttSslProtocols sslProtocol,
+ RemoteCertificateValidationCallback userCertificateValidationCallback,
+ LocalCertificateSelectionCallback userCertificateSelectionCallback)
+#else
+ public MqttNetworkChannel(Socket socket, bool secure, X509Certificate serverCert, MqttSslProtocols sslProtocol)
+#endif
+ {
+ this.socket = socket;
+ this.secure = secure;
+ this.serverCert = serverCert;
+ this.sslProtocol = sslProtocol;
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ this.userCertificateValidationCallback = userCertificateValidationCallback;
+ this.userCertificateSelectionCallback = userCertificateSelectionCallback;
+#endif
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Remote Host name
+ /// Remote port
+ public MqttNetworkChannel(string remoteHostName, int remotePort)
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ : this(remoteHostName, remotePort, false, null, null, MqttSslProtocols.None, null, null)
+#else
+ : this(remoteHostName, remotePort, false, null, null, MqttSslProtocols.None)
+#endif
+ {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Remote Host name
+ /// Remote port
+ /// Using SSL
+ /// CA certificate
+ /// Client certificate
+ /// SSL/TLS protocol version
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ /// A RemoteCertificateValidationCallback delegate responsible for validating the certificate supplied by the remote party
+ /// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication
+ public MqttNetworkChannel(string remoteHostName, int remotePort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol,
+ RemoteCertificateValidationCallback userCertificateValidationCallback,
+ LocalCertificateSelectionCallback userCertificateSelectionCallback)
+#else
+ public MqttNetworkChannel(string remoteHostName, int remotePort, bool secure, X509Certificate caCert, X509Certificate clientCert, MqttSslProtocols sslProtocol)
+#endif
+ {
+ IPAddress remoteIpAddress = null;
+ try
+ {
+ // check if remoteHostName is a valid IP address and get it
+ remoteIpAddress = IPAddress.Parse(remoteHostName);
+ }
+ catch
+ {
+ }
+
+ // in this case the parameter remoteHostName isn't a valid IP address
+ if (remoteIpAddress == null)
+ {
+ IPHostEntry hostEntry = Dns.GetHostEntry(remoteHostName);
+ if ((hostEntry != null) && (hostEntry.AddressList.Length > 0))
+ {
+ // check for the first address not null
+ // it seems that with .Net Micro Framework, the IPV6 addresses aren't supported and return "null"
+ int i = 0;
+ while (hostEntry.AddressList[i] == null) i++;
+ remoteIpAddress = hostEntry.AddressList[i];
+ }
+ else
+ {
+ throw new Exception("No address found for the remote host name");
+ }
+ }
+
+ this.remoteHostName = remoteHostName;
+ this.remoteIpAddress = remoteIpAddress;
+ this.remotePort = remotePort;
+ this.secure = secure;
+ this.caCert = caCert;
+ this.clientCert = clientCert;
+ this.sslProtocol = sslProtocol;
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK)
+ this.userCertificateValidationCallback = userCertificateValidationCallback;
+ this.userCertificateSelectionCallback = userCertificateSelectionCallback;
+#endif
+ }
+
+ ///
+ /// Connect to remote server
+ ///
+ public void Connect()
+ {
+ this.socket = new Socket(IPAddressUtility.GetAddressFamily(this.remoteIpAddress), SocketType.Stream, ProtocolType.Tcp);
+ // try connection to the broker
+ this.socket.Connect(new IPEndPoint(this.remoteIpAddress, this.remotePort));
+
+#if SSL
+ // secure channel requested
+ if (secure)
+ {
+ // create SSL stream
+#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
+ this.sslStream = new SslStream(this.socket);
+#else
+ this.netStream = new NetworkStream(this.socket);
+ this.sslStream = new SslStream(this.netStream, false, this.userCertificateValidationCallback, this.userCertificateSelectionCallback);
+#endif
+
+ // server authentication (SSL/TLS handshake)
+#if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
+ this.sslStream.AuthenticateAsClient(this.remoteHostName,
+ this.clientCert,
+ new X509Certificate[] { this.caCert },
+ SslVerification.CertificateRequired,
+ MqttSslUtility.ToSslPlatformEnum(this.sslProtocol));
+#else
+ X509CertificateCollection clientCertificates = null;
+ // check if there is a client certificate to add to the collection, otherwise it's null (as empty)
+ if (this.clientCert != null)
+ clientCertificates = new X509CertificateCollection(new X509Certificate[] { this.clientCert });
+
+ this.sslStream.AuthenticateAsClient(this.remoteHostName,
+ clientCertificates,
+ MqttSslUtility.ToSslPlatformEnum(this.sslProtocol),
+ false);
+
+#endif
+ }
+#endif
+ }
+
+ ///
+ /// 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);
+ this.sslStream.Flush();
+ return buffer.Length;
+ }
+ else
+ return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
+#else
+ return this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
+#endif
+ }
+
+ ///
+ /// Receive data from the network
+ ///
+ /// Data buffer for receiving data
+ /// Number of bytes received
+ public int Receive(byte[] buffer)
+ {
+#if SSL
+ if (this.secure)
+ {
+ // read all data needed (until fill buffer)
+ int idx = 0, read = 0;
+ while (idx < buffer.Length)
+ {
+ // fixed scenario with socket closed gracefully by peer/broker and
+ // Read return 0. Avoid infinite loop.
+ read = this.sslStream.Read(buffer, idx, buffer.Length - idx);
+ if (read == 0)
+ return 0;
+ idx += read;
+ }
+ return buffer.Length;
+ }
+ else
+ {
+ // read all data needed (until fill buffer)
+ int idx = 0, read = 0;
+ while (idx < buffer.Length)
+ {
+ // fixed scenario with socket closed gracefully by peer/broker and
+ // Read return 0. Avoid infinite loop.
+ read = this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None);
+ if (read == 0)
+ return 0;
+ idx += read;
+ }
+ return buffer.Length;
+ }
+#else
+ // read all data needed (until fill buffer)
+ int idx = 0, read = 0;
+ while (idx < buffer.Length)
+ {
+ // fixed scenario with socket closed gracefully by peer/broker and
+ // Read return 0. Avoid infinite loop.
+ read = this.socket.Receive(buffer, idx, buffer.Length - idx, SocketFlags.None);
+ if (read == 0)
+ return 0;
+ idx += read;
+ }
+ return buffer.Length;
+#endif
+ }
+
+ ///
+ /// Receive data from the network channel with a specified timeout
+ ///
+ /// Data buffer for receiving data
+ /// Timeout on receiving (in milliseconds)
+ /// Number of bytes received
+ public int Receive(byte[] buffer, int timeout)
+ {
+ // check data availability (timeout is in microseconds)
+ if (this.socket.Poll(timeout * 1000, SelectMode.SelectRead))
+ {
+ return this.Receive(buffer);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ ///
+ /// Close the network channel
+ ///
+ public void Close()
+ {
+#if SSL
+ if (this.secure)
+ {
+#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3)
+ this.netStream.Close();
+#endif
+ this.sslStream.Close();
+ }
+ this.socket.Close();
+#else
+ this.socket.Close();
+#endif
+ }
+
+ ///
+ /// Accept connection from a remote client
+ ///
+ public void Accept()
+ {
+#if SSL
+ // secure channel requested
+ if (secure)
+ {
+#if !(MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
+
+ this.netStream = new NetworkStream(this.socket);
+ this.sslStream = new SslStream(this.netStream, false, this.userCertificateValidationCallback, this.userCertificateSelectionCallback);
+
+ this.sslStream.AuthenticateAsServer(this.serverCert, false, MqttSslUtility.ToSslPlatformEnum(this.sslProtocol), false);
+#endif
+ }
+
+ return;
+#else
+ return;
+#endif
+ }
+ }
+
+ ///
+ /// IPAddress Utility class
+ ///
+ 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
+ }
+ }
+
+ ///
+ /// MQTT SSL utility class
+ ///
+ public static class MqttSslUtility
+ {
+#if (!MF_FRAMEWORK_VERSION_V4_2 && !MF_FRAMEWORK_VERSION_V4_3 && !COMPACT_FRAMEWORK)
+ public static SslProtocols ToSslPlatformEnum(MqttSslProtocols mqttSslProtocol)
+ {
+ switch (mqttSslProtocol)
+ {
+ case MqttSslProtocols.None:
+ return SslProtocols.None;
+ case MqttSslProtocols.SSLv3:
+ return SslProtocols.Ssl3;
+ case MqttSslProtocols.TLSv1_0:
+ return SslProtocols.Tls;
+ /*case MqttSslProtocols.TLSv1_1:
+ return SslProtocols.Tls11;
+ case MqttSslProtocols.TLSv1_2:
+ return SslProtocols.Tls12;*/
+ default:
+ throw new ArgumentException("SSL/TLS protocol version not supported");
+ }
+ }
+#elif (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3)
+ public static SslProtocols ToSslPlatformEnum(MqttSslProtocols mqttSslProtocol)
+ {
+ switch (mqttSslProtocol)
+ {
+ case MqttSslProtocols.None:
+ return SslProtocols.None;
+ case MqttSslProtocols.SSLv3:
+ return SslProtocols.SSLv3;
+ case MqttSslProtocols.TLSv1_0:
+ return SslProtocols.TLSv1;
+ case MqttSslProtocols.TLSv1_1:
+ case MqttSslProtocols.TLSv1_2:
+ default:
+ throw new ArgumentException("SSL/TLS protocol version not supported");
+ }
+ }
+#endif
+ }
+}
diff --git a/M2Mqtt/Properties/AssemblyInfo.cs b/M2Mqtt/Properties/AssemblyInfo.cs
index f99a6c1..b218618 100644
--- a/M2Mqtt/Properties/AssemblyInfo.cs
+++ b/M2Mqtt/Properties/AssemblyInfo.cs
@@ -1,46 +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.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")]
+/*
+Copyright (c) 2013, 2014 Paolo Patierno
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
+*/
+
+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("4.3.0.0")]
+// to avoid compilation error (AssemblyFileVersionAttribute doesn't exist) under .Net CF 3.5
+#if !WindowsCE
+[assembly: AssemblyFileVersion("4.3.0.0")]
#endif
\ No newline at end of file
diff --git a/M2Mqtt/Session/MqttBrokerSession.cs b/M2Mqtt/Session/MqttBrokerSession.cs
new file mode 100644
index 0000000..859213c
--- /dev/null
+++ b/M2Mqtt/Session/MqttBrokerSession.cs
@@ -0,0 +1,65 @@
+/*
+Copyright (c) 2013, 2014 Paolo Patierno
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
+*/
+
+#if BROKER
+using System.Collections;
+using System.Collections.Generic;
+using uPLibrary.Networking.M2Mqtt.Managers;
+using uPLibrary.Networking.M2Mqtt.Messages;
+
+namespace uPLibrary.Networking.M2Mqtt.Session
+{
+ ///
+ /// MQTT Broker Session
+ ///
+ public class MqttBrokerSession : MqttSession
+ {
+ ///
+ /// Client related to the subscription
+ ///
+ public MqttClient Client { get; set; }
+
+ ///
+ /// Subscriptions for the client session
+ ///
+ public List Subscriptions;
+
+ ///
+ /// Outgoing messages to publish
+ ///
+ public Queue OutgoingMessages;
+
+ ///
+ /// Constructor
+ ///
+ public MqttBrokerSession()
+ : base()
+ {
+ this.Client = null;
+ this.Subscriptions = new List();
+ this.OutgoingMessages = new Queue();
+ }
+
+ public override void Clear()
+ {
+ base.Clear();
+ this.Client = null;
+ this.Subscriptions.Clear();
+ this.OutgoingMessages.Clear();
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/M2Mqtt/Session/MqttClientSession.cs b/M2Mqtt/Session/MqttClientSession.cs
new file mode 100644
index 0000000..7613880
--- /dev/null
+++ b/M2Mqtt/Session/MqttClientSession.cs
@@ -0,0 +1,33 @@
+/*
+Copyright (c) 2013, 2014 Paolo Patierno
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
+*/
+
+namespace uPLibrary.Networking.M2Mqtt.Session
+{
+ ///
+ /// MQTT Client Session
+ ///
+ public class MqttClientSession : MqttSession
+ {
+ ///
+ /// Constructor
+ ///
+ /// Client Id to create session
+ public MqttClientSession(string clientId)
+ : base(clientId)
+ {
+ }
+ }
+}
diff --git a/M2Mqtt/Session/MqttSession.cs b/M2Mqtt/Session/MqttSession.cs
new file mode 100644
index 0000000..712a854
--- /dev/null
+++ b/M2Mqtt/Session/MqttSession.cs
@@ -0,0 +1,63 @@
+/*
+Copyright (c) 2013, 2014 Paolo Patierno
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
+*/
+
+using System.Collections;
+
+namespace uPLibrary.Networking.M2Mqtt.Session
+{
+ ///
+ /// MQTT Session base class
+ ///
+ public abstract class MqttSession
+ {
+ ///
+ /// Client Id
+ ///
+ public string ClientId { get; set; }
+
+ ///
+ /// Messages inflight during session
+ ///
+ public Hashtable InflightMessages { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public MqttSession()
+ : this(null)
+ {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Client Id to create session
+ public MqttSession(string clientId)
+ {
+ this.ClientId = clientId;
+ this.InflightMessages = new Hashtable();
+ }
+
+ ///
+ /// Clean session
+ ///
+ public virtual void Clear()
+ {
+ this.ClientId = null;
+ this.InflightMessages.Clear();
+ }
+ }
+}
diff --git a/M2Mqtt/Utility/QueueExtension.cs b/M2Mqtt/Utility/QueueExtension.cs
index 988198f..70175c3 100644
--- a/M2Mqtt/Utility/QueueExtension.cs
+++ b/M2Mqtt/Utility/QueueExtension.cs
@@ -1,25 +1,23 @@
/*
-M2Mqtt - MQTT Client Library for .Net
-Copyright (c) 2014, Paolo Patierno, All rights reserved.
+Copyright (c) 2013, 2014 Paolo Patierno
-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.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
-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.
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
-You should have received a copy of the GNU Lesser General Public
-License along with this library.
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
*/
using System;
using System.Collections;
-namespace uPLibrary.Networking.M2Mqtt
+namespace uPLibrary.Networking.M2Mqtt.Utility
{
///
/// Extension class for a Queue
diff --git a/M2Mqtt/Utility/Trace.cs b/M2Mqtt/Utility/Trace.cs
new file mode 100644
index 0000000..c299ebe
--- /dev/null
+++ b/M2Mqtt/Utility/Trace.cs
@@ -0,0 +1,86 @@
+/*
+Copyright (c) 2013, 2014 Paolo Patierno
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
+*/
+
+using System.Diagnostics;
+
+namespace uPLibrary.Networking.M2Mqtt.Utility
+{
+ ///
+ /// Tracing levels
+ ///
+ public enum TraceLevel
+ {
+ Error = 0x01,
+ Warning = 0x02,
+ Information = 0x04,
+ Verbose = 0x0F,
+ Frame = 0x10,
+ Queuing = 0x20
+ }
+
+ // delegate for writing trace
+ public delegate void WriteTrace(string format, params object[] args);
+
+ ///
+ /// Tracing class
+ ///
+ public static class Trace
+ {
+ public static TraceLevel TraceLevel;
+ public static WriteTrace TraceListener;
+
+ [Conditional("DEBUG")]
+ public static void Debug(string format, params object[] args)
+ {
+ if (TraceListener != null)
+ {
+ TraceListener(format, args);
+ }
+ }
+
+ public static void WriteLine(TraceLevel level, string format)
+ {
+ if (TraceListener != null && (level & TraceLevel) > 0)
+ {
+ TraceListener(format);
+ }
+ }
+
+ public static void WriteLine(TraceLevel level, string format, object arg1)
+ {
+ if (TraceListener != null && (level & TraceLevel) > 0)
+ {
+ TraceListener(format, arg1);
+ }
+ }
+
+ public static void WriteLine(TraceLevel level, string format, object arg1, object arg2)
+ {
+ if (TraceListener != null && (level & TraceLevel) > 0)
+ {
+ TraceListener(format, arg1, arg2);
+ }
+ }
+
+ public static void WriteLine(TraceLevel level, string format, object arg1, object arg2, object arg3)
+ {
+ if (TraceListener != null && (level & TraceLevel) > 0)
+ {
+ TraceListener(format, arg1, arg2, arg3);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/M2Mqtt/WinRT/Fx.cs b/M2Mqtt/WinRT/Fx.cs
new file mode 100644
index 0000000..6d62d28
--- /dev/null
+++ b/M2Mqtt/WinRT/Fx.cs
@@ -0,0 +1,36 @@
+/*
+Copyright (c) 2013, 2014 Paolo Patierno
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
+*/
+
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace uPLibrary.Networking.M2Mqtt
+{
+ ///
+ /// Support methods fos specific framework
+ ///
+ public class Fx
+ {
+
+ public delegate void ThreadStart();
+ public static void StartThread(ThreadStart threadStart)
+ {
+ Task.Factory.StartNew(o => ((ThreadStart)o)(), threadStart);
+ }
+
+ public static void SleepThread(int millisecondsTimeout) { Task.Delay(millisecondsTimeout).RunSynchronously(); }
+ }
+}
diff --git a/M2Mqtt/WinRT/Hashtable.cs b/M2Mqtt/WinRT/Hashtable.cs
new file mode 100644
index 0000000..a42532b
--- /dev/null
+++ b/M2Mqtt/WinRT/Hashtable.cs
@@ -0,0 +1,27 @@
+/*
+Copyright (c) 2013, 2014 Paolo Patierno
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ http://www.eclipse.org/legal/epl-v10.html
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+Contributors:
+ Paolo Patierno - initial API and implementation and/or initial documentation
+*/
+
+using System.Collections.Generic;
+
+namespace uPLibrary.Networking.M2Mqtt
+{
+ ///
+ /// Wrapper Hashtable class for generic Dictionary (the only available in WinRT)
+ ///
+ public class Hashtable : Dictionary