Compare commits

...
35 Commits
Author SHA1 Message Date
Christian Kratky 312773d054 Add password protected code signing certificate to enable build process. 2017-12-22 12:02:25 +01:00
Christian Kratky 1ee48a978f Merge develop 2017-12-18 14:17:31 +01:00
Christian Kratky 5ddb6b77e2 Update documentation 2017-12-14 13:24:50 +01:00
Christian Kratky e0284886b5 Added the bound IP address to the server options. 2017-12-14 13:23:12 +01:00
Christian Kratky 44c07c2034 Fix broken ASP.NET Core integration. 2017-12-14 13:12:18 +01:00
Christian Kratky f6b38081fc Update documents. 2017-12-12 14:30:28 +01:00
Christian c570509628 Merge pull request #139 from FourOnes/develop
fixed: Compile Source Code Occur Errors On Mac Machine
2017-12-12 14:27:04 +01:00
limingru e98ff1d6d6 uncomment code 2017-12-12 17:26:16 +08:00
limingru 0f54663789 fixed: Compile Source Code Occur Errors On Mac Machine 2017-12-12 17:21:22 +08:00
Christian Kratky f91d935708 Add the ClientId to the application message interceptor. 2017-12-11 17:10:27 +01:00
Christian Kratky fb8b60fccc Add overload for subscribing. 2017-12-08 21:12:05 +01:00
Christian Kratky ad8506cc30 Update documents 2017-12-07 20:41:44 +01:00
Christian Kratky d6f88acb8d Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2017-12-07 20:37:49 +01:00
Christian Kratky 333fc4c387 Fixed an TLS issue in the MqttClientOptionsBuilder; Fix a subscription issue in the managed client. 2017-12-07 20:35:29 +01:00
Christian 5f9214e9a2 Merge pull request #134 from cvellan/patch-3
Serialized certificates loaded as X509Certificate2
2017-12-05 13:18:16 +01:00
cvellan f974dcb647 Serialized certificates loaded as X509Certificate2
Instances of X509Certificate2 passed into an MqttClientOptionsBuilder lose data and functionality by being loaded as an X509Certificate.
2017-12-05 10:13:07 +11:00
Christian Kratky 2cb1c544f5 Add extension method for subscriptions 2017-12-04 22:40:44 +01:00
Christian Kratky 644e948489 Fix keep alive checks in server 2017-12-04 13:52:14 +01:00
Christian Kratky d12d2648b7 Add extension for RPC calls; replace locks; Refactored storage in Managed Client 2017-12-04 10:21:37 +01:00
Christian Kratky 0ee3c8e135 Add statistics of last sent packets to the _ConnectedClient_. 2017-11-28 21:19:30 +01:00
Christian Kratky bc959a8c3d Performance optimizations 2017-11-27 00:25:45 +01:00
Christian Kratky 69781d822a Update docs, fix managed client connection issue 2017-11-26 22:57:56 +01:00
Christian Kratky 611fabcd01 Performance optimizations 2017-11-25 23:10:48 +01:00
Christian Kratky 24ab285066 Update project settings 2017-11-25 20:33:04 +01:00
Christian Kratky ee6ec14bf8 Add interface for server options. Add context class for connection validator (hide internal packets). 2017-11-25 20:12:50 +01:00
Christian Kratky f9d67e2cd2 Merge proces, enable signing of the assembly. 2017-11-25 19:55:26 +01:00
Christian a72516fdcc Update README.md 2017-11-25 19:50:31 +01:00
Christian Kratky ebfc6aab6b Fix nuget spec 2017-11-25 16:20:07 +01:00
Christian Kratky 9c20edd3d0 Update documents 2017-11-25 16:17:44 +01:00
Christian Kratky b02b0ccd7c Refactor some names. 2017-11-23 22:50:26 +01:00
Christian Kratky 1bcf7ec29c Fix ASP.NET Core breaking changes 2017-11-23 22:34:16 +01:00
Christian Kratky e54b4f58df Update version info. Fix typo. Add MqttServerOptionsBuilder. 2017-11-23 22:26:22 +01:00
Christian Kratky 30540729df Merge branch 'master' into develop 2017-11-22 22:31:02 +01:00
Christian Kratky 77b80dc53e Removed 3rd-Party libraries. 2017-11-22 22:30:55 +01:00
Christian Kratky 861d3dfa0f Fix version info 2017-11-21 21:00:48 +01:00
189 changed files with 2800 additions and 2350 deletions
+2
View File
@@ -291,3 +291,5 @@ __pycache__/
Build/nuget.exe Build/nuget.exe
*.js *.js
*.map *.map
/Tests/MQTTnet.TestApp.NetCore/RetainedMessages.json
+3 -3
View File
@@ -2,7 +2,7 @@
<package > <package >
<metadata> <metadata>
<id>MQTTnet.AspNetCore</id> <id>MQTTnet.AspNetCore</id>
<version>2.5.1</version> <version>2.6.0</version>
<authors>Christian Kratky</authors> <authors>Christian Kratky</authors>
<owners>Christian Kratky</owners> <owners>Christian Kratky</owners>
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl> <licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
@@ -10,13 +10,13 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl> <iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is a support library to integrate MQTTnet into AspNetCore.</description> <description>This is a support library to integrate MQTTnet into AspNetCore.</description>
<releaseNotes>* Updated to MQTTnet 2.5.1. <releaseNotes>* Updated to MQTTnet 2.6.0.
</releaseNotes> </releaseNotes>
<copyright>Copyright Christian Kratky 2016-2017</copyright> <copyright>Copyright Christian Kratky 2016-2017</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags> <tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies> <dependencies>
<group targetFramework="netstandard2.0"> <group targetFramework="netstandard2.0">
<dependency id="MQTTnet" version="2.5.1" /> <dependency id="MQTTnet" version="2.6.0" />
</group> </group>
</dependencies> </dependencies>
</metadata> </metadata>
+47
View File
@@ -0,0 +1,47 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>MQTTnet.Extensions.Rpc</id>
<version>2.6.0</version>
<authors>Christian Kratky</authors>
<owners>Christian Kratky</owners>
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is a extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
<releaseNotes>* Initial version.
</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2017</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<group targetFramework="netstandard2.0">
<dependency id="MQTTnet" version="2.6.0-rc1" />
</group>
<group targetFramework="netstandard1.3">
<dependency id="MQTTnet" version="2.6.0-rc1" />
</group>
<group targetFramework="netstandard2.0">
<dependency id="MQTTnet" version="2.6.0-rc1" />
</group>
<group targetFramework="uap10.0">
<dependency id="MQTTnet" version="2.6.0-rc1" />
</group>
<group targetFramework="net452">
<dependency id="MQTTnet" version="2.6.0-rc1" />
</group>
<group targetFramework="net461">
<dependency id="MQTTnet" version="2.6.0-rc1" />
</group>
</dependencies>
</metadata>
<files>
<!-- .NET Standard 2.0 -->
<file src="..\Frameworks\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\"/>
</files>
</package>
+24 -26
View File
@@ -2,7 +2,7 @@
<package > <package >
<metadata> <metadata>
<id>MQTTnet</id> <id>MQTTnet</id>
<version>2.5.2</version> <version>2.6.0</version>
<authors>Christian Kratky</authors> <authors>Christian Kratky</authors>
<owners>Christian Kratky</owners> <owners>Christian Kratky</owners>
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl> <licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
@@ -10,7 +10,26 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl> <iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description> <description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
<releaseNotes>* [Core] Refactored trace messages. <releaseNotes>* [Core] Merged projects (BREAKING CHANGE! But only namespace changes).
* [Core] Added a strong name for the assembly.
* [Core] Performance optimizations.
* [Core] Fixed a logging issue when dealing with IOExceptions.
* [Core] Fixed a typo in the global logger class (BREAKING CHANGE! Please find new example in Wiki).
* [Core] Added support for project compilation under macOS (Thanks to @FourOnes).
* [Client] The certificate for encryption is now loaded as a _X509Certificate2_ which prodives more data (Thanks to @cvellan).
* [Client] Fixed an issue in _ManagedClient_ which can cause the client to stop when publishing subscriptions.
* [Client] Fixed an issue in _ManagedClient_ which prevents changing the QoS of an existing subscription (BREAKING CHANGE!).
* [Client] Fixed an issue in _MqttClientOptionsBuilder_ which prevents adding TLS options to the client options when calling _Build()_. (Thanks to @cvellan).
* [Client] Added an overload for subscribing at both clients which reduces required code.
* [Client] Added the _ClientId_ to the application message interceptor context.
* [Server] The application message interceptor can now delete any received application message.
* [Server] Added a ConnectionValidator context to align with other APIs (BREAKING CHANGE! Please find new example in Wiki).
* [Server] Added an interface for the _MqttServerOptions_.
* [Server] Added packet statistics for the connected clients.
* [Server] Fixed a security issue which sends retained packages to a failed subscription.
* [Server] Fixed the response (MaximumQoS) of a subscription (Thanks to @redbeans2017).
* [Server] The keep alive timeouts are now checked for every client (Thanks to @RainerMueller82).
* [Server] The used IP addresses can be specified now (IPAddress.Any as default).
</releaseNotes> </releaseNotes>
<copyright>Copyright Christian Kratky 2016-2017</copyright> <copyright>Copyright Christian Kratky 2016-2017</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags> <tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
@@ -18,66 +37,45 @@
<group targetFramework="netstandard1.3"> <group targetFramework="netstandard1.3">
<dependency id="NETStandard.Library" version="1.3.0" /> <dependency id="NETStandard.Library" version="1.3.0" />
<dependency id="Microsoft.Extensions.DependencyInjection" version="1.0.2" />
<dependency id="Microsoft.Extensions.Logging" version="1.0.2" />
<dependency id="Microsoft.Extensions.Options" version="1.0.2" />
<dependency id="System.Net.Security" version="4.3.2" /> <dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" /> <dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.1" /> <dependency id="System.Net.WebSockets.Client" version="4.3.1" />
<dependency id="System.Threading.Thread" version="4.3.0" />
</group> </group>
<group targetFramework="netstandard2.0"> <group targetFramework="netstandard2.0">
<dependency id="NETStandard.Library" version="2.0.0" /> <dependency id="NETStandard.Library" version="2.0.0" />
<dependency id="Microsoft.Extensions.DependencyInjection" version="2.0.0" />
<dependency id="Microsoft.Extensions.Logging" version="2.0.0" />
<dependency id="Microsoft.Extensions.Options" version="2.0.0" />
<dependency id="System.Net.Security" version="4.3.2" /> <dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" /> <dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.1" /> <dependency id="System.Net.WebSockets.Client" version="4.3.1" />
<dependency id="System.Threading.Thread" version="4.3.0" />
</group> </group>
<group targetFramework="uap10.0"> <group targetFramework="uap10.0">
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="5.4.0" /> <dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="5.4.1" />
<dependency id="Microsoft.Extensions.DependencyInjection" version="1.1.1" />
<dependency id="Microsoft.Extensions.Logging" version="1.1.2" />
<dependency id="Microsoft.Extensions.Options" version="1.1.2" />
</group> </group>
<group targetFramework="net452"> <group targetFramework="net452">
<dependency id="Microsoft.Extensions.DependencyInjection" version="1.0.2" />
<dependency id="Microsoft.Extensions.Logging" version="1.0.2" />
<dependency id="Microsoft.Extensions.Options" version="1.0.2" />
</group> </group>
<group targetFramework="net461"> <group targetFramework="net461">
<dependency id="Microsoft.Extensions.DependencyInjection" version="2.0.0" />
<dependency id="Microsoft.Extensions.Logging" version="2.0.0" />
<dependency id="Microsoft.Extensions.Options" version="2.0.0" />
</group> </group>
</dependencies> </dependencies>
</metadata> </metadata>
<files> <files>
<!-- .NET Standard 1.3 --> <!-- .NET Standard 1.3 -->
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard1.3\MQTTnet.Core.*" target="lib\netstandard1.3\"/>
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/> <file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
<!-- .NET Standard 2.0 --> <!-- .NET Standard 2.0 -->
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard2.0\MQTTnet.Core.*" target="lib\netstandard2.0\"/>
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\"/> <file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\"/>
<!-- Universal Windows --> <!-- Universal Windows -->
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\uap10.0\MQTTnet.Core.*" target="lib\uap10.0\"/>
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\"/> <file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\"/>
<!-- .NET Framework --> <!-- .NET Framework -->
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net452\MQTTnet.Core.*" target="lib\net452\"/>
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net452\MQTTnet.*" target="lib\net452\"/> <file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net452\MQTTnet.*" target="lib\net452\"/>
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net461\MQTTnet.Core.*" target="lib\net461\"/>
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net461\MQTTnet.*" target="lib\net461\"/> <file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net461\MQTTnet.*" target="lib\net461\"/>
</files> </files>
+10
View File
@@ -8,16 +8,26 @@ $path = &$vswhere -latest -products * -requires Microsoft.Component.MSBuild -pro
if ($path) { if ($path) {
$msbuild = join-path $path 'MSBuild\15.0\Bin\MSBuild.exe' $msbuild = join-path $path 'MSBuild\15.0\Bin\MSBuild.exe'
# Build the core library
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m &$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m &$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m &$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m &$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m &$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
# Build the ASP.NET Core 2.0 extension
&$msbuild ..\Frameworks\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m &$msbuild ..\Frameworks\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
# Build the RPC extension
&$msbuild ..\Extensions\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
&$msbuild ..\Extensions\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
&$msbuild ..\Extensions\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
&$msbuild ..\Extensions\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
&$msbuild ..\Extensions\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
Remove-Item .\NuGet -Force -Recurse Remove-Item .\NuGet -Force -Recurse
New-Item -ItemType Directory -Force -Path .\NuGet New-Item -ItemType Directory -Force -Path .\NuGet
.\NuGet.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion .\NuGet.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
.\NuGet.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion .\NuGet.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
.\NuGet.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
} }
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461;uap10.0</TargetFrameworks>
<AssemblyVersion>0.0.0.0</AssemblyVersion>
<FileVersion>0.0.0.0</FileVersion>
<Product />
<Company />
<Authors />
<PackageId />
<Version>0.0.0.0</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
<NugetTargetMoniker>UAP,Version=v10.0</NugetTargetMoniker>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion>10.0.16299.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
<TargetFrameworkIdentifier>.NETCore</TargetFrameworkIdentifier>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
<DefineConstants>$(DefineConstants);WINDOWS_UWP</DefineConstants>
<LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Frameworks\MQTTnet.Netstandard\MQTTnet.NetStandard.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,101 @@
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using MQTTnet.Client;
using MQTTnet.Internal;
using MQTTnet.Protocol;
namespace MQTTnet.Extensions.Rpc
{
public sealed class MqttRpcClient : IDisposable
{
private const string ResponseTopic = "$MQTTnet.RPC/+/+/response";
private readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
private readonly IMqttClient _mqttClient;
private bool _isEnabled;
public MqttRpcClient(IMqttClient mqttClient)
{
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
}
public async Task EnableAsync()
{
await _mqttClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(ResponseTopic).WithAtLeastOnceQoS().Build());
_isEnabled = true;
}
public async Task DisableAsync()
{
await _mqttClient.UnsubscribeAsync(ResponseTopic);
_isEnabled = false;
}
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
if (methodName == null) throw new ArgumentNullException(nameof(methodName));
if (methodName.Contains("/") || methodName.Contains("+") || methodName.Contains("#"))
{
throw new ArgumentException("The method name cannot contain /, + or #.");
}
if (!_isEnabled)
{
throw new InvalidOperationException("The RPC client is not enabled.");
}
var requestTopic = $"$MQTTnet.RPC/{Guid.NewGuid():N}/{methodName}";
var responseTopic = requestTopic + "/response";
var requestMessage = new MqttApplicationMessageBuilder()
.WithTopic(requestTopic)
.WithPayload(payload)
.WithQualityOfServiceLevel(qualityOfServiceLevel)
.Build();
try
{
var tcs = new TaskCompletionSource<byte[]>();
if (!_waitingCalls.TryAdd(responseTopic, tcs))
{
throw new InvalidOperationException();
}
await _mqttClient.PublishAsync(requestMessage);
return await tcs.Task.TimeoutAfter(timeout);
}
finally
{
_waitingCalls.TryRemove(responseTopic, out _);
}
}
private void OnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs eventArgs)
{
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out TaskCompletionSource<byte[]> tcs))
{
return;
}
if (tcs.Task.IsCompleted || tcs.Task.IsCanceled)
{
return;
}
tcs.TrySetResult(eventArgs.ApplicationMessage.Payload);
}
public void Dispose()
{
foreach (var tcs in _waitingCalls)
{
tcs.Value.SetCanceled();
}
_waitingCalls.Clear();
}
}
}
@@ -0,0 +1 @@

