diff --git a/M2Mqtt.sln b/M2Mqtt.sln index fc3baeaf..a88b7411 100644 --- a/M2Mqtt.sln +++ b/M2Mqtt.sln @@ -1,48 +1,59 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2009 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt.Net", "M2Mqtt\M2Mqtt.Net.csproj", "{A11AEF5A-B246-4FE8-8330-06DB73CC8074}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt.NetCf39", "M2Mqtt\M2Mqtt.NetCf39.csproj", "{BB9B7FF4-6502-41AF-8851-5060B67645E8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt.NetMf42", "M2Mqtt\M2Mqtt.NetMf42.csproj", "{F733523A-F14E-4F5A-9E7C-085CA80F52B1}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt.NetMf43", "M2Mqtt\M2Mqtt.NetMf43.csproj", "{6A6D540B-8554-4FFD-8884-8BEFCCD9AD41}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt.WinRT", "M2Mqtt\M2Mqtt.WinRT.csproj", "{0238F0E3-A02B-428D-8A3F-410D8F15BB50}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt.Universal", "M2Mqtt\M2Mqtt.Universal.csproj", "{7E4BD745-3633-44FE-B4DD-40F3350D66D2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|ARM.ActiveCfg = Debug|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|ARM.Build.0 = Debug|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|x64.ActiveCfg = Debug|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|x64.Build.0 = Debug|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|x86.ActiveCfg = Debug|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|x86.Build.0 = Debug|Any CPU {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.ActiveCfg = Release|Any CPU {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.Build.0 = Release|Any CPU - {BB9B7FF4-6502-41AF-8851-5060B67645E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BB9B7FF4-6502-41AF-8851-5060B67645E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BB9B7FF4-6502-41AF-8851-5060B67645E8}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {BB9B7FF4-6502-41AF-8851-5060B67645E8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BB9B7FF4-6502-41AF-8851-5060B67645E8}.Release|Any CPU.Build.0 = Release|Any CPU - {BB9B7FF4-6502-41AF-8851-5060B67645E8}.Release|Any CPU.Deploy.0 = Release|Any CPU - {F733523A-F14E-4F5A-9E7C-085CA80F52B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F733523A-F14E-4F5A-9E7C-085CA80F52B1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F733523A-F14E-4F5A-9E7C-085CA80F52B1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F733523A-F14E-4F5A-9E7C-085CA80F52B1}.Release|Any CPU.Build.0 = Release|Any CPU - {6A6D540B-8554-4FFD-8884-8BEFCCD9AD41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6A6D540B-8554-4FFD-8884-8BEFCCD9AD41}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6A6D540B-8554-4FFD-8884-8BEFCCD9AD41}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6A6D540B-8554-4FFD-8884-8BEFCCD9AD41}.Release|Any CPU.Build.0 = Release|Any CPU - {0238F0E3-A02B-428D-8A3F-410D8F15BB50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0238F0E3-A02B-428D-8A3F-410D8F15BB50}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0238F0E3-A02B-428D-8A3F-410D8F15BB50}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0238F0E3-A02B-428D-8A3F-410D8F15BB50}.Release|Any CPU.Build.0 = Release|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|ARM.ActiveCfg = Release|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|ARM.Build.0 = Release|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|x64.ActiveCfg = Release|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|x64.Build.0 = Release|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|x86.ActiveCfg = Release|Any CPU + {A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|x86.Build.0 = Release|Any CPU + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|Any CPU.ActiveCfg = Debug|x86 + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|ARM.ActiveCfg = Debug|ARM + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|ARM.Build.0 = Debug|ARM + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|x64.ActiveCfg = Debug|x64 + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|x64.Build.0 = Debug|x64 + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|x86.ActiveCfg = Debug|x86 + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Debug|x86.Build.0 = Debug|x86 + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|Any CPU.ActiveCfg = Release|x86 + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|ARM.ActiveCfg = Release|ARM + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|ARM.Build.0 = Release|ARM + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|x64.ActiveCfg = Release|x64 + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|x64.Build.0 = Release|x64 + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|x86.ActiveCfg = Release|x86 + {7E4BD745-3633-44FE-B4DD-40F3350D66D2}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B0CADC1E-0867-4C86-A1E6-1D3FABF29ACA} + EndGlobalSection EndGlobal diff --git a/M2Mqtt/IMqttNetworkChannel.cs b/M2Mqtt/IMqttNetworkChannel.cs index f4ff90fb..1149ce69 100644 --- a/M2Mqtt/IMqttNetworkChannel.cs +++ b/M2Mqtt/IMqttNetworkChannel.cs @@ -51,6 +51,10 @@ public interface IMqttNetworkChannel /// Number of byte sent int Send(byte[] buffer); +#if WINDOWS_UWP + System.Threading.Tasks.Task SendAsync(byte[] buffer); +#endif + /// /// Close the network channel /// @@ -60,10 +64,14 @@ public interface IMqttNetworkChannel /// Connect to remote server /// void Connect(); - + /// /// Accept client connection /// void Accept(); + +#if WINDOWS_UWP + System.Threading.Tasks.Task ConnectAsync(); +#endif } -} +} \ No newline at end of file diff --git a/M2Mqtt/M2Mqtt.Universal.csproj b/M2Mqtt/M2Mqtt.Universal.csproj new file mode 100644 index 00000000..f81f2c0c --- /dev/null +++ b/M2Mqtt/M2Mqtt.Universal.csproj @@ -0,0 +1,154 @@ + + + + + Debug + x86 + {7E4BD745-3633-44FE-B4DD-40F3350D66D2} + Library + Properties + uPLibrary.Networking.M2Mqtt + M2Mqtt.Universal + en-US + UAP + 10.0.15063.0 + 10.0.14393.0 + 14 + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + x86 + true + ..\bin\x86\Debug\M2Mqtt.Universal\ + TRACE;DEBUG;NETFX_CORE;WINDOWS_UWP;WINDOWS_APP;SSL + ;2008 + full + x86 + false + prompt + + + x86 + ..\bin\x86\Release\M2Mqtt.Universal\ + TRACE;NETFX_CORE;WINDOWS_UWP;WINDOWS_APP;SSL + true + ;2008 + pdbonly + x86 + false + prompt + + + ARM + true + ..\bin\ARM\Debug\M2Mqtt.Universal\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP;WINDOWS_APP;SSL + ;2008 + full + ARM + false + prompt + + + ARM + ..\bin\ARM\Release\M2Mqtt.Universal\ + TRACE;NETFX_CORE;WINDOWS_UWP;WINDOWS_APP;SSL + true + ;2008 + pdbonly + ARM + false + prompt + + + x64 + true + ..\bin\x64\Debug\M2Mqtt.Universal\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP;WINDOWS_APP;SSL + ;2008 + full + x64 + false + prompt + + + x64 + ..\bin\x64\Release\M2Mqtt.Universal\ + TRACE;NETFX_CORE;WINDOWS_UWP;WINDOWS_APP;SSL + true + ;2008 + pdbonly + x64 + false + prompt + + + PackageReference + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.4.0 + + + + 14.0 + + + + \ No newline at end of file diff --git a/M2Mqtt/MqttClient.cs b/M2Mqtt/MqttClient.cs index 40745b5e..3dadd826 100644 --- a/M2Mqtt/MqttClient.cs +++ b/M2Mqtt/MqttClient.cs @@ -167,7 +167,7 @@ public class MqttClient // event for peer/client disconnection public event ConnectionClosedEventHandler ConnectionClosed; - + // channel to communicate over the network private IMqttNetworkChannel channel; @@ -304,7 +304,7 @@ public MqttClient(string brokerHostName) : /// 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) + 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) @@ -476,9 +476,16 @@ private void Init(string brokerHostName, int brokerPort, bool secure, X509Certif /// /// Client identifier /// Return code of CONNACK message from broker +#if WINDOWS_UWP + public async System.Threading.Tasks.Task ConnectAsync(string clientId) + { + + return await this.ConnectAsync(clientId, null, null, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT); +#else public byte Connect(string clientId) { return this.Connect(clientId, null, null, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT); +#endif } /// @@ -488,11 +495,15 @@ public byte Connect(string clientId) /// Username /// Password /// Return code of CONNACK message from broker - public byte Connect(string clientId, - string username, - string password) +#if WINDOWS_UWP + public async System.Threading.Tasks.Task ConnectAsync(string clientId, string username, string password) + { + return await this.ConnectAsync(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT); +#else + public byte Connect(string clientId, string username, string password) { return this.Connect(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, true, MqttMsgConnect.KEEP_ALIVE_PERIOD_DEFAULT); +#endif } /// @@ -504,13 +515,15 @@ public byte Connect(string clientId, /// Clean sessione flag /// Keep alive period /// Return code of CONNACK message from broker - public byte Connect(string clientId, - string username, - string password, - bool cleanSession, - ushort keepAlivePeriod) +#if WINDOWS_UWP + public async System.Threading.Tasks.Task ConnectAsync(string clientId, string username, string password, bool cleanSession, ushort keepAlivePeriod) + { + return await this.ConnectAsync(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, cleanSession, keepAlivePeriod); +#else + public byte Connect(string clientId, string username, string password, bool cleanSession, ushort keepAlivePeriod) { return this.Connect(clientId, username, password, false, MqttMsgConnect.QOS_LEVEL_AT_MOST_ONCE, false, null, null, cleanSession, keepAlivePeriod); +#endif } /// @@ -527,16 +540,11 @@ public byte Connect(string clientId, /// Clean sessione flag /// Keep alive period /// Return code of CONNACK message from broker - public byte Connect(string clientId, - string username, - string password, - bool willRetain, - byte willQosLevel, - bool willFlag, - string willTopic, - string willMessage, - bool cleanSession, - ushort keepAlivePeriod) +#if WINDOWS_UWP + public async System.Threading.Tasks.Task ConnectAsync(string clientId, string username, string password, bool willRetain, byte willQosLevel, bool willFlag, string willTopic, string willMessage, bool cleanSession, ushort keepAlivePeriod) +#else + public byte Connect(string clientId, string username, string password, bool willRetain, byte willQosLevel, bool willFlag, string willTopic, string willMessage, bool cleanSession, ushort keepAlivePeriod) +#endif { // create CONNECT message MqttMsgConnect connect = new MqttMsgConnect(clientId, @@ -553,8 +561,12 @@ public byte Connect(string clientId, try { +#if WINDOWS_UWP + await this.channel.ConnectAsync(); +#else // connect to the broker this.channel.Connect(); +#endif } catch (Exception ex) { @@ -566,7 +578,7 @@ public byte Connect(string clientId, this.isConnectionClosing = false; // start thread for receiving messages from broker Fx.StartThread(this.ReceiveThread); - + MqttMsgConnack connack = (MqttMsgConnack)this.SendReceive(connect); // if connection accepted, start keep alive timer and if (connack.ReturnCode == MqttMsgConnack.CONN_ACCEPTED) @@ -593,7 +605,7 @@ public byte Connect(string clientId, // start thread for raising received message event from broker Fx.StartThread(this.DispatchEventThread); - + // start thread for handling inflight messages queue to broker asynchronously (publish and acknowledge) Fx.StartThread(this.ProcessInflightThread); @@ -1000,7 +1012,12 @@ private void Send(byte[] msgBytes) try { // send message +#if WINDOWS_UWP + var task = this.channel.SendAsync(msgBytes); + task.GetAwaiter().GetResult(); +#else this.channel.Send(msgBytes); +#endif #if !BROKER // update last message sent ticks @@ -1052,7 +1069,12 @@ private MqttMsgBase SendReceive(byte[] msgBytes, int timeout) try { // send message +#if WINDOWS_UWP + var task = this.channel.SendAsync(msgBytes); + task.GetAwaiter().GetResult(); +#else this.channel.Send(msgBytes); +#endif // update last message sent ticks this.lastCommTime = Environment.TickCount; diff --git a/M2Mqtt/Net/MqttNetworkChannel.cs b/M2Mqtt/Net/MqttNetworkChannel.cs index 94a329d6..c7843263 100644 --- a/M2Mqtt/Net/MqttNetworkChannel.cs +++ b/M2Mqtt/Net/MqttNetworkChannel.cs @@ -26,6 +26,7 @@ Paolo Patierno - initial API and implementation and/or initial documentation using System.Net; using System.Security.Cryptography.X509Certificates; using System; +using System.Threading.Tasks; namespace uPLibrary.Networking.M2Mqtt { diff --git a/M2Mqtt/Properties/AssemblyInfo.cs b/M2Mqtt/Properties/AssemblyInfo.cs index b218618f..a9c24a64 100644 --- a/M2Mqtt/Properties/AssemblyInfo.cs +++ b/M2Mqtt/Properties/AssemblyInfo.cs @@ -41,4 +41,5 @@ Paolo Patierno - initial API and implementation and/or initial documentation // 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 +#endif +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/M2Mqtt/Properties/M2Mqtt.Universal.rd.xml b/M2Mqtt/Properties/M2Mqtt.Universal.rd.xml new file mode 100644 index 00000000..d65f60b9 --- /dev/null +++ b/M2Mqtt/Properties/M2Mqtt.Universal.rd.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/M2Mqtt/WinRT/MqttNetworkChannel.cs b/M2Mqtt/WinRT/MqttNetworkChannel.cs index 305992c1..fb171475 100644 --- a/M2Mqtt/WinRT/MqttNetworkChannel.cs +++ b/M2Mqtt/WinRT/MqttNetworkChannel.cs @@ -24,6 +24,8 @@ Paolo Patierno - initial API and implementation and/or initial documentation using System.Runtime.InteropServices.WindowsRuntime; using Windows.Storage.Streams; using System.Threading; +using Windows.Security.Cryptography.Certificates; +using System.Diagnostics; namespace uPLibrary.Networking.M2Mqtt { @@ -130,6 +132,14 @@ public int Send(byte[] buffer) return (int)this.socket.OutputStream.WriteAsync(buffer.AsBuffer()).AsTask().Result; } +#if WINDOWS_UWP + public async Task SendAsync(byte[] buffer) + { + var result = await this.socket.OutputStream.WriteAsync(buffer.AsBuffer()); + return (int)result; + } +#endif + public void Close() { this.socket.Dispose(); @@ -145,6 +155,51 @@ public void Connect() MqttSslUtility.ToSslPlatformEnum(this.sslProtocol)).AsTask().Wait(); } + public async Task ConnectAsync() + { + this.socket = new StreamSocket(); + int retryTime = 2; + while (retryTime > 0) + { + try + { + await socket.ConnectAsync(this.remoteHostName, this.remotePort.ToString(), MqttSslUtility.ToSslPlatformEnum(this.sslProtocol)); + break; + } + catch (Exception exception) + { + // If this is an unknown status it means that the error is fatal and retry will likely fail. + if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) + { + throw; + } + + // If the exception was caused by an SSL error that is ignorable we are going to prompt the user + // with an enumeration of the errors and ask for permission to ignore. + if (socket.Information.ServerCertificateErrorSeverity != SocketSslErrorSeverity.Ignorable) + { + Debug.WriteLine("Connect failed with error: " + exception.Message); + throw; + } +#if DEBUG + // ----------------------------------------------------------------------------------------------- + // WARNING: Only test applications should ignore SSL errors. + // In real applications, ignoring server certificate errors can lead to Man-In-The-Middle attacks. + // ----------------------------------------------------------------------------------------------- + + socket.Control.IgnorableServerCertificateErrors.Clear(); + + foreach (var ignorableError in socket.Information.ServerCertificateErrors) + { + socket.Control.IgnorableServerCertificateErrors.Add(ignorableError); + } +#endif + } + + retryTime -= 1; + } + } + public void Accept() { // TODO : SSL support with StreamSocket / StreamSocketListener seems to be NOT supported @@ -176,4 +231,4 @@ public static SocketProtectionLevel ToSslPlatformEnum(MqttSslProtocols mqttSslPr } } } -} +} \ No newline at end of file diff --git a/M2Mqtt/packages.config b/M2Mqtt/packages.config new file mode 100644 index 00000000..ee51c237 --- /dev/null +++ b/M2Mqtt/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/M2Mqtt/project.json b/M2Mqtt/project.json new file mode 100644 index 00000000..091353e9 --- /dev/null +++ b/M2Mqtt/project.json @@ -0,0 +1,9 @@ +{ + "framework": { + "net451": { + "dependencies": { + "System.Collections.Immutable": "1.3.1" + } + } + } +} \ No newline at end of file