@@ -1,8 +1,8 @@
using System; using System;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using MQTTnet.Core.Server;
using System.Linq; using System.Linq;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore namespace MQTTnet.AspNetCore
{ {
@@ -2,8 +2,17 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<AssemblyVersion>2.5.2.0</AssemblyVersion> <AssemblyVersion>0.0.0.0</AssemblyVersion>
<FileVersion>2.5.2.0</FileVersion> <FileVersion>0.0.0.0</FileVersion>
<Version>0.0.0.0</Version>
<Product />
<Company />
<Authors />
<PackageId />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -1,30 +1,26 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using MQTTnet.Adapter;
using Microsoft.Extensions.Options; using MQTTnet.Diagnostics;
using MQTTnet.Core.Adapter; using MQTTnet.Server;
using MQTTnet.Core.Server;
namespace MQTTnet.AspNetCore namespace MQTTnet.AspNetCore
{ {
public class MqttHostedServer : MqttServer, IHostedService public class MqttHostedServer : MqttServer, IHostedService
{ {
public MqttHostedServer( private readonly IMqttServerOptions _options;
IOptions<MqttServerOptions> options,
IEnumerable<IMqttServerAdapter> adapters, public MqttHostedServer(IMqttServerOptions options, IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger) : base(adapters, logger)
ILogger<MqttServer> logger,
MqttClientSessionsManager clientSessionsManager,
IMqttClientRetainedMessageManager clientRetainedMessageManager
)
: base(options, adapters, logger, clientSessionsManager, clientRetainedMessageManager)
{ {
_options = options ?? throw new ArgumentNullException(nameof(options));
} }
public Task StartAsync(CancellationToken cancellationToken) public Task StartAsync(CancellationToken cancellationToken)
{ {
return StartAsync(); return StartAsync(_options);
} }
public Task StopAsync(CancellationToken cancellationToken) public Task StopAsync(CancellationToken cancellationToken)
@@ -1,23 +1,18 @@
using System; using System;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Adapter; using MQTTnet.Adapter;
using MQTTnet.Core.Server; using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore namespace MQTTnet.AspNetCore
{ {
public class MqttWebSocketServerAdapter : IMqttServerAdapter, IDisposable public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter, IDisposable
{ {
private readonly IMqttCommunicationAdapterFactory _mqttCommunicationAdapterFactory;
public MqttWebSocketServerAdapter(IMqttCommunicationAdapterFactory mqttCommunicationAdapterFactory)
{
_mqttCommunicationAdapterFactory = mqttCommunicationAdapterFactory ?? throw new ArgumentNullException(nameof(mqttCommunicationAdapterFactory));
}
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted; public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
public Task StartAsync(MqttServerOptions options) public Task StartAsync(IMqttServerOptions options)
{ {
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -32,7 +27,7 @@ namespace MQTTnet.AspNetCore
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket)); if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
var channel = new MqttWebSocketServerChannel(webSocket); var channel = new MqttWebSocketServerChannel(webSocket);
var clientAdapter = _mqttCommunicationAdapterFactory.CreateServerCommunicationAdapter(channel); var clientAdapter = new MqttChannelAdapter(channel, new MqttPacketSerializer(), new MqttNetLogger());
var eventArgs = new MqttServerAdapterClientAcceptedEventArgs(clientAdapter); var eventArgs = new MqttServerAdapterClientAcceptedEventArgs(clientAdapter);
ClientAccepted?.Invoke(this, eventArgs); ClientAccepted?.Invoke(this, eventArgs);
@@ -3,12 +3,12 @@ using System.IO;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Channel; using MQTTnet.Channel;
using MQTTnet.Implementations; using MQTTnet.Implementations;
namespace MQTTnet.AspNetCore namespace MQTTnet.AspNetCore
{ {
public class MqttWebSocketServerChannel : IMqttCommunicationChannel, IDisposable public class MqttWebSocketServerChannel : IMqttChannel, IDisposable
{ {
private WebSocket _webSocket; private WebSocket _webSocket;
@@ -1,19 +1,23 @@
using Microsoft.Extensions.DependencyInjection; using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using MQTTnet.Core.Adapter; using MQTTnet.Adapter;
using MQTTnet.Core.Server; using MQTTnet.Diagnostics;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore namespace MQTTnet.AspNetCore
{ {
public static class ServiceCollectionExtensions public static class ServiceCollectionExtensions
{ {
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services) public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, IMqttServerOptions options)
{ {
services.AddMqttServerServices(); if (options == null) throw new ArgumentNullException(nameof(options));
services.AddSingleton(options);
services.AddSingleton<IMqttNetLogger>(new MqttNetLogger());
services.AddSingleton<MqttHostedServer>();
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>()); services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
services.AddSingleton<IMqttServer>(s => s.GetService<MqttHostedServer>()); services.AddSingleton<IMqttServer>(s => s.GetService<MqttHostedServer>());
services.AddSingleton<MqttHostedServer>();
services.AddSingleton<MqttWebSocketServerAdapter>(); services.AddSingleton<MqttWebSocketServerAdapter>();
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttWebSocketServerAdapter>()); services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttWebSocketServerAdapter>());
@@ -2,12 +2,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Packets; using MQTTnet.Packets;
using MQTTnet.Core.Serializer; using MQTTnet.Serializer;
namespace MQTTnet.Core.Adapter namespace MQTTnet.Adapter
{ {
public interface IMqttCommunicationAdapter public interface IMqttChannelAdapter
{ {
IMqttPacketSerializer PacketSerializer { get; } IMqttPacketSerializer PacketSerializer { get; }
@@ -1,14 +1,14 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Server; using MQTTnet.Server;
namespace MQTTnet.Core.Adapter namespace MQTTnet.Adapter
{ {
public interface IMqttServerAdapter public interface IMqttServerAdapter
{ {
event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted; event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
Task StartAsync(MqttServerOptions options); Task StartAsync(IMqttServerOptions options);
Task StopAsync(); Task StopAsync();
} }
} }
@@ -1,27 +1,30 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net.Sockets;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Channel; using MQTTnet.Channel;
using MQTTnet.Core.Exceptions; using MQTTnet.Diagnostics;
using MQTTnet.Core.Internal; using MQTTnet.Exceptions;
using MQTTnet.Core.Packets; using MQTTnet.Internal;
using MQTTnet.Core.Serializer; using MQTTnet.Packets;
using Microsoft.Extensions.Logging; using MQTTnet.Serializer;
namespace MQTTnet.Core.Adapter namespace MQTTnet.Adapter
{ {
public class MqttChannelCommunicationAdapter : IMqttCommunicationAdapter public class MqttChannelAdapter : IMqttChannelAdapter
{ {
private const uint ErrorOperationAborted = 0x800703E3; private const uint ErrorOperationAborted = 0x800703E3;
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private static readonly byte[] EmptyBody = new byte[0];
private readonly ILogger<MqttChannelCommunicationAdapter> _logger;
private readonly IMqttCommunicationChannel _channel;
public MqttChannelCommunicationAdapter(IMqttCommunicationChannel channel, IMqttPacketSerializer serializer, ILogger<MqttChannelCommunicationAdapter> logger) private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
private readonly IMqttNetLogger _logger;
private readonly IMqttChannel _channel;
public MqttChannelAdapter(IMqttChannel channel, IMqttPacketSerializer serializer, IMqttNetLogger logger)
{ {
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_channel = channel ?? throw new ArgumentNullException(nameof(channel)); _channel = channel ?? throw new ArgumentNullException(nameof(channel));
@@ -30,23 +33,23 @@ namespace MQTTnet.Core.Adapter
public IMqttPacketSerializer PacketSerializer { get; } public IMqttPacketSerializer PacketSerializer { get; }
public async Task ConnectAsync(TimeSpan timeout) public Task ConnectAsync(TimeSpan timeout)
{ {
_logger.LogInformation("Connecting [Timeout={0}]", timeout); _logger.Trace<MqttChannelAdapter>("Connecting [Timeout={0}]", timeout);
await ExecuteAndWrapExceptionAsync(() => _channel.ConnectAsync().TimeoutAfter(timeout)); return ExecuteAndWrapExceptionAsync(() => _channel.ConnectAsync().TimeoutAfter(timeout));
} }
public async Task DisconnectAsync(TimeSpan timeout) public Task DisconnectAsync(TimeSpan timeout)
{ {
_logger.LogInformation("Disconnecting [Timeout={0}]", timeout); _logger.Trace<MqttChannelAdapter>("Disconnecting [Timeout={0}]", timeout);
await ExecuteAndWrapExceptionAsync(() => _channel.DisconnectAsync().TimeoutAfter(timeout)); return ExecuteAndWrapExceptionAsync(() => _channel.DisconnectAsync().TimeoutAfter(timeout));
} }
public async Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets) public Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets)
{ {
await ExecuteAndWrapExceptionAsync(async () => return ExecuteAndWrapExceptionAsync(async () =>
{ {
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try try
@@ -58,10 +61,13 @@ namespace MQTTnet.Core.Adapter
continue; continue;
} }
_logger.LogInformation("TX >>> {0} [Timeout={1}]", packet, timeout); _logger.Trace<MqttChannelAdapter>("TX >>> {0} [Timeout={1}]", packet, timeout);
var writeBuffer = PacketSerializer.Serialize(packet); var chunks = PacketSerializer.Serialize(packet);
await _channel.SendStream.WriteAsync(writeBuffer, 0, writeBuffer.Length, cancellationToken).ConfigureAwait(false); foreach (var chunk in chunks)
{
await _channel.SendStream.WriteAsync(chunk.Array, chunk.Offset, chunk.Count, cancellationToken).ConfigureAwait(false);
}
} }
if (timeout > TimeSpan.Zero) if (timeout > TimeSpan.Zero)
@@ -85,36 +91,29 @@ namespace MQTTnet.Core.Adapter
MqttBasePacket packet = null; MqttBasePacket packet = null;
await ExecuteAndWrapExceptionAsync(async () => await ExecuteAndWrapExceptionAsync(async () =>
{ {
ReceivedMqttPacket receivedMqttPacket = null; ReceivedMqttPacket receivedMqttPacket;
try if (timeout > TimeSpan.Zero)
{ {
if (timeout > TimeSpan.Zero) receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
{
receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
}
else
{
receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).ConfigureAwait(false);
}
if (receivedMqttPacket == null || cancellationToken.IsCancellationRequested)
{
throw new TaskCanceledException();
}
packet = PacketSerializer.Deserialize(receivedMqttPacket);
if (packet == null)
{
throw new MqttProtocolViolationException("Received malformed packet.");
}
_logger.LogInformation("RX <<< {0}", packet);
} }
finally else
{ {
receivedMqttPacket?.Dispose(); receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).ConfigureAwait(false);
} }
});
if (receivedMqttPacket == null || cancellationToken.IsCancellationRequested)
{
throw new TaskCanceledException();
}
packet = PacketSerializer.Deserialize(receivedMqttPacket.Header, receivedMqttPacket.Body);
if (packet == null)
{
throw new MqttProtocolViolationException("Received malformed packet.");
}
_logger.Trace<MqttChannelAdapter>("RX <<< {0}", packet);
}).ConfigureAwait(false);
return packet; return packet;
} }
@@ -129,7 +128,7 @@ namespace MQTTnet.Core.Adapter
if (header.BodyLength == 0) if (header.BodyLength == 0)
{ {
return new ReceivedMqttPacket(header, new MemoryStream(0)); return new ReceivedMqttPacket(header, EmptyBody);
} }
var body = new byte[header.BodyLength]; var body = new byte[header.BodyLength];
@@ -140,8 +139,8 @@ namespace MQTTnet.Core.Adapter
var readBytesCount = await stream.ReadAsync(body, offset, body.Length - offset, cancellationToken).ConfigureAwait(false); var readBytesCount = await stream.ReadAsync(body, offset, body.Length - offset, cancellationToken).ConfigureAwait(false);
offset += readBytesCount; offset += readBytesCount;
} while (offset < header.BodyLength); } while (offset < header.BodyLength);
return new ReceivedMqttPacket(header, new MemoryStream(body, 0, body.Length)); return new ReceivedMqttPacket(header, body);
} }
private static async Task ExecuteAndWrapExceptionAsync(Func<Task> action) private static async Task ExecuteAndWrapExceptionAsync(Func<Task> action)
@@ -168,13 +167,25 @@ namespace MQTTnet.Core.Adapter
} }
catch (COMException comException) catch (COMException comException)
{ {
if ((uint)comException.HResult == ErrorOperationAborted) if ((uint) comException.HResult == ErrorOperationAborted)
{ {
throw new OperationCanceledException(); throw new OperationCanceledException();
} }
throw new MqttCommunicationException(comException); throw new MqttCommunicationException(comException);
} }
catch (IOException exception)
{
if (exception.InnerException is SocketException socketException)
{
if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
{
throw new OperationCanceledException();
}
}
throw new MqttCommunicationException(exception);
}
catch (Exception exception) catch (Exception exception)
{ {
throw new MqttCommunicationException(exception); throw new MqttCommunicationException(exception);
@@ -0,0 +1,17 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Packets;
namespace MQTTnet.Adapter
{
public static class MqttChannelAdapterExtensions
{
public static Task SendPacketsAsync(this IMqttChannelAdapter adapter, TimeSpan timeout, CancellationToken cancellationToken, params MqttBasePacket[] packets)
{
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
return adapter.SendPacketsAsync(timeout, cancellationToken, packets);
}
}
}
@@ -1,7 +1,7 @@
using MQTTnet.Core.Exceptions; using MQTTnet.Exceptions;
using MQTTnet.Core.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Core.Adapter namespace MQTTnet.Adapter
{ {
public class MqttConnectingFailedException : MqttCommunicationException public class MqttConnectingFailedException : MqttCommunicationException
{ {
@@ -1,16 +1,16 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MQTTnet.Core.Adapter namespace MQTTnet.Adapter
{ {
public class MqttServerAdapterClientAcceptedEventArgs : EventArgs public class MqttServerAdapterClientAcceptedEventArgs : EventArgs
{ {
public MqttServerAdapterClientAcceptedEventArgs(IMqttCommunicationAdapter client) public MqttServerAdapterClientAcceptedEventArgs(IMqttChannelAdapter client)
{ {
Client = client ?? throw new ArgumentNullException(nameof(client)); Client = client ?? throw new ArgumentNullException(nameof(client));
} }
public IMqttCommunicationAdapter Client { get; } public IMqttChannelAdapter Client { get; }
public Task SessionTask { get; set; } public Task SessionTask { get; set; }
} }
@@ -0,0 +1,18 @@
using System;
using MQTTnet.Packets;
namespace MQTTnet.Adapter
{
public class ReceivedMqttPacket
{
public ReceivedMqttPacket(MqttPacketHeader header, byte[] body)
{
Header = header ?? throw new ArgumentNullException(nameof(header));
Body = body ?? throw new ArgumentNullException(nameof(body));
}
public MqttPacketHeader Header { get; }
public byte[] Body { get; }
}
}
@@ -1,7 +1,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MQTTnet.Core namespace MQTTnet
{ {
public static class ApplicationMessagePublisherExtensions public static class ApplicationMessagePublisherExtensions
{ {
@@ -1,9 +1,9 @@
using System.Threading.Tasks; using System.IO;
using System.IO; using System.Threading.Tasks;
namespace MQTTnet.Core.Channel namespace MQTTnet.Channel
{ {
public interface IMqttCommunicationChannel public interface IMqttChannel
{ {
Stream SendStream { get; } Stream SendStream { get; }
Stream ReceiveStream { get; } Stream ReceiveStream { get; }
@@ -1,9 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Packets;
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public interface IMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher public interface IMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher
{ {
@@ -16,6 +15,6 @@ namespace MQTTnet.Core.Client
Task DisconnectAsync(); Task DisconnectAsync();
Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters); Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
Task UnsubscribeAsync(IEnumerable<string> topicFilters); Task UnsubscribeAsync(IEnumerable<string> topics);
} }
} }
@@ -0,0 +1,10 @@
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
namespace MQTTnet.Client
{
public interface IMqttClientAdapterFactory
{
IMqttChannelAdapter CreateClientAdapter(IMqttClientChannelOptions options, IMqttNetLogger logger);
}
}
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public interface IMqttClientChannelOptions public interface IMqttClientChannelOptions
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public interface IMqttClientCredentials public interface IMqttClientCredentials
{ {
@@ -0,0 +1,16 @@
using MQTTnet.Diagnostics;
using MQTTnet.ManagedClient;
namespace MQTTnet.Client
{
public interface IMqttClientFactory
{
IMqttClient CreateMqttClient();
IMqttClient CreateMqttClient(IMqttNetLogger logger);
IManagedMqttClient CreateManagedMqttClient();
IManagedMqttClient CreateManagedMqttClient(IMqttNetLogger logger);
}
}
@@ -1,16 +1,12 @@
using System; using System;
using MQTTnet.Core.Serializer; using MQTTnet.Serializer;
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public interface IMqttClientOptions public interface IMqttClientOptions
{ {
string ClientId { get; } string ClientId { get; }
/// <summary>
/// The LogId is used to create a scope to correlate logging. If no value is provided the ClientId is used instead
/// </summary>
string LogId { get; }
IMqttClientCredentials Credentials { get; } IMqttClientCredentials Credentials { get; }
bool CleanSession { get; } bool CleanSession { get; }
MqttApplicationMessage WillMessage { get; } MqttApplicationMessage WillMessage { get; }
@@ -3,34 +3,33 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Adapter; using MQTTnet.Adapter;
using MQTTnet.Core.Exceptions; using MQTTnet.Diagnostics;
using MQTTnet.Core.Internal; using MQTTnet.Exceptions;
using MQTTnet.Core.Packets; using MQTTnet.Internal;
using MQTTnet.Core.Protocol; using MQTTnet.Packets;
using Microsoft.Extensions.Logging; using MQTTnet.Protocol;
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public class MqttClient : IMqttClient public class MqttClient : IMqttClient, IDisposable
{ {
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>(); private readonly IMqttClientAdapterFactory _adapterFactory;
private readonly MqttPacketDispatcher _packetDispatcher; private readonly MqttPacketDispatcher _packetDispatcher;
private readonly IMqttCommunicationAdapterFactory _communicationAdapterFactory; private readonly IMqttNetLogger _logger;
private readonly ILogger<MqttClient> _logger;
private IMqttClientOptions _options; private IMqttClientOptions _options;
private bool _isReceivingPackets; private bool _isReceivingPackets;
private int _latestPacketIdentifier; private int _latestPacketIdentifier;
private CancellationTokenSource _cancellationTokenSource; private CancellationTokenSource _cancellationTokenSource;
private IMqttCommunicationAdapter _adapter; private IMqttChannelAdapter _adapter;
private IDisposable _scopeHandle;
public MqttClient(IMqttCommunicationAdapterFactory communicationAdapterFactory, ILogger<MqttClient> logger, MqttPacketDispatcher packetDispatcher) public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
{ {
_communicationAdapterFactory = communicationAdapterFactory ?? throw new ArgumentNullException(nameof(communicationAdapterFactory)); _adapterFactory = channelFactory ?? throw new ArgumentNullException(nameof(channelFactory));
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_packetDispatcher = packetDispatcher ?? throw new ArgumentNullException(nameof(packetDispatcher));
_packetDispatcher = new MqttPacketDispatcher(logger);
} }
public event EventHandler<MqttClientConnectedEventArgs> Connected; public event EventHandler<MqttClientConnectedEventArgs> Connected;
@@ -53,17 +52,16 @@ namespace MQTTnet.Core.Client
_latestPacketIdentifier = 0; _latestPacketIdentifier = 0;
_packetDispatcher.Reset(); _packetDispatcher.Reset();
_adapter = _communicationAdapterFactory.CreateClientCommunicationAdapter(options); _adapter = _adapterFactory.CreateClientAdapter(options.ChannelOptions, _logger);
_scopeHandle = _logger.BeginScope(options.LogId ?? options.ClientId); _logger.Trace<MqttClient>("Trying to connect with server.");
_logger.LogTrace("Trying to connect with server.");
await _adapter.ConnectAsync(_options.CommunicationTimeout).ConfigureAwait(false); await _adapter.ConnectAsync(_options.CommunicationTimeout).ConfigureAwait(false);
_logger.LogTrace("Connection with server established."); _logger.Trace<MqttClient>("Connection with server established.");
await StartReceivingPacketsAsync(_cancellationTokenSource.Token).ConfigureAwait(false); await StartReceivingPacketsAsync(_cancellationTokenSource.Token).ConfigureAwait(false);
var connectResponse = await AuthenticateAsync(options.WillMessage).ConfigureAwait(false); var connectResponse = await AuthenticateAsync(options.WillMessage).ConfigureAwait(false);
_logger.LogTrace("MQTT connection with server established."); _logger.Trace<MqttClient>("MQTT connection with server established.");
if (_options.KeepAlivePeriod != TimeSpan.Zero) if (_options.KeepAlivePeriod != TimeSpan.Zero)
{ {
@@ -76,7 +74,7 @@ namespace MQTTnet.Core.Client
} }
catch (Exception exception) catch (Exception exception)
{ {
_logger.LogError(new EventId(), exception, "Error while connecting with server."); _logger.Error<MqttClient>(exception, "Error while connecting with server.");
await DisconnectInternalAsync().ConfigureAwait(false); await DisconnectInternalAsync().ConfigureAwait(false);
throw; throw;
} }
@@ -133,7 +131,7 @@ namespace MQTTnet.Core.Client
TopicFilters = topicFilters.ToList() TopicFilters = topicFilters.ToList()
}; };
await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket); await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket).ConfigureAwait(false);
} }
public async Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages) public async Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages)
@@ -141,21 +139,21 @@ namespace MQTTnet.Core.Client
ThrowIfNotConnected(); ThrowIfNotConnected();
var publishPackets = applicationMessages.Select(m => m.ToPublishPacket()); var publishPackets = applicationMessages.Select(m => m.ToPublishPacket());
var packetGroups = publishPackets.GroupBy(p => p.QualityOfServiceLevel).OrderBy(g => g.Key);
foreach (var qosGroup in publishPackets.GroupBy(p => p.QualityOfServiceLevel)) foreach (var qosGroup in packetGroups)
{ {
var qosPackets = qosGroup.ToArray();
switch (qosGroup.Key) switch (qosGroup.Key)
{ {
case MqttQualityOfServiceLevel.AtMostOnce: case MqttQualityOfServiceLevel.AtMostOnce:
{ {
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier] // No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
await _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, qosPackets).ConfigureAwait(false); await _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, qosGroup).ConfigureAwait(false);
break; break;
} }
case MqttQualityOfServiceLevel.AtLeastOnce: case MqttQualityOfServiceLevel.AtLeastOnce:
{ {
foreach (var publishPacket in qosPackets) foreach (var publishPacket in qosGroup)
{ {
publishPacket.PacketIdentifier = GetNewPacketIdentifier(); publishPacket.PacketIdentifier = GetNewPacketIdentifier();
await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket).ConfigureAwait(false); await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket).ConfigureAwait(false);
@@ -165,7 +163,7 @@ namespace MQTTnet.Core.Client
} }
case MqttQualityOfServiceLevel.ExactlyOnce: case MqttQualityOfServiceLevel.ExactlyOnce:
{ {
foreach (var publishPacket in qosPackets) foreach (var publishPacket in qosGroup)
{ {
publishPacket.PacketIdentifier = GetNewPacketIdentifier(); publishPacket.PacketIdentifier = GetNewPacketIdentifier();
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket).ConfigureAwait(false); var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket).ConfigureAwait(false);
@@ -182,6 +180,11 @@ namespace MQTTnet.Core.Client
} }
} }
public void Dispose()
{
_cancellationTokenSource?.Dispose();
}
private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage) private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage)
{ {
var connectPacket = new MqttConnectPacket var connectPacket = new MqttConnectPacket
@@ -215,8 +218,6 @@ namespace MQTTnet.Core.Client
private async Task DisconnectInternalAsync() private async Task DisconnectInternalAsync()
{ {
_scopeHandle?.Dispose();
var clientWasConnected = IsConnected; var clientWasConnected = IsConnected;
IsConnected = false; IsConnected = false;
@@ -233,15 +234,15 @@ namespace MQTTnet.Core.Client
try try
{ {
await _adapter.DisconnectAsync(_options.CommunicationTimeout).ConfigureAwait(false); await _adapter.DisconnectAsync(_options.CommunicationTimeout).ConfigureAwait(false);
_logger.LogInformation("Disconnected from adapter."); _logger.Info<MqttClient>("Disconnected from adapter.");
} }
catch (Exception exception) catch (Exception exception)
{ {
_logger.LogWarning(new EventId(), exception, "Error while disconnecting from adapter."); _logger.Warning<MqttClient>(exception, "Error while disconnecting from adapter.");
} }
finally finally
{ {
_logger.LogInformation("Disconnected."); _logger.Info<MqttClient>("Disconnected.");
Disconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientWasConnected)); Disconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientWasConnected));
} }
} }
@@ -250,29 +251,29 @@ namespace MQTTnet.Core.Client
{ {
try try
{ {
_logger.LogInformation("Received <<< {0}", packet); _logger.Info<MqttClient>("Received <<< {0}", packet);
if (packet is MqttPublishPacket publishPacket)
{
await ProcessReceivedPublishPacketAsync(publishPacket).ConfigureAwait(false);
return;
}
if (packet is MqttPingReqPacket) if (packet is MqttPingReqPacket)
{ {
await SendAsync(new MqttPingRespPacket()); await SendAsync(new MqttPingRespPacket()).ConfigureAwait(false);
return; return;
} }
if (packet is MqttDisconnectPacket) if (packet is MqttDisconnectPacket)
{ {
await DisconnectAsync(); await DisconnectAsync().ConfigureAwait(false);
return;
}
if (packet is MqttPublishPacket publishPacket)
{
await ProcessReceivedPublishPacket(publishPacket);
return; return;
} }
if (packet is MqttPubRelPacket pubRelPacket) if (packet is MqttPubRelPacket pubRelPacket)
{ {
await ProcessReceivedPubRelPacket(pubRelPacket); await ProcessReceivedPubRelPacket(pubRelPacket).ConfigureAwait(false);
return; return;
} }
@@ -280,7 +281,7 @@ namespace MQTTnet.Core.Client
} }
catch (Exception exception) catch (Exception exception)
{ {
_logger.LogError(new EventId(), exception, "Unhandled exception while processing received packet."); _logger.Error<MqttClient>(exception, "Unhandled exception while processing received packet.");
} }
} }
@@ -293,36 +294,29 @@ namespace MQTTnet.Core.Client
} }
catch (Exception exception) catch (Exception exception)
{ {
_logger.LogError(new EventId(), exception, "Unhandled exception while handling application message."); _logger.Error<MqttClient>(exception, "Unhandled exception while handling application message.");
} }
} }
private async Task ProcessReceivedPublishPacket(MqttPublishPacket publishPacket) private Task ProcessReceivedPublishPacketAsync(MqttPublishPacket publishPacket)
{ {
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce) if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{ {
FireApplicationMessageReceivedEvent(publishPacket); FireApplicationMessageReceivedEvent(publishPacket);
return; return Task.FromResult(0);
} }
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{ {
FireApplicationMessageReceivedEvent(publishPacket); FireApplicationMessageReceivedEvent(publishPacket);
await SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }); return SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
return;
} }
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{ {
// QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery] // QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
lock (_unacknowledgedPublishPackets)
{
_unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
}
FireApplicationMessageReceivedEvent(publishPacket); FireApplicationMessageReceivedEvent(publishPacket);
await SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }); return SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier });
return;
} }
throw new MqttCommunicationException("Received a not supported QoS level."); throw new MqttCommunicationException("Received a not supported QoS level.");
@@ -330,11 +324,6 @@ namespace MQTTnet.Core.Client
private Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket) private Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket)
{ {
lock (_unacknowledgedPublishPackets)
{
_unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
}
return SendAsync(pubRelPacket.CreateResponse<MqttPubCompPacket>()); return SendAsync(pubRelPacket.CreateResponse<MqttPubCompPacket>());
} }
@@ -357,19 +346,14 @@ namespace MQTTnet.Core.Client
private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken) private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken)
{ {
_logger.LogInformation("Start sending keep alive packets."); _logger.Info<MqttClient>("Start sending keep alive packets.");
try try
{ {
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
await Task.Delay(_options.KeepAlivePeriod, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return;
}
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket()).ConfigureAwait(false); await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket()).ConfigureAwait(false);
await Task.Delay(_options.KeepAlivePeriod, cancellationToken).ConfigureAwait(false);
} }
} }
catch (OperationCanceledException) catch (OperationCanceledException)
@@ -388,23 +372,23 @@ namespace MQTTnet.Core.Client
return; return;
} }
_logger.LogWarning(new EventId(), exception, "MQTT communication exception while sending/receiving keep alive packets."); _logger.Warning<MqttClient>(exception, "MQTT communication exception while sending/receiving keep alive packets.");
await DisconnectInternalAsync().ConfigureAwait(false); await DisconnectInternalAsync().ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
_logger.LogWarning(new EventId(), exception, "Unhandled exception while sending/receiving keep alive packets."); _logger.Warning<MqttClient>(exception, "Unhandled exception while sending/receiving keep alive packets.");
await DisconnectInternalAsync().ConfigureAwait(false); await DisconnectInternalAsync().ConfigureAwait(false);
} }
finally finally
{ {
_logger.LogInformation("Stopped sending keep alive packets."); _logger.Info<MqttClient>("Stopped sending keep alive packets.");
} }
} }
private async Task ReceivePacketsAsync(CancellationToken cancellationToken) private async Task ReceivePacketsAsync(CancellationToken cancellationToken)
{ {
_logger.LogInformation("Start receiving packets."); _logger.Info<MqttClient>("Start receiving packets.");
try try
{ {
@@ -438,17 +422,17 @@ namespace MQTTnet.Core.Client
return; return;
} }
_logger.LogWarning(new EventId(), exception, "MQTT communication exception while receiving packets."); _logger.Warning<MqttClient>(exception, "MQTT communication exception while receiving packets.");
await DisconnectInternalAsync().ConfigureAwait(false); await DisconnectInternalAsync().ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
_logger.LogError(new EventId(), exception, "Unhandled exception while receiving packets."); _logger.Error<MqttClient>(exception, "Unhandled exception while receiving packets.");
await DisconnectInternalAsync().ConfigureAwait(false); await DisconnectInternalAsync().ConfigureAwait(false);
} }
finally finally
{ {
_logger.LogInformation(nameof(MqttClient), "Stopped receiving packets."); _logger.Info<MqttClient>("Stopped receiving packets.");
} }
} }
@@ -456,7 +440,7 @@ namespace MQTTnet.Core.Client
{ {
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run( Task.Run(
() => ProcessReceivedPacketAsync(packet), async () => await ProcessReceivedPacketAsync(packet).ConfigureAwait(false),
cancellationToken).ConfigureAwait(false); cancellationToken).ConfigureAwait(false);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
} }
@@ -467,7 +451,7 @@ namespace MQTTnet.Core.Client
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run( Task.Run(
async () => await ReceivePacketsAsync(cancellationToken), async () => await ReceivePacketsAsync(cancellationToken).ConfigureAwait(false),
cancellationToken).ConfigureAwait(false); cancellationToken).ConfigureAwait(false);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
@@ -481,7 +465,7 @@ namespace MQTTnet.Core.Client
{ {
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run( Task.Run(
async () => await SendKeepAliveMessagesAsync(cancellationToken), async () => await SendKeepAliveMessagesAsync(cancellationToken).ConfigureAwait(false),
cancellationToken).ConfigureAwait(false); cancellationToken).ConfigureAwait(false);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
} }
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public class MqttClientConnectResult public class MqttClientConnectResult
{ {
@@ -1,6 +1,6 @@
using System; using System;
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public class MqttClientConnectedEventArgs : EventArgs public class MqttClientConnectedEventArgs : EventArgs
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public class MqttClientCredentials : IMqttClientCredentials public class MqttClientCredentials : IMqttClientCredentials
{ {
@@ -1,6 +1,6 @@
using System; using System;
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public class MqttClientDisconnectedEventArgs : EventArgs public class MqttClientDisconnectedEventArgs : EventArgs
{ {
@@ -2,8 +2,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Protocol;
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public static class MqttClientExtensions public static class MqttClientExtensions
{ {
@@ -15,6 +16,14 @@ namespace MQTTnet.Core.Client
return client.SubscribeAsync(topicFilters.ToList()); return client.SubscribeAsync(topicFilters.ToList());
} }
public static Task<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
}
public static Task UnsubscribeAsync(this IMqttClient client, params string[] topicFilters) public static Task UnsubscribeAsync(this IMqttClient client, params string[] topicFilters)
{ {
if (client == null) throw new ArgumentNullException(nameof(client)); if (client == null) throw new ArgumentNullException(nameof(client));
@@ -1,7 +1,7 @@
using System; using System;
using MQTTnet.Core.Serializer; using MQTTnet.Serializer;
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public class MqttClientOptions : IMqttClientOptions public class MqttClientOptions : IMqttClientOptions
{ {
@@ -9,9 +9,6 @@ namespace MQTTnet.Core.Client
public string ClientId { get; set; } = Guid.NewGuid().ToString("N"); public string ClientId { get; set; } = Guid.NewGuid().ToString("N");
/// <inheritdoc />
public string LogId { get; set; }
public bool CleanSession { get; set; } = true; public bool CleanSession { get; set; } = true;
public IMqttClientCredentials Credentials { get; set; } = new MqttClientCredentials(); public IMqttClientCredentials Credentials { get; set; } = new MqttClientCredentials();
@@ -1,8 +1,8 @@
using System; using System;
using System.Linq; using System.Linq;
using MQTTnet.Core.Serializer; using MQTTnet.Serializer;
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public class MqttClientOptionsBuilder public class MqttClientOptionsBuilder
{ {
@@ -12,12 +12,6 @@ namespace MQTTnet.Core.Client
private MqttClientTlsOptions _tlsOptions; private MqttClientTlsOptions _tlsOptions;
public MqttClientOptionsBuilder WithLogId(string value)
{
_options.LogId = value;
return this;
}
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value) public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
{ {
_options.ProtocolVersion = value; _options.ProtocolVersion = value;
@@ -125,11 +119,11 @@ namespace MQTTnet.Core.Client
if (_tcpOptions != null) if (_tcpOptions != null)
{ {
_options.ChannelOptions = _tcpOptions; _tcpOptions.TlsOptions = _tlsOptions;
} }
else else if (_webSocketOptions != null)
{ {
_options.ChannelOptions = _webSocketOptions; _webSocketOptions.TlsOptions = _tlsOptions;
} }
} }
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public class MqttClientTcpOptions : IMqttClientChannelOptions public class MqttClientTcpOptions : IMqttClientChannelOptions
{ {
@@ -1,6 +1,6 @@
using System; using System;
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public static class MqttClientTcpOptionsExtensions public static class MqttClientTcpOptionsExtensions
{ {
@@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public class MqttClientTlsOptions public class MqttClientTlsOptions
{ {
@@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public class MqttClientWebSocketOptions : IMqttClientChannelOptions public class MqttClientWebSocketOptions : IMqttClientChannelOptions
{ {
@@ -1,20 +1,20 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Exceptions; using MQTTnet.Diagnostics;
using MQTTnet.Core.Internal; using MQTTnet.Exceptions;
using MQTTnet.Core.Packets; using MQTTnet.Internal;
using Microsoft.Extensions.Logging; using MQTTnet.Packets;
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public class MqttPacketDispatcher public class MqttPacketDispatcher
{ {
private readonly ConcurrentDictionary<Type, TaskCompletionSource<MqttBasePacket>> _packetByResponseType = new ConcurrentDictionary<Type, TaskCompletionSource<MqttBasePacket>>(); private readonly ConcurrentDictionary<Type, TaskCompletionSource<MqttBasePacket>> _packetByResponseType = new ConcurrentDictionary<Type, TaskCompletionSource<MqttBasePacket>>();
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>> _packetByResponseTypeAndIdentifier = new ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>>(); private readonly ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>> _packetByResponseTypeAndIdentifier = new ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>>();
private readonly ILogger<MqttPacketDispatcher> _logger; private readonly IMqttNetLogger _logger;
public MqttPacketDispatcher(ILogger<MqttPacketDispatcher> logger) public MqttPacketDispatcher(IMqttNetLogger logger)
{ {
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
} }
@@ -30,7 +30,7 @@ namespace MQTTnet.Core.Client
} }
catch (MqttCommunicationTimedOutException) catch (MqttCommunicationTimedOutException)
{ {
_logger.LogWarning("Timeout while waiting for packet of type '{0}'.", responseType.Name); _logger.Warning<MqttPacketDispatcher>("Timeout while waiting for packet of type '{0}'.", responseType.Name);
throw; throw;
} }
finally finally
@@ -1,7 +1,6 @@
using MQTTnet.Core.Packets; using MQTTnet.Protocol;
using MQTTnet.Core.Protocol;
namespace MQTTnet.Core.Client namespace MQTTnet.Client
{ {
public class MqttSubscribeResult public class MqttSubscribeResult
{ {
@@ -0,0 +1,21 @@
using System;
namespace MQTTnet.Diagnostics
{
public interface IMqttNetLogger
{
event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
void Trace<TSource>(string message, params object[] parameters);
void Info<TSource>(string message, params object[] parameters);
void Warning<TSource>(Exception exception, string message, params object[] parameters);
void Warning<TSource>(string message, params object[] parameters);
void Error<TSource>(Exception exception, string message, params object[] parameters);
void Error<TSource>(string message, params object[] parameters);
}
}
@@ -0,0 +1,18 @@
using System;
namespace MQTTnet.Diagnostics
{
public static class MqttNetGlobalLogger
{
public static event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
public static bool HasListeners => LogMessagePublished != null;
public static void Publish(MqttNetLogMessage logMessage)
{
if (logMessage == null) throw new ArgumentNullException(nameof(logMessage));
LogMessagePublished?.Invoke(null, new MqttNetLogMessagePublishedEventArgs(logMessage));
}
}
}
@@ -0,0 +1,13 @@
namespace MQTTnet.Diagnostics
{
public enum MqttNetLogLevel
{
Verbose,
Info,
Warning,
Error
}
}
@@ -1,12 +1,12 @@
using System; using System;
using Microsoft.Extensions.Logging;
namespace MQTTnet.Core.Diagnostics namespace MQTTnet.Diagnostics
{ {
public sealed class MqttNetTraceMessage public sealed class MqttNetLogMessage
{ {
public MqttNetTraceMessage(DateTime timestamp, int threadId, string source, LogLevel level, string message, Exception exception) public MqttNetLogMessage(string logId, DateTime timestamp, int threadId, string source, MqttNetLogLevel level, string message, Exception exception)
{ {
LogId = logId;
Timestamp = timestamp; Timestamp = timestamp;
ThreadId = threadId; ThreadId = threadId;
Source = source; Source = source;
@@ -15,13 +15,15 @@ namespace MQTTnet.Core.Diagnostics
Exception = exception; Exception = exception;
} }
public string LogId { get; }
public DateTime Timestamp { get; } public DateTime Timestamp { get; }
public int ThreadId { get; } public int ThreadId { get; }
public string Source { get; } public string Source { get; }
public LogLevel Level { get; } public MqttNetLogLevel Level { get; }
public string Message { get; } public string Message { get; }
@@ -0,0 +1,14 @@
using System;
namespace MQTTnet.Diagnostics
{
public sealed class MqttNetLogMessagePublishedEventArgs : EventArgs
{
public MqttNetLogMessagePublishedEventArgs(MqttNetLogMessage logMessage)
{
TraceMessage = logMessage ?? throw new ArgumentNullException(nameof(logMessage));
}
public MqttNetLogMessage TraceMessage { get; }
}
}
@@ -0,0 +1,74 @@
using System;
namespace MQTTnet.Diagnostics
{
public class MqttNetLogger : IMqttNetLogger
{
private readonly string _logId;
public MqttNetLogger(string logId = null)
{
_logId = logId;
}
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
public void Trace<TSource>(string message, params object[] parameters)
{
Publish<TSource>(MqttNetLogLevel.Verbose, null, message, parameters);
}
public void Info<TSource>(string message, params object[] parameters)
{
Publish<TSource>(MqttNetLogLevel.Info, null, message, parameters);
}
public void Warning<TSource>(Exception exception, string message, params object[] parameters)
{
Publish<TSource>(MqttNetLogLevel.Warning, exception, message, parameters);
}
public void Warning<TSource>(string message, params object[] parameters)
{
Warning<TSource>(null, message, parameters);
}
public void Error<TSource>(Exception exception, string message, params object[] parameters)
{
Publish<TSource>(MqttNetLogLevel.Error, exception, message, parameters);
}
public void Error<TSource>(string message, params object[] parameters)
{
Warning<TSource>(null, message, parameters);
}
private void Publish<TSource>(MqttNetLogLevel logLevel, Exception exception, string message, object[] parameters)
{
var hasLocalListeners = LogMessagePublished != null;
var hasGlobalListeners = MqttNetGlobalLogger.HasListeners;
if (!hasLocalListeners && !hasGlobalListeners)
{
return;
}
if (parameters.Length > 0)
{
message = string.Format(message, parameters);
}
var traceMessage = new MqttNetLogMessage(_logId, DateTime.Now, Environment.CurrentManagedThreadId, typeof(TSource).Name, logLevel, message, exception);
if (hasGlobalListeners)
{
MqttNetGlobalLogger.Publish(traceMessage);
}
if (hasLocalListeners)
{
LogMessagePublished?.Invoke(this, new MqttNetLogMessagePublishedEventArgs(traceMessage));
}
}
}
}
@@ -1,4 +1,4 @@
namespace MQTTnet namespace MQTTnet.Diagnostics
{ {
public static class TargetFrameworkInfoProvider public static class TargetFrameworkInfoProvider
{ {
@@ -1,6 +1,6 @@
using System; using System;
namespace MQTTnet.Core.Exceptions namespace MQTTnet.Exceptions
{ {
public class MqttCommunicationException : Exception public class MqttCommunicationException : Exception
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Exceptions namespace MQTTnet.Exceptions
{ {
public sealed class MqttCommunicationTimedOutException : MqttCommunicationException public sealed class MqttCommunicationTimedOutException : MqttCommunicationException
{ {
@@ -1,6 +1,6 @@
using System; using System;
namespace MQTTnet.Core.Exceptions namespace MQTTnet.Exceptions
{ {
public sealed class MqttProtocolViolationException : Exception public sealed class MqttProtocolViolationException : Exception
{ {
@@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MQTTnet.Core namespace MQTTnet
{ {
public interface IApplicationMessagePublisher public interface IApplicationMessagePublisher
{ {
@@ -1,7 +1,6 @@
using System; using System;
using MQTTnet.Core.Client;
namespace MQTTnet.Core namespace MQTTnet
{ {
public interface IApplicationMessageReceiver public interface IApplicationMessageReceiver
{ {
@@ -0,0 +1,26 @@
using System;
using MQTTnet.Adapter;
using MQTTnet.Client;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
namespace MQTTnet.Implementations
{
public class MqttClientAdapterFactory : IMqttClientAdapterFactory
{
public IMqttChannelAdapter CreateClientAdapter(IMqttClientChannelOptions options, IMqttNetLogger logger)
{
if (options == null) throw new ArgumentNullException(nameof(options));
switch (options)
{
case MqttClientTcpOptions tcpOptions:
return new MqttChannelAdapter(new MqttTcpChannel(tcpOptions), new MqttPacketSerializer(), logger);
case MqttClientWebSocketOptions webSocketOptions:
return new MqttChannelAdapter(new MqttWebSocketChannel(webSocketOptions), new MqttPacketSerializer(), logger);
default:
throw new NotSupportedException();
}
}
}
}
@@ -1,28 +1,27 @@
#if WINDOWS_UWP #if WINDOWS_UWP
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Server;
using Windows.Networking.Sockets; using Windows.Networking.Sockets;
using Microsoft.Extensions.Logging; using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Server;
namespace MQTTnet.Implementations namespace MQTTnet.Implementations
{ {
public class MqttServerAdapter : IMqttServerAdapter, IDisposable public class MqttServerAdapter : IMqttServerAdapter, IDisposable
{ {
private readonly ILogger<MqttServerAdapter> _logger; private readonly IMqttNetLogger _logger;
private readonly IMqttCommunicationAdapterFactory _communicationAdapterFactory;
private StreamSocketListener _defaultEndpointSocket; private StreamSocketListener _defaultEndpointSocket;
public MqttServerAdapter(ILogger<MqttServerAdapter> logger, IMqttCommunicationAdapterFactory communicationAdapterFactory) public MqttServerAdapter(IMqttNetLogger logger)
{ {
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_communicationAdapterFactory = communicationAdapterFactory ?? throw new ArgumentNullException(nameof(communicationAdapterFactory));
} }
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted; public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
public async Task StartAsync(MqttServerOptions options) public async Task StartAsync(IMqttServerOptions options)
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));
@@ -63,12 +62,12 @@ namespace MQTTnet.Implementations
{ {
try try
{ {
var clientAdapter = _communicationAdapterFactory.CreateServerCommunicationAdapter(new MqttTcpChannel(args.Socket)); var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket), new MqttPacketSerializer(), _logger);
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter)); ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
} }
catch (Exception exception) catch (Exception exception)
{ {
_logger.LogError(new EventId(), exception, "Error while accepting connection at default endpoint."); _logger.Error<MqttServerAdapter>(exception, "Error while accepting connection at default endpoint.");
} }
} }
} }
@@ -7,31 +7,30 @@ using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Adapter; using MQTTnet.Adapter;
using MQTTnet.Core.Server; using MQTTnet.Diagnostics;
using Microsoft.Extensions.Logging; using MQTTnet.Serializer;
using MQTTnet.Server;
namespace MQTTnet.Implementations namespace MQTTnet.Implementations
{ {
public class MqttServerAdapter : IMqttServerAdapter, IDisposable public class MqttServerAdapter : IMqttServerAdapter, IDisposable
{ {
private readonly ILogger<MqttServerAdapter> _logger; private readonly IMqttNetLogger _logger;
private readonly IMqttCommunicationAdapterFactory _communicationAdapterFactory;
private CancellationTokenSource _cancellationTokenSource; private CancellationTokenSource _cancellationTokenSource;
private Socket _defaultEndpointSocket; private Socket _defaultEndpointSocket;
private Socket _tlsEndpointSocket; private Socket _tlsEndpointSocket;
private X509Certificate2 _tlsCertificate; private X509Certificate2 _tlsCertificate;
public MqttServerAdapter(ILogger<MqttServerAdapter> logger, IMqttCommunicationAdapterFactory communicationAdapterFactory) public MqttServerAdapter(IMqttNetLogger logger)
{ {
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_communicationAdapterFactory = communicationAdapterFactory ?? throw new ArgumentNullException(nameof(communicationAdapterFactory));
} }
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted; public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
public Task StartAsync(MqttServerOptions options) public Task StartAsync(IMqttServerOptions options)
{ {
if (_cancellationTokenSource != null) throw new InvalidOperationException("Server is already started."); if (_cancellationTokenSource != null) throw new InvalidOperationException("Server is already started.");
@@ -40,10 +39,10 @@ namespace MQTTnet.Implementations
if (options.DefaultEndpointOptions.IsEnabled) if (options.DefaultEndpointOptions.IsEnabled)
{ {
_defaultEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp); _defaultEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
_defaultEndpointSocket.Bind(new IPEndPoint(IPAddress.Any, options.GetDefaultEndpointPort())); _defaultEndpointSocket.Bind(new IPEndPoint(options.DefaultEndpointOptions.BoundIPAddress, options.GetDefaultEndpointPort()));
_defaultEndpointSocket.Listen(options.ConnectionBacklog); _defaultEndpointSocket.Listen(options.ConnectionBacklog);
Task.Run(() => AcceptDefaultEndpointConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token); Task.Run(async () => await AcceptDefaultEndpointConnectionsAsync(_cancellationTokenSource.Token).ConfigureAwait(false), _cancellationTokenSource.Token).ConfigureAwait(false);
} }
if (options.TlsEndpointOptions.IsEnabled) if (options.TlsEndpointOptions.IsEnabled)
@@ -60,10 +59,10 @@ namespace MQTTnet.Implementations
} }
_tlsEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp); _tlsEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
_tlsEndpointSocket.Bind(new IPEndPoint(IPAddress.Any, options.GetTlsEndpointPort())); _tlsEndpointSocket.Bind(new IPEndPoint(options.TlsEndpointOptions.BoundIPAddress, options.GetTlsEndpointPort()));
_tlsEndpointSocket.Listen(options.ConnectionBacklog); _tlsEndpointSocket.Listen(options.ConnectionBacklog);
Task.Run(() => AcceptTlsEndpointConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token); Task.Run(async () => await AcceptTlsEndpointConnectionsAsync(_cancellationTokenSource.Token).ConfigureAwait(false), _cancellationTokenSource.Token).ConfigureAwait(false);
} }
return Task.FromResult(0); return Task.FromResult(0);
@@ -103,13 +102,29 @@ namespace MQTTnet.Implementations
#else #else
var clientSocket = await _defaultEndpointSocket.AcceptAsync().ConfigureAwait(false); var clientSocket = await _defaultEndpointSocket.AcceptAsync().ConfigureAwait(false);
#endif #endif
var clientAdapter = _communicationAdapterFactory.CreateServerCommunicationAdapter(new MqttTcpChannel(clientSocket, null));
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, null), new MqttPacketSerializer(), _logger);
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter)); ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
} }
catch (ObjectDisposedException)
{
// It can happen that the listener socket is accessed after the cancellation token is already set and the listener socket is disposed.
}
catch (SocketException exception)
{
if (exception.SocketErrorCode == SocketError.OperationAborted)
{
return;
}
_logger.Error<MqttServerAdapter>(exception, "Error while accepting connection at default endpoint.");
}
catch (Exception exception) catch (Exception exception)
{ {
_logger.LogError(new EventId(), exception, "Error while accepting connection at default endpoint."); _logger.Error<MqttServerAdapter>(exception, "Error while accepting connection at default endpoint.");
}
finally
{
//excessive CPU consumed if in endless loop of socket errors //excessive CPU consumed if in endless loop of socket errors
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
} }
@@ -130,14 +145,29 @@ namespace MQTTnet.Implementations
var sslStream = new SslStream(new NetworkStream(clientSocket)); var sslStream = new SslStream(new NetworkStream(clientSocket));
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false); await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false);
var clientAdapter = _communicationAdapterFactory.CreateServerCommunicationAdapter(new MqttTcpChannel(clientSocket, sslStream)); var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer(), _logger);
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter)); ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
} }
catch (ObjectDisposedException)
{
// It can happen that the listener socket is accessed after the cancellation token is already set and the listener socket is disposed.
}
catch (SocketException exception)
{
if (exception.SocketErrorCode == SocketError.OperationAborted)
{
return;
}
_logger.Error<MqttServerAdapter>(exception, "Error while accepting connection at default endpoint.");
}
catch (Exception exception) catch (Exception exception)
{ {
_logger.LogError(new EventId(), exception, "Error while accepting connection at TLS endpoint."); _logger.Error<MqttServerAdapter>(exception, "Error while accepting connection at TLS endpoint.");
}
finally
{
//excessive CPU consumed if in endless loop of socket errors //excessive CPU consumed if in endless loop of socket errors
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
} }
@@ -8,12 +8,12 @@ using System.Threading.Tasks;
using Windows.Networking; using Windows.Networking;
using Windows.Networking.Sockets; using Windows.Networking.Sockets;
using Windows.Security.Cryptography.Certificates; using Windows.Security.Cryptography.Certificates;
using MQTTnet.Core.Channel; using MQTTnet.Channel;
using MQTTnet.Core.Client; using MQTTnet.Client;
namespace MQTTnet.Implementations namespace MQTTnet.Implementations
{ {
public sealed class MqttTcpChannel : IMqttCommunicationChannel, IDisposable public sealed class MqttTcpChannel : IMqttChannel, IDisposable
{ {
private readonly MqttClientTcpOptions _options; private readonly MqttClientTcpOptions _options;
private StreamSocket _socket; private StreamSocket _socket;
@@ -5,14 +5,14 @@ using System.Net.Sockets;
using System.Security.Authentication; using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Channel;
using MQTTnet.Core.Client;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using MQTTnet.Channel;
using MQTTnet.Client;
namespace MQTTnet.Implementations namespace MQTTnet.Implementations
{ {
public sealed class MqttTcpChannel : IMqttCommunicationChannel, IDisposable public sealed class MqttTcpChannel : IMqttChannel, IDisposable
{ {
private readonly MqttClientTcpOptions _options; private readonly MqttClientTcpOptions _options;
@@ -70,7 +70,7 @@ namespace MQTTnet.Implementations
_sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback); _sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback);
await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false); await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
} }
CreateStreams(_socket, _sslStream); CreateStreams(_socket, _sslStream);
} }
@@ -130,7 +130,7 @@ namespace MQTTnet.Implementations
foreach (var certificate in options.TlsOptions.Certificates) foreach (var certificate in options.TlsOptions.Certificates)
{ {
certificates.Add(new X509Certificate(certificate)); certificates.Add(new X509Certificate2(certificate));
} }
return certificates; return certificates;
@@ -139,10 +139,7 @@ namespace MQTTnet.Implementations
private void CreateStreams(Socket socket, Stream sslStream) private void CreateStreams(Socket socket, Stream sslStream)
{ {
var stream = sslStream ?? new NetworkStream(socket); var stream = sslStream ?? new NetworkStream(socket);
//cannot use this as default buffering prevents from receiving the first connect message
//need two streams otherwise read and write have to be synchronized
//todo: if branch can be used with min dependency NetStandard1.6 //todo: if branch can be used with min dependency NetStandard1.6
#if NET452 || NET461 #if NET452 || NET461
SendStream = new BufferedStream(stream, BufferSize); SendStream = new BufferedStream(stream, BufferSize);
@@ -155,4 +152,4 @@ namespace MQTTnet.Implementations
} }
} }
#endif #endif
@@ -1,15 +1,15 @@
using MQTTnet.Core.Channel; using System;
using MQTTnet.Core.Client;
using System;
using System.IO; using System.IO;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Client;
namespace MQTTnet.Implementations namespace MQTTnet.Implementations
{ {
public sealed class MqttWebSocketChannel : IMqttCommunicationChannel, IDisposable public sealed class MqttWebSocketChannel : IMqttChannel, IDisposable
{ {
// ReSharper disable once MemberCanBePrivate.Global // ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
@@ -29,9 +29,16 @@ namespace MQTTnet.Implementations
public async Task ConnectAsync() public async Task ConnectAsync()
{ {
var uri = _options.Uri; var uri = _options.Uri;
if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase)) if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase) && !uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase))
{ {
uri = "ws://" + uri; if (_options.TlsOptions?.UseTls == false)
{
uri = "ws://" + uri;
}
else
{
uri = "wss://" + uri;
}
} }
_webSocket = new ClientWebSocket(); _webSocket = new ClientWebSocket();
@@ -5,7 +5,7 @@ using System.Linq;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Exceptions; using MQTTnet.Exceptions;
namespace MQTTnet.Implementations namespace MQTTnet.Implementations
{ {
@@ -1,6 +1,6 @@
using MQTTnet.Core.Packets; using MQTTnet.Packets;
namespace MQTTnet.Core.Internal namespace MQTTnet.Internal
{ {
internal static class MqttApplicationMessageExtensions internal static class MqttApplicationMessageExtensions
{ {
@@ -1,21 +1,23 @@
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Exceptions; using MQTTnet.Exceptions;
namespace MQTTnet.Core.Internal namespace MQTTnet.Internal
{ {
public static class TaskExtensions public static class TaskExtensions
{ {
public static async Task TimeoutAfter(this Task task, TimeSpan timeout) public static async Task TimeoutAfter(this Task task, TimeSpan timeout)
{ {
using (var cancellationTokenSource = new CancellationTokenSource()) if (task == null) throw new ArgumentNullException(nameof(task));
using (var timeoutCts = new CancellationTokenSource())
{ {
try try
{ {
var timeoutTask = Task.Delay(timeout, cancellationTokenSource.Token); var timeoutTask = Task.Delay(timeout, timeoutCts.Token);
var finishedTask = await Task.WhenAny(timeoutTask, task).ConfigureAwait(false); var finishedTask = await Task.WhenAny(timeoutTask, task).ConfigureAwait(false);
if (finishedTask == timeoutTask) if (finishedTask == timeoutTask)
{ {
throw new MqttCommunicationTimedOutException(); throw new MqttCommunicationTimedOutException();
@@ -33,18 +35,20 @@ namespace MQTTnet.Core.Internal
} }
finally finally
{ {
cancellationTokenSource.Cancel(); timeoutCts.Cancel();
} }
} }
} }
public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout) public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout)
{ {
using (var cancellationTokenSource = new CancellationTokenSource()) if (task == null) throw new ArgumentNullException(nameof(task));
using (var timeoutCts = new CancellationTokenSource())
{ {
try try
{ {
var timeoutTask = Task.Delay(timeout, cancellationTokenSource.Token); var timeoutTask = Task.Delay(timeout, timeoutCts.Token);
var finishedTask = await Task.WhenAny(timeoutTask, task).ConfigureAwait(false); var finishedTask = await Task.WhenAny(timeoutTask, task).ConfigureAwait(false);
if (finishedTask == timeoutTask) if (finishedTask == timeoutTask)
@@ -66,7 +70,7 @@ namespace MQTTnet.Core.Internal
} }
finally finally
{ {
cancellationTokenSource.Cancel(); timeoutCts.Cancel();
} }
} }
} }
@@ -1,17 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461;uap10.0</TargetFrameworks> <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">netstandard1.3;netstandard2.0;net452;net461;uap10.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.3;netstandard2.0</TargetFrameworks>
<AssemblyName>MQTTnet</AssemblyName> <AssemblyName>MQTTnet</AssemblyName>
<RootNamespace>MQTTnet</RootNamespace> <RootNamespace>MQTTnet</RootNamespace>
<AssemblyVersion>2.5.2.0</AssemblyVersion> <GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<FileVersion>2.5.2.0</FileVersion> <DebugType>Full</DebugType>
<AssemblyVersion>0.0.0.0</AssemblyVersion>
<FileVersion>0.0.0.0</FileVersion>
<Version>0.0.0.0</Version> <Version>0.0.0.0</Version>
<Company /> <Company />
<Product /> <Product />
<Description /> <Description />
<Authors /> <Authors />
<PackageId /> <PackageId />
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>codeSigningKey.pfx</AssemblyOriginatorKeyFile>
<DelaySign>false</DelaySign>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'"> <PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
@@ -24,54 +30,36 @@
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion> <TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
<DefineConstants>$(DefineConstants);WINDOWS_UWP</DefineConstants> <DefineConstants>$(DefineConstants);WINDOWS_UWP</DefineConstants>
<LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets> <LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
<ItemGroup> <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard1.3|AnyCPU'">
</ItemGroup> <DefineConstants>RELEASE;NETSTANDARD1_3</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
<PackageReference Include="System.Net.Security" Version="4.3.2" /> <PackageReference Include="System.Net.Security" Version="4.3.2" />
<PackageReference Include="System.Net.WebSockets" Version="4.3.0" /> <PackageReference Include="System.Net.WebSockets" Version="4.3.0" />
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.1" /> <PackageReference Include="System.Net.WebSockets.Client" Version="4.3.1" />
<PackageReference Include="System.Threading.Thread" Version="4.3.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="2.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.3'"> <ItemGroup Condition="'$(TargetFramework)'=='netstandard1.3'">
<PackageReference Include="System.Net.Security" Version="4.3.2" /> <PackageReference Include="System.Net.Security" Version="4.3.2" />
<PackageReference Include="System.Net.WebSockets" Version="4.3.0" /> <PackageReference Include="System.Net.WebSockets" Version="4.3.0" />
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.1" /> <PackageReference Include="System.Net.WebSockets.Client" Version="4.3.1" />
<PackageReference Include="System.Threading.Thread" Version="4.3.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='uap10.0'"> <ItemGroup Condition="'$(TargetFramework)'=='uap10.0'">
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="5.4.0" /> <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="5.4.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.2" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net452'"> <ItemGroup Condition="'$(TargetFramework)'=='net452'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net461'"> <ItemGroup Condition="'$(TargetFramework)'=='net461'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="2.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -1,9 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Client; using MQTTnet.Client;
namespace MQTTnet.Core.ManagedClient namespace MQTTnet.ManagedClient
{ {
public interface IManagedMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher public interface IManagedMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher
{ {
@@ -16,6 +16,6 @@ namespace MQTTnet.Core.ManagedClient
Task StopAsync(); Task StopAsync();
Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters); Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
Task UnsubscribeAsync(IEnumerable<TopicFilter> topicFilters); Task UnsubscribeAsync(IEnumerable<string> topics);
} }
} }
@@ -1,7 +1,7 @@
using System; using System;
using MQTTnet.Core.Client; using MQTTnet.Client;
namespace MQTTnet.Core.ManagedClient namespace MQTTnet.ManagedClient
{ {
public interface IManagedMqttClientOptions public interface IManagedMqttClientOptions
{ {
@@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MQTTnet.Core.ManagedClient namespace MQTTnet.ManagedClient
{ {
public interface IManagedMqttClientStorage public interface IManagedMqttClientStorage
{ {
@@ -4,29 +4,31 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Client; using MQTTnet.Client;
using MQTTnet.Core.Exceptions; using MQTTnet.Diagnostics;
using MQTTnet.Core.Protocol; using MQTTnet.Exceptions;
using Microsoft.Extensions.Logging; using MQTTnet.Protocol;
namespace MQTTnet.Core.ManagedClient namespace MQTTnet.ManagedClient
{ {
public class ManagedMqttClient : IManagedMqttClient public class ManagedMqttClient : IManagedMqttClient, IDisposable
{ {
private readonly ManagedMqttClientStorageManager _storageManager = new ManagedMqttClientStorageManager();
private readonly BlockingCollection<MqttApplicationMessage> _messageQueue = new BlockingCollection<MqttApplicationMessage>(); private readonly BlockingCollection<MqttApplicationMessage> _messageQueue = new BlockingCollection<MqttApplicationMessage>();
private readonly HashSet<TopicFilter> _subscriptions = new HashSet<TopicFilter>(); private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
private readonly SemaphoreSlim _subscriptionsSemaphore = new SemaphoreSlim(1, 1);
private readonly IMqttClient _mqttClient; private readonly IMqttClient _mqttClient;
private readonly ILogger<ManagedMqttClient> _logger; private readonly IMqttNetLogger _logger;
private CancellationTokenSource _connectionCancellationToken; private CancellationTokenSource _connectionCancellationToken;
private CancellationTokenSource _publishingCancellationToken; private CancellationTokenSource _publishingCancellationToken;
private ManagedMqttClientStorageManager _storageManager;
private IManagedMqttClientOptions _options; private IManagedMqttClientOptions _options;
private bool _subscriptionsNotPushed; private bool _subscriptionsNotPushed;
public ManagedMqttClient(ILogger<ManagedMqttClient> logger, IMqttClient mqttClient) public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
{ {
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient)); _mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
@@ -52,36 +54,29 @@ namespace MQTTnet.Core.ManagedClient
throw new NotSupportedException("The managed client does not support existing sessions."); throw new NotSupportedException("The managed client does not support existing sessions.");
} }
if (_connectionCancellationToken != null) if (_connectionCancellationToken != null) throw new InvalidOperationException("The managed client is already started.");
{
throw new InvalidOperationException("The managed client is already started.");
}
_options = options; _options = options;
await _storageManager.SetStorageAsync(_options.Storage).ConfigureAwait(false);
if (_options.Storage != null) if (_options.Storage != null)
{ {
var loadedMessages = await _options.Storage.LoadQueuedMessagesAsync().ConfigureAwait(false); _storageManager = new ManagedMqttClientStorageManager(_options.Storage);
foreach (var loadedMessage in loadedMessages) await _storageManager.LoadQueuedMessagesAsync().ConfigureAwait(false);
{
_messageQueue.Add(loadedMessage);
}
} }
_connectionCancellationToken = new CancellationTokenSource(); _connectionCancellationToken = new CancellationTokenSource();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Factory.StartNew(() => MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false); Task.Run(async () => await MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token).ConfigureAwait(false);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
_logger.LogInformation("Started"); _logger.Info<ManagedMqttClient>("Started");
} }
public Task StopAsync() public Task StopAsync()
{ {
_connectionCancellationToken?.Cancel(false); StopPublishing();
_connectionCancellationToken = null; StopMaintainingConnection();
while (_messageQueue.Any()) while (_messageQueue.Any())
{ {
@@ -90,50 +85,66 @@ namespace MQTTnet.Core.ManagedClient
return Task.FromResult(0); return Task.FromResult(0);
} }
public async Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages) public async Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages)
{ {
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages)); if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
foreach (var applicationMessage in applicationMessages) foreach (var applicationMessage in applicationMessages)
{ {
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false); if (_storageManager != null)
{
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
}
_messageQueue.Add(applicationMessage); _messageQueue.Add(applicationMessage);
} }
} }
public Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters) public async Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
{ {
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters)); if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
lock (_subscriptions) await _subscriptionsSemaphore.WaitAsync().ConfigureAwait(false);
try
{ {
foreach (var topicFilter in topicFilters) foreach (var topicFilter in topicFilters)
{ {
if (_subscriptions.Add(topicFilter)) _subscriptions[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
{ _subscriptionsNotPushed = true;
_subscriptionsNotPushed = true;
}
} }
} }
finally
return Task.FromResult(0); {
_subscriptionsSemaphore.Release();
}
} }
public Task UnsubscribeAsync(IEnumerable<TopicFilter> topicFilters) public async Task UnsubscribeAsync(IEnumerable<string> topics)
{ {
lock (_subscriptions) await _subscriptionsSemaphore.WaitAsync().ConfigureAwait(false);
try
{ {
foreach (var topicFilter in topicFilters) foreach (var topic in topics)
{ {
if (_subscriptions.Remove(topicFilter)) if (_subscriptions.Remove(topic))
{ {
_subscriptionsNotPushed = true; _subscriptionsNotPushed = true;
} }
} }
} }
finally
{
_subscriptionsSemaphore.Release();
}
}
return Task.FromResult(0); public void Dispose()
{
_messageQueue?.Dispose();
_subscriptionsSemaphore?.Dispose();
_connectionCancellationToken?.Dispose();
_publishingCancellationToken?.Dispose();
} }
private async Task MaintainConnectionAsync(CancellationToken cancellationToken) private async Task MaintainConnectionAsync(CancellationToken cancellationToken)
@@ -142,33 +153,48 @@ namespace MQTTnet.Core.ManagedClient
{ {
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
var connectionState = await ReconnectIfRequiredAsync().ConfigureAwait(false); await TryMaintainConnectionAsync(cancellationToken);
if (connectionState == ReconnectionResult.NotConnected) }
{ }
_publishingCancellationToken?.Cancel(false); catch (OperationCanceledException)
_publishingCancellationToken = null; {
}
catch (Exception exception)
{
_logger.Error<ManagedMqttClient>(exception, "Unhandled exception while maintaining connection.");
}
finally
{
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
_logger.Info<ManagedMqttClient>("Stopped");
}
}
await Task.Delay(_options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false); private async Task TryMaintainConnectionAsync(CancellationToken cancellationToken)
continue; {
} try
{
var connectionState = await ReconnectIfRequiredAsync().ConfigureAwait(false);
if (connectionState == ReconnectionResult.NotConnected)
{
StopPublishing();
await Task.Delay(_options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false);
return;
}
if (connectionState == ReconnectionResult.Reconnected || _subscriptionsNotPushed) if (connectionState == ReconnectionResult.Reconnected || _subscriptionsNotPushed)
{ {
await PushSubscriptionsAsync(); await PushSubscriptionsAsync().ConfigureAwait(false);
_publishingCancellationToken = new CancellationTokenSource(); StartPublishing();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Factory.StartNew(() => PublishQueuedMessagesAsync(_publishingCancellationToken.Token), _publishingCancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false); return;
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed }
continue; if (connectionState == ReconnectionResult.StillConnected)
} {
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
if (connectionState == ReconnectionResult.StillConnected)
{
await Task.Delay(100, _connectionCancellationToken.Token).ConfigureAwait(false); // Consider using the _Disconnected_ event here. (TaskCompletionSource)
}
} }
} }
catch (OperationCanceledException) catch (OperationCanceledException)
@@ -176,19 +202,14 @@ namespace MQTTnet.Core.ManagedClient
} }
catch (MqttCommunicationException exception) catch (MqttCommunicationException exception)
{ {
_logger.LogWarning(new EventId(), exception, "Communication exception while maintaining connection."); _logger.Warning<ManagedMqttClient>(exception, "Communication exception while maintaining connection.");
} }
catch (Exception exception) catch (Exception exception)
{ {
_logger.LogError(new EventId(), exception, "Unhandled exception while maintaining connection."); _logger.Error<ManagedMqttClient>(exception, "Unhandled exception while maintaining connection.");
}
finally
{
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
_logger.LogInformation("Stopped");
} }
} }
private async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken) private async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken)
{ {
try try
@@ -206,16 +227,19 @@ namespace MQTTnet.Core.ManagedClient
continue; continue;
} }
await TryPublishQueuedMessageAsync(message).ConfigureAwait(false); await TryPublishQueuedMessageAsync(message).ConfigureAwait(false);
await _storageManager.RemoveAsync(message).ConfigureAwait(false);
} }
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
} }
catch (Exception exception)
{
_logger.Error<ManagedMqttClient>(exception, "Unhandled exception while publishing queued application messages.");
}
finally finally
{ {
_logger.LogInformation("Stopped publishing messages"); _logger.Trace<ManagedMqttClient>("Stopped publishing messages.");
} }
} }
@@ -224,10 +248,15 @@ namespace MQTTnet.Core.ManagedClient
try try
{ {
await _mqttClient.PublishAsync(message).ConfigureAwait(false); await _mqttClient.PublishAsync(message).ConfigureAwait(false);
if (_storageManager != null)
{
await _storageManager.RemoveAsync(message).ConfigureAwait(false);
}
} }
catch (MqttCommunicationException exception) catch (MqttCommunicationException exception)
{ {
_logger.LogWarning(new EventId(), exception, "Publishing application message failed."); _logger.Warning<ManagedMqttClient>(exception, "Publishing application message failed.");
if (message.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) if (message.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{ {
@@ -236,22 +265,27 @@ namespace MQTTnet.Core.ManagedClient
} }
catch (Exception exception) catch (Exception exception)
{ {
_logger.LogError(new EventId(), exception, "Unhandled exception while publishing queued application message."); _logger.Error<ManagedMqttClient>(exception, "Unhandled exception while publishing queued application message.");
} }
} }
private async Task PushSubscriptionsAsync() private async Task PushSubscriptionsAsync()
{ {
_logger.LogInformation(nameof(ManagedMqttClient), "Synchronizing subscriptions"); _logger.Info<ManagedMqttClient>(nameof(ManagedMqttClient), "Synchronizing subscriptions");
List<TopicFilter> subscriptions; List<TopicFilter> subscriptions;
lock (_subscriptions) await _subscriptionsSemaphore.WaitAsync().ConfigureAwait(false);
try
{ {
subscriptions = _subscriptions.ToList(); subscriptions = _subscriptions.Select(i => new TopicFilter(i.Key, i.Value)).ToList();
_subscriptionsNotPushed = false; _subscriptionsNotPushed = false;
} }
finally
{
_subscriptionsSemaphore.Release();
}
if (!_subscriptions.Any()) if (!subscriptions.Any())
{ {
return; return;
} }
@@ -262,7 +296,7 @@ namespace MQTTnet.Core.ManagedClient
} }
catch (Exception exception) catch (Exception exception)
{ {
_logger.LogWarning(new EventId(), exception, "Synchronizing subscriptions failed"); _logger.Warning<ManagedMqttClient>(exception, "Synchronizing subscriptions failed");
_subscriptionsNotPushed = true; _subscriptionsNotPushed = true;
} }
} }
@@ -299,5 +333,35 @@ namespace MQTTnet.Core.ManagedClient
{ {
Connected?.Invoke(this, eventArgs); Connected?.Invoke(this, eventArgs);
} }
private void StartPublishing()
{
if (_publishingCancellationToken != null)
{
StopPublishing();
}
var cts = new CancellationTokenSource();
_publishingCancellationToken = cts;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(async () => await PublishQueuedMessagesAsync(cts.Token).ConfigureAwait(false), cts.Token).ConfigureAwait(false);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
}
private void StopPublishing()
{
_publishingCancellationToken?.Cancel(false);
_publishingCancellationToken?.Dispose();
_publishingCancellationToken = null;
}
private void StopMaintainingConnection()
{
_connectionCancellationToken?.Cancel(false);
_connectionCancellationToken?.Dispose();
_connectionCancellationToken = null;
}
} }
} }
@@ -1,7 +1,8 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Protocol;
namespace MQTTnet.Core.ManagedClient namespace MQTTnet.ManagedClient
{ {
public static class ManagedMqttClientExtensions public static class ManagedMqttClientExtensions
{ {
@@ -12,7 +13,15 @@ namespace MQTTnet.Core.ManagedClient
return managedClient.SubscribeAsync(topicFilters); return managedClient.SubscribeAsync(topicFilters);
} }
public static Task UnsubscribeAsync(this IManagedMqttClient managedClient, params TopicFilter[] topicFilters) public static Task SubscribeAsync(this IManagedMqttClient managedClient, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return managedClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
}
public static Task UnsubscribeAsync(this IManagedMqttClient managedClient, params string[] topicFilters)
{ {
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient)); if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
@@ -1,7 +1,7 @@
using System; using System;
using MQTTnet.Core.Client; using MQTTnet.Client;
namespace MQTTnet.Core.ManagedClient namespace MQTTnet.ManagedClient
{ {
public class ManagedMqttClientOptions : IManagedMqttClientOptions public class ManagedMqttClientOptions : IManagedMqttClientOptions
{ {
@@ -1,7 +1,7 @@
using System; using System;
using MQTTnet.Core.Client; using MQTTnet.Client;
namespace MQTTnet.Core.ManagedClient namespace MQTTnet.ManagedClient
{ {
public class ManagedMqttClientOptionsBuilder public class ManagedMqttClientOptionsBuilder
{ {
@@ -1,25 +1,27 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MQTTnet.Core.ManagedClient namespace MQTTnet.ManagedClient
{ {
public class ManagedMqttClientStorageManager public class ManagedMqttClientStorageManager
{ {
private readonly List<MqttApplicationMessage> _applicationMessages = new List<MqttApplicationMessage>(); private readonly List<MqttApplicationMessage> _applicationMessages = new List<MqttApplicationMessage>();
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
private IManagedMqttClientStorage _storage; private readonly IManagedMqttClientStorage _storage;
public async Task SetStorageAsync(IManagedMqttClientStorage storage) public ManagedMqttClientStorageManager(IManagedMqttClientStorage storage)
{ {
await _semaphore.WaitAsync().ConfigureAwait(false); _storage = storage ?? throw new ArgumentNullException(nameof(storage));
try }
public async Task LoadQueuedMessagesAsync()
{
var loadedMessages = await _storage.LoadQueuedMessagesAsync().ConfigureAwait(false);
foreach (var loadedMessage in loadedMessages)
{ {
_storage = storage; _applicationMessages.Add(loadedMessage);
}
finally
{
_semaphore.Release();
} }
} }
@@ -28,11 +30,6 @@ namespace MQTTnet.Core.ManagedClient
await _semaphore.WaitAsync().ConfigureAwait(false); await _semaphore.WaitAsync().ConfigureAwait(false);
try try
{ {
if (_storage == null)
{
return;
}
_applicationMessages.Add(applicationMessage); _applicationMessages.Add(applicationMessage);
await SaveAsync().ConfigureAwait(false); await SaveAsync().ConfigureAwait(false);
} }
@@ -47,11 +44,6 @@ namespace MQTTnet.Core.ManagedClient
await _semaphore.WaitAsync().ConfigureAwait(false); await _semaphore.WaitAsync().ConfigureAwait(false);
try try
{ {
if (_storage == null)
{
return;
}
var index = _applicationMessages.IndexOf(applicationMessage); var index = _applicationMessages.IndexOf(applicationMessage);
if (index == -1) if (index == -1)
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.ManagedClient namespace MQTTnet.ManagedClient
{ {
public enum ReconnectionResult public enum ReconnectionResult
{ {
@@ -1,7 +1,7 @@
using System; using System;
using MQTTnet.Core.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Core namespace MQTTnet
{ {
public sealed class MqttApplicationMessage public sealed class MqttApplicationMessage
{ {
@@ -2,10 +2,10 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using MQTTnet.Core.Exceptions; using MQTTnet.Exceptions;
using MQTTnet.Core.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Core namespace MQTTnet
{ {
public class MqttApplicationMessageBuilder public class MqttApplicationMessageBuilder
{ {
@@ -1,6 +1,6 @@
using System; using System;
namespace MQTTnet.Core namespace MQTTnet
{ {
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
{ {
+29 -107
View File
@@ -1,130 +1,52 @@
using System; using System;
using MQTTnet.Core.Adapter; using System.Collections.Generic;
using MQTTnet.Core.Client; using MQTTnet.Adapter;
using MQTTnet.Core.Serializer; using MQTTnet.Client;
using Microsoft.Extensions.Logging; using MQTTnet.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MQTTnet.Implementations; using MQTTnet.Implementations;
using MQTTnet.Core.ManagedClient; using MQTTnet.ManagedClient;
using MQTTnet.Core.Server; using MQTTnet.Server;
using MQTTnet.Core.Channel;
namespace MQTTnet namespace MQTTnet
{ {
public class MqttFactory : IMqttCommunicationAdapterFactory, IMqttClientSesssionFactory, IMqttClientFactory, IMqttServerFactory public class MqttFactory : IMqttClientFactory, IMqttServerFactory
{ {
private readonly IServiceProvider _serviceProvider;
public MqttFactory()
: this(BuildServiceProvider())
{
}
public MqttFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
public ILoggerFactory GetLoggerFactory()
{
return _serviceProvider.GetRequiredService<ILoggerFactory>();
}
public IMqttCommunicationAdapter CreateClientCommunicationAdapter(IMqttClientOptions options)
{
var logger = _serviceProvider.GetRequiredService<ILogger<MqttChannelCommunicationAdapter>>();
return new MqttChannelCommunicationAdapter(CreateCommunicationChannel(options.ChannelOptions), CreateSerializer(options.ProtocolVersion), logger);
}
public IMqttCommunicationAdapter CreateServerCommunicationAdapter(IMqttCommunicationChannel channel)
{
var serializer = _serviceProvider.GetRequiredService<IMqttPacketSerializer>();
var logger = _serviceProvider.GetRequiredService<ILogger<MqttChannelCommunicationAdapter>>();
return new MqttChannelCommunicationAdapter(channel, serializer, logger);
}
public IMqttCommunicationChannel CreateCommunicationChannel(IMqttClientChannelOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
switch (options)
{
case MqttClientTcpOptions tcpOptions:
return CreateTcpChannel(tcpOptions);
case MqttClientWebSocketOptions webSocketOptions:
return CreateWebSocketChannel(webSocketOptions);
default:
throw new NotSupportedException();
}
}
public MqttTcpChannel CreateTcpChannel(MqttClientTcpOptions tcpOptions)
{
return new MqttTcpChannel(tcpOptions);
}
public MqttWebSocketChannel CreateWebSocketChannel(MqttClientWebSocketOptions webSocketOptions)
{
return new MqttWebSocketChannel(webSocketOptions);
}
public MqttPacketSerializer CreateSerializer(MqttProtocolVersion protocolVersion)
{
return new MqttPacketSerializer
{
ProtocolVersion = protocolVersion
};
}
public MqttClientSession CreateClientSession(string clientId, MqttClientSessionsManager clientSessionsManager)
{
return new MqttClientSession(
clientId,
_serviceProvider.GetRequiredService<IOptions<MqttServerOptions>>(),
clientSessionsManager,
_serviceProvider.GetRequiredService<MqttClientSubscriptionsManager>(),
_serviceProvider.GetRequiredService<ILogger<MqttClientSession>>(),
_serviceProvider.GetRequiredService<ILogger<MqttClientPendingMessagesQueue>>());
}
public IMqttClient CreateMqttClient() public IMqttClient CreateMqttClient()
{ {
return _serviceProvider.GetRequiredService<IMqttClient>(); return CreateMqttClient(new MqttNetLogger());
}
public IMqttClient CreateMqttClient(IMqttNetLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
return new MqttClient(new MqttClientAdapterFactory(), logger);
} }
public IManagedMqttClient CreateManagedMqttClient() public IManagedMqttClient CreateManagedMqttClient()
{ {
return _serviceProvider.GetRequiredService<IManagedMqttClient>(); return new ManagedMqttClient(CreateMqttClient(), new MqttNetLogger());
}
public IManagedMqttClient CreateManagedMqttClient(IMqttNetLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
return new ManagedMqttClient(CreateMqttClient(), logger);
} }
public IMqttServer CreateMqttServer() public IMqttServer CreateMqttServer()
{ {
return _serviceProvider.GetRequiredService<IMqttServer>(); var logger = new MqttNetLogger();
return CreateMqttServer(new List<IMqttServerAdapter> { new MqttServerAdapter(logger) }, logger);
} }
public IMqttServer CreateMqttServer(Action<MqttServerOptions> configure) public IMqttServer CreateMqttServer(IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger)
{ {
if (configure == null) throw new ArgumentNullException(nameof(configure)); if (adapters == null) throw new ArgumentNullException(nameof(adapters));
if (logger == null) throw new ArgumentNullException(nameof(logger));
var options = _serviceProvider.GetRequiredService<IOptions<MqttServerOptions>>(); return new MqttServer(adapters, logger);
configure(options.Value);
return _serviceProvider.GetRequiredService<IMqttServer>();
}
private static IServiceProvider BuildServiceProvider()
{
var serviceProvider = new ServiceCollection()
.AddMqttClient()
.AddMqttServer()
.AddLogging()
.BuildServiceProvider();
serviceProvider.GetRequiredService<ILoggerFactory>()
.AddMqttTrace();
return serviceProvider;
} }
} }
} }
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public interface IMqttPacketWithIdentifier public interface IMqttPacketWithIdentifier
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public abstract class MqttBasePacket public abstract class MqttBasePacket
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public class MqttBasePublishPacket : MqttBasePacket, IMqttPacketWithIdentifier public class MqttBasePublishPacket : MqttBasePacket, IMqttPacketWithIdentifier
{ {
@@ -1,6 +1,6 @@
using MQTTnet.Core.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttConnAckPacket : MqttBasePacket public sealed class MqttConnAckPacket : MqttBasePacket
{ {
@@ -1,6 +1,6 @@
using MQTTnet.Core.Serializer; using MQTTnet.Serializer;
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttConnectPacket : MqttBasePacket public sealed class MqttConnectPacket : MqttBasePacket
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttDisconnectPacket : MqttBasePacket public sealed class MqttDisconnectPacket : MqttBasePacket
{ {
@@ -1,6 +1,6 @@
using System; using System;
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public static class MqttPacketExtensions public static class MqttPacketExtensions
{ {
@@ -1,6 +1,6 @@
using MQTTnet.Core.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public class MqttPacketHeader public class MqttPacketHeader
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttPingReqPacket : MqttBasePacket public sealed class MqttPingReqPacket : MqttBasePacket
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttPingRespPacket : MqttBasePacket public sealed class MqttPingRespPacket : MqttBasePacket
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttPubAckPacket : MqttBasePublishPacket public sealed class MqttPubAckPacket : MqttBasePublishPacket
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttPubCompPacket : MqttBasePublishPacket public sealed class MqttPubCompPacket : MqttBasePublishPacket
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttPubRecPacket : MqttBasePublishPacket public sealed class MqttPubRecPacket : MqttBasePublishPacket
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttPubRelPacket : MqttBasePublishPacket public sealed class MqttPubRelPacket : MqttBasePublishPacket
{ {
@@ -1,6 +1,6 @@
using MQTTnet.Core.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttPublishPacket : MqttBasePublishPacket public sealed class MqttPublishPacket : MqttBasePublishPacket
{ {
@@ -1,8 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using MQTTnet.Core.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttSubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier public sealed class MqttSubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
{ {
@@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttSubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier public sealed class MqttSubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttUnsubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier public sealed class MqttUnsubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
{ {
@@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace MQTTnet.Core.Packets namespace MQTTnet.Packets
{ {
public sealed class MqttUnsubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier public sealed class MqttUnsubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Protocol namespace MQTTnet.Protocol
{ {
public enum MqttConnectReturnCode public enum MqttConnectReturnCode
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Protocol namespace MQTTnet.Protocol
{ {
public enum MqttControlPacketType public enum MqttControlPacketType
{ {
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Protocol namespace MQTTnet.Protocol
{ {
public enum MqttQualityOfServiceLevel public enum MqttQualityOfServiceLevel
{ {

Some files were not shown because too many files have changed in this diff Show More