Compare commits

...
87 Commits
Author SHA1 Message Date
Christian fcf748e980 Merge branch 'develop' 2018-03-25 14:45:35 +02:00
Christian e58cb3c9cd Update docs. 2018-03-25 14:42:43 +02:00
Christian 3587a8ebd1 Add new option which allows configuring the send interval for keep alive packets. 2018-03-25 14:42:34 +02:00
Christian 3193381b69 Refactor web socket stream. Add mqtt as default sub protocol. 2018-03-25 14:42:12 +02:00
Christian 7a9ddfc2b5 Fix packet identifier which is sent from the server. 2018-03-25 14:41:12 +02:00
Christian 447b2aa2c6 Add new extension methods. 2018-03-25 14:39:44 +02:00
Christian b3eaeea8a4 Fix log issue in UWP test app. 2018-03-25 14:38:36 +02:00
Christian 45b4c05867 Update version 2018-03-25 14:38:16 +02:00
Christian 49da66dcff Fix issue when disconnecting on UWP 2018-03-13 23:28:50 +01:00
Christian bb9d5586f0 Update nuspec 2018-03-13 20:31:01 +01:00
Christian c5d22f4b27 Update docs 2018-03-13 20:23:36 +01:00
Christian e57ec11ed8 Update libs 2018-03-13 20:23:31 +01:00
Christian 56acc82445 Fix null ref in server when using interceptor 2018-03-13 20:23:22 +01:00
Christian 6e828d091f Override ToString() for LogMessage 2018-03-13 20:23:07 +01:00
Christian e1d8697331 Fix wrong parsing and generating of MqttConnAck for version 3.1.0 2018-03-13 20:22:48 +01:00
Christian 90098c2006 Optimize dispatcher 2018-03-13 20:22:13 +01:00
Christian b3a25fb9e8 Fix subscription handling of _ManagedClient_. 2018-03-05 20:43:37 +01:00
Christian f85ad4965b Refactor keep alive monitoring and add unit tests. 2018-02-28 21:43:19 +01:00
Christian f5696b6fc0 Merge branch 'develop' 2018-02-21 21:33:01 +01:00
Christian c26555aca5 Refactor ManagedClient 2018-02-21 21:32:21 +01:00
Christian 0accb467b5 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-02-21 20:44:10 +01:00
Christian 3b34fd4750 Fix build 2018-02-21 20:44:05 +01:00
Christian abb216c055 Fix retained message handling 2018-02-21 20:43:22 +01:00
Christian 1760dc7d35 Fix async issues 2018-02-21 20:43:09 +01:00
Christian 034e7fd750 Add event for processed application message (managed client only) 2018-02-21 20:42:39 +01:00
Christian 76c7b319b8 Fix build errors 2018-02-21 20:42:05 +01:00
Christian 09010e95a2 Merge pull request #180 from lerppana/managed_unsubscribe_fix
Fix ManagedMqttClient missing call to unsubscribe topics
2018-02-21 20:09:10 +01:00
Lassi Laati 2341d66192 Fix ManagedMqttClient missing call to unsubscribe topics 2018-02-20 09:23:47 +02:00
Christian 21586b7710 Merge branch 'master' of https://github.com/chkr1011/MQTTnet 2018-02-04 20:10:11 +01:00
Christian b426ae1198 Update docs 2018-02-04 20:06:01 +01:00
Christian e9db9d65ac Update README.md 2018-02-04 20:05:34 +01:00
Christian 59dcbf4c0c Add server events for subscribed and unsubscribed topics. Add pending messages count for connected clients. 2018-02-02 22:44:21 +01:00
Christian a204d233f6 Update configs 2018-02-02 00:47:00 +01:00
Christian 26cd84c88c Fix generation of packet identifier. 2018-02-02 00:46:47 +01:00
Christian 5f77255893 Update docs 2018-01-27 14:12:54 +01:00
Christian 8856a7aa34 Fix broken package reader 2018-01-26 23:56:22 +01:00
Christian 30754433eb Add server methods for controlling the client subscriptions. 2018-01-26 22:06:21 +01:00
Christian 751ac19c4b Fix reading more data than required for the body. 2018-01-26 21:25:56 +01:00
Christian 1415e4b878 Covnert the body buffer to a memory stream which will grow automatically. 2018-01-24 20:49:47 +01:00
Christian e56203faac Update docs 2018-01-24 20:16:47 +01:00
Christian 64c997a4a2 Merge pull request #163 from kpreisser/fixInfiniteLoop
Fix infinite loop when the client closes the connection
2018-01-24 20:12:15 +01:00
Christian 391ee34a0f Merge pull request #166 from kpreisser/fixDecodeRemainingLength
Fix the algorithm for decoding the body length
2018-01-24 20:09:14 +01:00
kpreisser 88de5047bc Fix the algorithm for decoding the remaining length to allow a length greater than 2,097,151 bytes.
See: https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os.html#_Toc442180732
2018-01-24 15:32:36 +01:00
kpreisser d020e809a1 Fix infinite loop that can occur when the client closes the connection before sending the complete packet body. 2018-01-23 10:45:58 +01:00
Christian 8b2b8d41df Update docs 2018-01-22 21:11:44 +01:00
Christian 40c33ac639 Refactoring 2018-01-22 21:11:35 +01:00
Christian 002e4b8d69 Fix broken unit tests 2018-01-22 20:55:50 +01:00
Christian c060c379d3 Add thrown exception for disconnected event 2018-01-22 20:55:06 +01:00
Christian 4d13407a83 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-01-21 14:33:33 +01:00
Christian d737f550c7 Add encrypted code signing key 2018-01-21 14:33:29 +01:00
Christian 5e8d5b9219 Add documents 2018-01-21 14:29:43 +01:00
Christian f98f21ffdf Update libs 2018-01-21 14:29:36 +01:00
Christian ec9e64ebfb Update docs 2018-01-21 14:14:04 +01:00
Christian 39672970e2 Update 3rd-Partly libs 2018-01-21 14:13:50 +01:00
Christian 5becf7980a Merge pull request #157 from kpreisser/trueAsync
Use asynchronous methods for blocking operations
2018-01-21 14:12:29 +01:00
Christian c7c84ad39b Make clients interfaces disposable. 2018-01-21 14:10:23 +01:00
Christian 8773117493 Fix an issue which adds a delay after every accepted connection. 2018-01-21 14:09:34 +01:00
kpreisser bb697e1bd9 Follow-Up: Add missing ConfigureAwait() call. 2018-01-17 11:29:48 +01:00
kpreisser e1a9aa7d03 Adjust the MqttClientPendingMessagesQueue and the MqttPacketReader to use asynchronous methods for long-running/blocking operations.
Otherwise, Worker Threads from the .NET Threadpool are blocked while waiting for the next client message, and while waiting for the next message to send.
2018-01-17 11:10:39 +01:00
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
192 changed files with 3377 additions and 1905 deletions
+2
View File
@@ -291,3 +291,5 @@ __pycache__/
Build/nuget.exe
*.js
*.map
/Tests/MQTTnet.TestApp.NetCore/RetainedMessages.json
+4 -4
View File
@@ -2,7 +2,7 @@
<package >
<metadata>
<id>MQTTnet.AspNetCore</id>
<version>2.5.3</version>
<version>2.7.1</version>
<authors>Christian Kratky</authors>
<owners>Christian Kratky</owners>
<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>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is a support library to integrate MQTTnet into AspNetCore.</description>
<releaseNotes>* Updated to MQTTnet 2.5.3.
<releaseNotes>* Updated to MQTTnet 2.7.1.
</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2017</copyright>
<copyright>Copyright Christian Kratky 2016-2018</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.5.3-rc1" />
<dependency id="MQTTnet" version="2.7.1" />
</group>
</dependencies>
</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>
+8 -11
View File
@@ -2,7 +2,7 @@
<package >
<metadata>
<id>MQTTnet</id>
<version>2.5.3</version>
<version>2.7.2</version>
<authors>Christian Kratky</authors>
<owners>Christian Kratky</owners>
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
@@ -10,11 +10,13 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<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>
<releaseNotes>* [Core] Removed all dependencies to other libraries (BREAKING CHANGE!).
* [Core] Updated SDK libraries.
* [Client] Fixed broken support for WebSocketSecure connections (Thanks to @StAI).
<releaseNotes>* [Client] Added the subprotocol "mqtt" as default for web socket based connections.
* [Client] Added a new client setting called "KeepAliveSendInterval". It allows configuring the effective interval for sending ping requests.
* [Client] The client will no longer send ping requests if other packets are sent within the configured interval.
* [Server] The server now generates a valid packet identifier when disaptching publish packets to clients.
* [Core] Add several new extension methods.
</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2017</copyright>
<copyright>Copyright Christian Kratky 2016-2018</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>
@@ -46,23 +48,18 @@
</metadata>
<files>
<!-- .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\"/>
<!-- .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\"/>
<!-- 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\"/>
<!-- .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\net461\MQTTnet.Core.*" target="lib\net461\"/>
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net461\MQTTnet.*" target="lib\net461\"/>
</files>
+20 -8
View File
@@ -1,6 +1,7 @@
param([string]$assemblyVersion, [string]$nugetVersion)
if ([string]::IsNullOrEmpty($version)) {$version = "0.0.1"}
if ([string]::IsNullOrEmpty($assemblyVersion)) {$assemblyVersion = "0.0.1"}
if ([string]::IsNullOrEmpty($nugetVersion)) {$nugetVersion = "0.0.1"}
$vswhere = ${Env:\ProgramFiles(x86)} + '\Microsoft Visual Studio\Installer\vswhere'
@@ -8,16 +9,27 @@ $path = &$vswhere -latest -products * -requires Microsoft.Component.MSBuild -pro
if ($path) {
$msbuild = join-path $path 'MSBuild\15.0\Bin\MSBuild.exe'
&$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="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="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
# 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 /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$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 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 /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# 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 -ErrorAction SilentlyContinue
Remove-Item .\NuGet -Force -Recurse
New-Item -ItemType Directory -Force -Path .\NuGet
.\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.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
}
Binary file not shown.
+9
View File
@@ -0,0 +1,9 @@
# Import codeSigningKey.pfx
In order to import the key for code signing on a new developer machine use the following command within the VS developer command line:
> sn –i codeSigningKey.pfx VS_KEY_EFCA4C5B6DFD4B4F
The container name may be different.
# Check if the assembly has a strong name
> sn -vf MQTTnet.dll
@@ -0,0 +1,31 @@
<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>
<DefaultLanguage>en</DefaultLanguage>
<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 Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using MQTTnet.Core.Server;
using System.Linq;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore
{
@@ -2,14 +2,23 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyVersion>2.5.2.0</AssemblyVersion>
<FileVersion>2.5.2.0</FileVersion>
<AssemblyVersion>0.0.0.0</AssemblyVersion>
<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>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.0.1" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.0.1" />
</ItemGroup>
<ItemGroup>
@@ -3,17 +3,17 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Diagnostics;
using MQTTnet.Core.Server;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore
{
public class MqttHostedServer : MqttServer, IHostedService
{
private readonly MqttServerOptions _options;
private readonly IMqttServerOptions _options;
public MqttHostedServer(MqttServerOptions options, IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger) : base(adapters, logger)
public MqttHostedServer(IMqttServerOptions options, IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger) : base(adapters, logger)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
}
@@ -1,18 +1,18 @@
using System;
using System.Net.WebSockets;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Diagnostics;
using MQTTnet.Core.Serializer;
using MQTTnet.Core.Server;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore
{
public class MqttWebSocketServerAdapter : IMqttServerAdapter, IDisposable
public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter, IDisposable
{
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
public Task StartAsync(MqttServerOptions options)
public Task StartAsync(IMqttServerOptions options)
{
return Task.CompletedTask;
}
@@ -3,7 +3,7 @@ using System.IO;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Channel;
using MQTTnet.Channel;
using MQTTnet.Implementations;
namespace MQTTnet.AspNetCore
@@ -1,15 +1,15 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Diagnostics;
using MQTTnet.Core.Server;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, MqttServerOptions options)
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, IMqttServerOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
@@ -2,10 +2,10 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Packets;
using MQTTnet.Core.Serializer;
using MQTTnet.Packets;
using MQTTnet.Serializer;
namespace MQTTnet.Core.Adapter
namespace MQTTnet.Adapter
{
public interface IMqttChannelAdapter
{
@@ -1,14 +1,14 @@
using System;
using System.Threading.Tasks;
using MQTTnet.Core.Server;
using MQTTnet.Server;
namespace MQTTnet.Core.Adapter
namespace MQTTnet.Adapter
{
public interface IMqttServerAdapter
{
event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
Task StartAsync(MqttServerOptions options);
Task StartAsync(IMqttServerOptions options);
Task StopAsync();
}
}
@@ -1,21 +1,23 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Channel;
using MQTTnet.Core.Diagnostics;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Internal;
using MQTTnet.Core.Packets;
using MQTTnet.Core.Serializer;
using MQTTnet.Channel;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Serializer;
namespace MQTTnet.Core.Adapter
namespace MQTTnet.Adapter
{
public class MqttChannelAdapter : IMqttChannelAdapter
{
private const uint ErrorOperationAborted = 0x800703E3;
private const int ReadBufferSize = 4096; // TODO: Move buffer size to config
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
private readonly IMqttNetLogger _logger;
@@ -30,23 +32,23 @@ namespace MQTTnet.Core.Adapter
public IMqttPacketSerializer PacketSerializer { get; }
public async Task ConnectAsync(TimeSpan timeout)
public Task ConnectAsync(TimeSpan timeout)
{
_logger.Info<MqttChannelAdapter>("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.Info<MqttChannelAdapter>("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);
try
@@ -60,8 +62,11 @@ namespace MQTTnet.Core.Adapter
_logger.Trace<MqttChannelAdapter>("TX >>> {0} [Timeout={1}]", packet, timeout);
var writeBuffer = PacketSerializer.Serialize(packet);
await _channel.SendStream.WriteAsync(writeBuffer, 0, writeBuffer.Length, cancellationToken).ConfigureAwait(false);
var chunks = PacketSerializer.Serialize(packet);
foreach (var chunk in chunks)
{
await _channel.SendStream.WriteAsync(chunk.Array, chunk.Offset, chunk.Count, cancellationToken).ConfigureAwait(false);
}
}
if (timeout > TimeSpan.Zero)
@@ -102,7 +107,7 @@ namespace MQTTnet.Core.Adapter
throw new TaskCanceledException();
}
packet = PacketSerializer.Deserialize(receivedMqttPacket);
packet = PacketSerializer.Deserialize(receivedMqttPacket.Header, receivedMqttPacket.Body);
if (packet == null)
{
throw new MqttProtocolViolationException("Received malformed packet.");
@@ -114,14 +119,14 @@ namespace MQTTnet.Core.Adapter
{
receivedMqttPacket?.Dispose();
}
});
}).ConfigureAwait(false);
return packet;
}
private static async Task<ReceivedMqttPacket> ReceiveAsync(Stream stream, CancellationToken cancellationToken)
{
var header = MqttPacketReader.ReadHeaderFromSource(stream, cancellationToken);
var header = await MqttPacketReader.ReadHeaderAsync(stream, cancellationToken).ConfigureAwait(false);
if (header == null)
{
return null;
@@ -129,19 +134,36 @@ namespace MQTTnet.Core.Adapter
if (header.BodyLength == 0)
{
return new ReceivedMqttPacket(header, new MemoryStream(0));
return new ReceivedMqttPacket(header, new MemoryStream(new byte[0], false));
}
var body = new byte[header.BodyLength];
var body = header.BodyLength <= ReadBufferSize ? new MemoryStream(header.BodyLength) : new MemoryStream();
var offset = 0;
do
var buffer = new byte[ReadBufferSize];
while (body.Length < header.BodyLength)
{
var readBytesCount = await stream.ReadAsync(body, offset, body.Length - offset, cancellationToken).ConfigureAwait(false);
offset += readBytesCount;
} while (offset < header.BodyLength);
var bytesLeft = header.BodyLength - (int)body.Length;
if (bytesLeft > buffer.Length)
{
bytesLeft = buffer.Length;
}
return new ReceivedMqttPacket(header, new MemoryStream(body, 0, body.Length));
var readBytesCount = await stream.ReadAsync(buffer, 0, bytesLeft, cancellationToken).ConfigureAwait(false);
// Check if the client closed the connection before sending the full body.
if (readBytesCount == 0)
{
throw new MqttCommunicationException("Connection closed while reading remaining packet body.");
}
// Here is no need to await because internally only an array is used and no real I/O operation is made.
// Using async here will only generate overhead.
body.Write(buffer, 0, readBytesCount);
}
body.Seek(0L, SeekOrigin.Begin);
return new ReceivedMqttPacket(header, body);
}
private static async Task ExecuteAndWrapExceptionAsync(Func<Task> action)
@@ -175,6 +197,18 @@ namespace MQTTnet.Core.Adapter
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)
{
throw new MqttCommunicationException(exception);
@@ -1,7 +1,7 @@
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Protocol;
using MQTTnet.Exceptions;
using MQTTnet.Protocol;
namespace MQTTnet.Core.Adapter
namespace MQTTnet.Adapter
{
public class MqttConnectingFailedException : MqttCommunicationException
{
@@ -1,7 +1,7 @@
using System;
using System.Threading.Tasks;
namespace MQTTnet.Core.Adapter
namespace MQTTnet.Adapter
{
public class MqttServerAdapterClientAcceptedEventArgs : EventArgs
{
@@ -1,8 +1,8 @@
using System;
using System.IO;
using MQTTnet.Core.Packets;
using MQTTnet.Packets;
namespace MQTTnet.Core.Adapter
namespace MQTTnet.Adapter
{
public sealed class ReceivedMqttPacket : IDisposable
{
@@ -0,0 +1,41 @@
using System;
using System.Threading.Tasks;
using MQTTnet.Protocol;
namespace MQTTnet
{
public static class ApplicationMessagePublisherExtensions
{
public static Task PublishAsync(this IApplicationMessagePublisher publisher, params MqttApplicationMessage[] applicationMessages)
{
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
return publisher.PublishAsync(applicationMessages);
}
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic)
{
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return publisher.PublishAsync(new MqttApplicationMessageBuilder().WithTopic(topic).Build());
}
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload)
{
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return publisher.PublishAsync(new MqttApplicationMessageBuilder().WithTopic(topic).WithPayload(payload).Build());
}
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return publisher.PublishAsync(new MqttApplicationMessageBuilder().WithTopic(topic).WithPayload(payload).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
}
}
}
@@ -1,7 +1,7 @@
using System.Threading.Tasks;
using System.IO;
using System.IO;
using System.Threading.Tasks;
namespace MQTTnet.Core.Channel
namespace MQTTnet.Channel
{
public interface IMqttChannel
{
@@ -1,11 +1,10 @@
using System;
using System.Collections.Generic;
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, IDisposable
{
bool IsConnected { get; }
@@ -16,6 +15,6 @@ namespace MQTTnet.Core.Client
Task DisconnectAsync();
Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
Task UnsubscribeAsync(IEnumerable<string> topicFilters);
Task UnsubscribeAsync(IEnumerable<string> topics);
}
}
@@ -1,7 +1,7 @@
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Diagnostics;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public interface IMqttClientAdapterFactory
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public interface IMqttClientChannelOptions
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public interface IMqttClientCredentials
{
@@ -1,7 +1,7 @@
using MQTTnet.Core.Diagnostics;
using MQTTnet.Core.ManagedClient;
using MQTTnet.Diagnostics;
using MQTTnet.ManagedClient;
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public interface IMqttClientFactory
{
@@ -1,7 +1,7 @@
using System;
using MQTTnet.Core.Serializer;
using MQTTnet.Serializer;
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public interface IMqttClientOptions
{
@@ -13,6 +13,8 @@ namespace MQTTnet.Core.Client
TimeSpan CommunicationTimeout { get; }
TimeSpan KeepAlivePeriod { get; }
TimeSpan? KeepAliveSendInterval { get; set; }
MqttProtocolVersion ProtocolVersion { get; }
IMqttClientChannelOptions ChannelOptions { get; }
@@ -1,28 +1,28 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Diagnostics;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Internal;
using MQTTnet.Core.Packets;
using MQTTnet.Core.Protocol;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public class MqttClient : IMqttClient
{
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();
private readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
private readonly Stopwatch _sendTracker = new Stopwatch();
private readonly IMqttClientAdapterFactory _adapterFactory;
private readonly MqttPacketDispatcher _packetDispatcher;
private readonly IMqttNetLogger _logger;
private IMqttClientOptions _options;
private bool _isReceivingPackets;
private int _latestPacketIdentifier;
private CancellationTokenSource _cancellationTokenSource;
private IMqttChannelAdapter _adapter;
@@ -51,7 +51,7 @@ namespace MQTTnet.Core.Client
{
_options = options;
_cancellationTokenSource = new CancellationTokenSource();
_latestPacketIdentifier = 0;
_packetIdentifierProvider.Reset();
_packetDispatcher.Reset();
_adapter = _adapterFactory.CreateClientAdapter(options.ChannelOptions, _logger);
@@ -61,10 +61,12 @@ namespace MQTTnet.Core.Client
_logger.Trace<MqttClient>("Connection with server established.");
await StartReceivingPacketsAsync(_cancellationTokenSource.Token).ConfigureAwait(false);
var connectResponse = await AuthenticateAsync(options.WillMessage).ConfigureAwait(false);
var connectResponse = await AuthenticateAsync(options.WillMessage).ConfigureAwait(false);
_logger.Trace<MqttClient>("MQTT connection with server established.");
_sendTracker.Restart();
if (_options.KeepAlivePeriod != TimeSpan.Zero)
{
StartSendingKeepAliveMessages(_cancellationTokenSource.Token);
@@ -77,7 +79,8 @@ namespace MQTTnet.Core.Client
catch (Exception exception)
{
_logger.Error<MqttClient>(exception, "Error while connecting with server.");
await DisconnectInternalAsync().ConfigureAwait(false);
await DisconnectInternalAsync(exception).ConfigureAwait(false);
throw;
}
}
@@ -95,7 +98,7 @@ namespace MQTTnet.Core.Client
}
finally
{
await DisconnectInternalAsync().ConfigureAwait(false);
await DisconnectInternalAsync(null).ConfigureAwait(false);
}
}
@@ -107,7 +110,7 @@ namespace MQTTnet.Core.Client
var subscribePacket = new MqttSubscribePacket
{
PacketIdentifier = GetNewPacketIdentifier(),
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(),
TopicFilters = topicFilters.ToList()
};
@@ -129,11 +132,11 @@ namespace MQTTnet.Core.Client
var unsubscribePacket = new MqttUnsubscribePacket
{
PacketIdentifier = GetNewPacketIdentifier(),
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(),
TopicFilters = topicFilters.ToList()
};
await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket);
await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket).ConfigureAwait(false);
}
public async Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages)
@@ -141,23 +144,23 @@ namespace MQTTnet.Core.Client
ThrowIfNotConnected();
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)
{
case MqttQualityOfServiceLevel.AtMostOnce:
{
// 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 SendAsync(qosGroup.ToArray()).ConfigureAwait(false);
break;
}
case MqttQualityOfServiceLevel.AtLeastOnce:
{
foreach (var publishPacket in qosPackets)
foreach (var publishPacket in qosGroup)
{
publishPacket.PacketIdentifier = GetNewPacketIdentifier();
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier();
await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket).ConfigureAwait(false);
}
@@ -165,11 +168,17 @@ namespace MQTTnet.Core.Client
}
case MqttQualityOfServiceLevel.ExactlyOnce:
{
foreach (var publishPacket in qosPackets)
foreach (var publishPacket in qosGroup)
{
publishPacket.PacketIdentifier = GetNewPacketIdentifier();
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier();
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket).ConfigureAwait(false);
await SendAndReceiveAsync<MqttPubCompPacket>(pubRecPacket.CreateResponse<MqttPubRelPacket>()).ConfigureAwait(false);
var pubRelPacket = new MqttPubRelPacket
{
PacketIdentifier = pubRecPacket.PacketIdentifier
};
await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket).ConfigureAwait(false);
}
break;
@@ -182,6 +191,11 @@ namespace MQTTnet.Core.Client
}
}
public void Dispose()
{
_cancellationTokenSource?.Dispose();
}
private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage)
{
var connectPacket = new MqttConnectPacket
@@ -213,7 +227,7 @@ namespace MQTTnet.Core.Client
if (IsConnected) throw new MqttProtocolViolationException(message);
}
private async Task DisconnectInternalAsync()
private async Task DisconnectInternalAsync(Exception exception)
{
var clientWasConnected = IsConnected;
IsConnected = false;
@@ -233,14 +247,14 @@ namespace MQTTnet.Core.Client
await _adapter.DisconnectAsync(_options.CommunicationTimeout).ConfigureAwait(false);
_logger.Info<MqttClient>("Disconnected from adapter.");
}
catch (Exception exception)
catch (Exception adapterException)
{
_logger.Warning<MqttClient>(exception, "Error while disconnecting from adapter.");
_logger.Warning<MqttClient>(adapterException, "Error while disconnecting from adapter.");
}
finally
{
_logger.Info<MqttClient>("Disconnected.");
Disconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientWasConnected));
Disconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientWasConnected, exception));
}
}
@@ -249,28 +263,28 @@ namespace MQTTnet.Core.Client
try
{
_logger.Info<MqttClient>("Received <<< {0}", packet);
if (packet is MqttPublishPacket publishPacket)
{
await ProcessReceivedPublishPacketAsync(publishPacket).ConfigureAwait(false);
return;
}
if (packet is MqttPingReqPacket)
{
await SendAsync(new MqttPingRespPacket());
await SendAsync(new MqttPingRespPacket()).ConfigureAwait(false);
return;
}
if (packet is MqttDisconnectPacket)
{
await DisconnectAsync();
return;
}
if (packet is MqttPublishPacket publishPacket)
{
await ProcessReceivedPublishPacket(publishPacket);
await DisconnectAsync().ConfigureAwait(false);
return;
}
if (packet is MqttPubRelPacket pubRelPacket)
{
await ProcessReceivedPubRelPacket(pubRelPacket);
await ProcessReceivedPubRelPacket(pubRelPacket).ConfigureAwait(false);
return;
}
@@ -295,32 +309,25 @@ namespace MQTTnet.Core.Client
}
}
private async Task ProcessReceivedPublishPacket(MqttPublishPacket publishPacket)
private Task ProcessReceivedPublishPacketAsync(MqttPublishPacket publishPacket)
{
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{
FireApplicationMessageReceivedEvent(publishPacket);
return;
return Task.FromResult(0);
}
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{
FireApplicationMessageReceivedEvent(publishPacket);
await SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
return;
return SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
}
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{
// QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
lock (_unacknowledgedPublishPackets)
{
_unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
}
FireApplicationMessageReceivedEvent(publishPacket);
await SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier });
return;
return SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier });
}
throw new MqttCommunicationException("Received a not supported QoS level.");
@@ -328,29 +335,32 @@ namespace MQTTnet.Core.Client
private Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket)
{
lock (_unacknowledgedPublishPackets)
var response = new MqttPubCompPacket
{
_unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
}
PacketIdentifier = pubRelPacket.PacketIdentifier
};
return SendAsync(pubRelPacket.CreateResponse<MqttPubCompPacket>());
return SendAsync(response);
}
private Task SendAsync(MqttBasePacket packet)
private Task SendAsync(params MqttBasePacket[] packets)
{
return _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, packet);
_sendTracker.Restart();
return _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, packets);
}
private async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket) where TResponsePacket : MqttBasePacket
{
var packetAwaiter = _packetDispatcher.WaitForPacketAsync(requestPacket, typeof(TResponsePacket), _options.CommunicationTimeout);
await _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, requestPacket).ConfigureAwait(false);
return (TResponsePacket)await packetAwaiter.ConfigureAwait(false);
}
ushort? identifier = null;
if (requestPacket is IMqttPacketWithIdentifier requestPacketWithIdentifier)
{
identifier = requestPacketWithIdentifier.PacketIdentifier;
}
private ushort GetNewPacketIdentifier()
{
return (ushort)Interlocked.Increment(ref _latestPacketIdentifier);
var packetAwaiter = _packetDispatcher.WaitForPacketAsync(typeof(TResponsePacket), identifier, _options.CommunicationTimeout);
await SendAsync(requestPacket).ConfigureAwait(false);
return (TResponsePacket)await packetAwaiter.ConfigureAwait(false);
}
private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken)
@@ -361,13 +371,22 @@ namespace MQTTnet.Core.Client
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(_options.KeepAlivePeriod, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
TimeSpan keepAliveSendInterval;
if (_options.KeepAliveSendInterval.HasValue)
{
return;
keepAliveSendInterval = _options.KeepAliveSendInterval.Value;
}
else
{
keepAliveSendInterval = TimeSpan.FromSeconds(_options.KeepAlivePeriod.TotalSeconds * 0.75);
}
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket()).ConfigureAwait(false);
if (_sendTracker.Elapsed > keepAliveSendInterval)
{
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket()).ConfigureAwait(false);
}
await Task.Delay(keepAliveSendInterval, cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
@@ -377,7 +396,7 @@ namespace MQTTnet.Core.Client
return;
}
await DisconnectInternalAsync().ConfigureAwait(false);
await DisconnectInternalAsync(null).ConfigureAwait(false);
}
catch (MqttCommunicationException exception)
{
@@ -387,12 +406,12 @@ namespace MQTTnet.Core.Client
}
_logger.Warning<MqttClient>(exception, "MQTT communication exception while sending/receiving keep alive packets.");
await DisconnectInternalAsync().ConfigureAwait(false);
await DisconnectInternalAsync(exception).ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.Warning<MqttClient>(exception, "Unhandled exception while sending/receiving keep alive packets.");
await DisconnectInternalAsync().ConfigureAwait(false);
await DisconnectInternalAsync(exception).ConfigureAwait(false);
}
finally
{
@@ -427,7 +446,7 @@ namespace MQTTnet.Core.Client
return;
}
await DisconnectInternalAsync().ConfigureAwait(false);
await DisconnectInternalAsync(null).ConfigureAwait(false);
}
catch (MqttCommunicationException exception)
{
@@ -437,12 +456,12 @@ namespace MQTTnet.Core.Client
}
_logger.Warning<MqttClient>(exception, "MQTT communication exception while receiving packets.");
await DisconnectInternalAsync().ConfigureAwait(false);
await DisconnectInternalAsync(exception).ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.Error<MqttClient>(exception, "Unhandled exception while receiving packets.");
await DisconnectInternalAsync().ConfigureAwait(false);
await DisconnectInternalAsync(exception).ConfigureAwait(false);
}
finally
{
@@ -454,7 +473,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
Task.Run(
() => ProcessReceivedPacketAsync(packet),
async () => await ProcessReceivedPacketAsync(packet).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
}
@@ -465,7 +484,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
Task.Run(
async () => await ReceivePacketsAsync(cancellationToken),
async () => await ReceivePacketsAsync(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
@@ -479,7 +498,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
Task.Run(
async () => await SendKeepAliveMessagesAsync(cancellationToken),
async () => await SendKeepAliveMessagesAsync(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
}
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public class MqttClientConnectResult
{
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public class MqttClientConnectedEventArgs : EventArgs
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public class MqttClientCredentials : IMqttClientCredentials
{
@@ -1,14 +1,17 @@
using System;
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public class MqttClientDisconnectedEventArgs : EventArgs
{
public MqttClientDisconnectedEventArgs(bool clientWasConnected)
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception)
{
ClientWasConnected = clientWasConnected;
Exception = exception;
}
public bool ClientWasConnected { get; }
public Exception Exception { get; }
}
}
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MQTTnet.Protocol;
namespace MQTTnet.Client
{
public static class MqttClientExtensions
{
public static Task<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, params TopicFilter[] topicFilters)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
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<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, string topic)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build());
}
public static Task UnsubscribeAsync(this IMqttClient client, params string[] topicFilters)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
return client.UnsubscribeAsync(topicFilters.ToList());
}
}
}
@@ -1,7 +1,7 @@
using System;
using MQTTnet.Core.Serializer;
using MQTTnet.Serializer;
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public class MqttClientOptions : IMqttClientOptions
{
@@ -13,7 +13,9 @@ namespace MQTTnet.Core.Client
public IMqttClientCredentials Credentials { get; set; } = new MqttClientCredentials();
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(5);
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(15);
public TimeSpan? KeepAliveSendInterval { get; set; }
public TimeSpan CommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10);
@@ -1,8 +1,8 @@
using System;
using System.Linq;
using MQTTnet.Core.Serializer;
using MQTTnet.Serializer;
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public class MqttClientOptionsBuilder
{
@@ -119,11 +119,11 @@ namespace MQTTnet.Core.Client
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
{
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public static class MqttClientTcpOptionsExtensions
{
@@ -1,6 +1,6 @@
using System.Collections.Generic;
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public class MqttClientTlsOptions
{
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Net;
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public class MqttClientWebSocketOptions : IMqttClientChannelOptions
{
@@ -9,7 +9,7 @@ namespace MQTTnet.Core.Client
public IDictionary<string, string> RequestHeaders { get; set; }
public ICollection<string> SubProtocols { get; set; }
public ICollection<string> SubProtocols { get; set; } = new List<string> { "mqtt" };
public CookieContainer CookieContainer { get; set; }
@@ -0,0 +1,97 @@
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Packets;
namespace MQTTnet.Client
{
public class MqttPacketDispatcher
{
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>> _awaiters = new ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>>();
private readonly IMqttNetLogger _logger;
public MqttPacketDispatcher(IMqttNetLogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task<MqttBasePacket> WaitForPacketAsync(Type responseType, ushort? identifier, TimeSpan timeout)
{
var packetAwaiter = AddPacketAwaiter(responseType, identifier);
try
{
return await packetAwaiter.Task.TimeoutAfter(timeout).ConfigureAwait(false);
}
catch (MqttCommunicationTimedOutException)
{
_logger.Warning<MqttPacketDispatcher>("Timeout while waiting for packet of type '{0}'.", responseType.Name);
throw;
}
finally
{
RemovePacketAwaiter(responseType, identifier);
}
}
public void Dispatch(MqttBasePacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
var type = packet.GetType();
if (_awaiters.TryGetValue(type, out var byId))
{
ushort? identifier = 0;
if (packet is IMqttPacketWithIdentifier packetWithIdentifier)
{
identifier = packetWithIdentifier.PacketIdentifier;
}
if (byId.TryRemove(identifier.Value, out var tcs))
{
tcs.TrySetResult(packet);
return;
}
}
throw new InvalidOperationException($"Packet of type '{type.Name}' not handled or dispatched.");
}
public void Reset()
{
_awaiters.Clear();
}
private TaskCompletionSource<MqttBasePacket> AddPacketAwaiter(Type responseType, ushort? identifier)
{
var tcs = new TaskCompletionSource<MqttBasePacket>();
if (!identifier.HasValue)
{
identifier = 0;
}
var byId = _awaiters.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>());
if (!byId.TryAdd(identifier.Value, tcs))
{
throw new InvalidOperationException($"The packet dispatcher already has an awaiter for packet of type '{responseType}' with identifier {identifier}.");
}
return tcs;
}
private void RemovePacketAwaiter(Type responseType, ushort? identifier)
{
if (!identifier.HasValue)
{
identifier = 0;
}
var byId = _awaiters.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>());
byId.TryRemove(identifier.Value, out var _);
}
}
}
@@ -0,0 +1,32 @@
namespace MQTTnet.Client
{
public class MqttPacketIdentifierProvider
{
private readonly object _syncRoot = new object();
private ushort _value;
public void Reset()
{
lock (_syncRoot)
{
_value = 0;
}
}
public ushort GetNewPacketIdentifier()
{
lock (_syncRoot)
{
_value++;
if (_value == 0)
{
// As per official MQTT documentation the package identifier should never be 0.
_value = 1;
}
return _value;
}
}
}
}
@@ -1,7 +1,6 @@
using MQTTnet.Core.Packets;
using MQTTnet.Core.Protocol;
using MQTTnet.Protocol;
namespace MQTTnet.Core.Client
namespace MQTTnet.Client
{
public class MqttSubscribeResult
{
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Core.Diagnostics
namespace MQTTnet.Diagnostics
{
public interface IMqttNetLogger
{
@@ -1,50 +0,0 @@
using System;
using System.Text;
using MQTTnet.Core.Diagnostics;
namespace MQTTnet.Diagnostics
{
public static class MqttNetConsoleLogger
{
private static readonly object Lock = new object();
public static void ForwardToConsole()
{
MqttNetGlobalLog.LogMessagePublished -= PrintToConsole;
MqttNetGlobalLog.LogMessagePublished += PrintToConsole;
}
private static void PrintToConsole(object sender, MqttNetLogMessagePublishedEventArgs e)
{
var output = new StringBuilder();
output.AppendLine($">> [{e.TraceMessage.Timestamp:O}] [{e.TraceMessage.ThreadId}] [{e.TraceMessage.Source}] [{e.TraceMessage.Level}]: {e.TraceMessage.Message}");
if (e.TraceMessage.Exception != null)
{
output.AppendLine(e.TraceMessage.Exception.ToString());
}
lock (Lock)
{
var backupColor = Console.ForegroundColor;
switch (e.TraceMessage.Level)
{
case MqttNetLogLevel.Error:
Console.ForegroundColor = ConsoleColor.Red;
break;
case MqttNetLogLevel.Warning:
Console.ForegroundColor = ConsoleColor.Yellow;
break;
case MqttNetLogLevel.Info:
Console.ForegroundColor = ConsoleColor.Green;
break;
case MqttNetLogLevel.Verbose:
Console.ForegroundColor = ConsoleColor.Gray;
break;
}
Console.Write(output);
Console.ForegroundColor = backupColor;
}
}
}
}
@@ -1,8 +1,8 @@
using System;
namespace MQTTnet.Core.Diagnostics
namespace MQTTnet.Diagnostics
{
public static class MqttNetGlobalLog
public static class MqttNetGlobalLogger
{
public static event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Diagnostics
namespace MQTTnet.Diagnostics
{
public enum MqttNetLogLevel
{
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Core.Diagnostics
namespace MQTTnet.Diagnostics
{
public sealed class MqttNetLogMessage
{
@@ -28,5 +28,16 @@ namespace MQTTnet.Core.Diagnostics
public string Message { get; }
public Exception Exception { get; }
public override string ToString()
{
var result = $"[{Timestamp:O}] [{LogId}] [{ThreadId}] [{Source}] [{Level}]: {Message}";
if (Exception != null)
{
result += Environment.NewLine + Exception;
}
return result;
}
}
}
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Core.Diagnostics
namespace MQTTnet.Diagnostics
{
public sealed class MqttNetLogMessagePublishedEventArgs : EventArgs
{
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Core.Diagnostics
namespace MQTTnet.Diagnostics
{
public class MqttNetLogger : IMqttNetLogger
{
@@ -10,7 +10,7 @@ namespace MQTTnet.Core.Diagnostics
{
_logId = logId;
}
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
public void Trace<TSource>(string message, params object[] parameters)
@@ -46,7 +46,7 @@ namespace MQTTnet.Core.Diagnostics
private void Publish<TSource>(MqttNetLogLevel logLevel, Exception exception, string message, object[] parameters)
{
var hasLocalListeners = LogMessagePublished != null;
var hasGlobalListeners = MqttNetGlobalLog.HasListeners;
var hasGlobalListeners = MqttNetGlobalLogger.HasListeners;
if (!hasLocalListeners && !hasGlobalListeners)
{
@@ -62,7 +62,7 @@ namespace MQTTnet.Core.Diagnostics
if (hasGlobalListeners)
{
MqttNetGlobalLog.Publish(traceMessage);
MqttNetGlobalLogger.Publish(traceMessage);
}
if (hasLocalListeners)
@@ -1,4 +1,4 @@
namespace MQTTnet
namespace MQTTnet.Diagnostics
{
public static class TargetFrameworkInfoProvider
{
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Core.Exceptions
namespace MQTTnet.Exceptions
{
public class MqttCommunicationException : Exception
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Exceptions
namespace MQTTnet.Exceptions
{
public sealed class MqttCommunicationTimedOutException : MqttCommunicationException
{
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Core.Exceptions
namespace MQTTnet.Exceptions
{
public sealed class MqttProtocolViolationException : Exception
{
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MQTTnet.Core
namespace MQTTnet
{
public interface IApplicationMessagePublisher
{
@@ -1,7 +1,6 @@
using System;
using MQTTnet.Core.Client;
namespace MQTTnet.Core
namespace MQTTnet
{
public interface IApplicationMessageReceiver
{
@@ -1,8 +1,8 @@
using System;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Client;
using MQTTnet.Core.Diagnostics;
using MQTTnet.Core.Serializer;
using MQTTnet.Adapter;
using MQTTnet.Client;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
namespace MQTTnet.Implementations
{
@@ -1,11 +1,11 @@
#if WINDOWS_UWP
using System;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Server;
using Windows.Networking.Sockets;
using MQTTnet.Core.Diagnostics;
using MQTTnet.Core.Serializer;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Server;
namespace MQTTnet.Implementations
{
@@ -21,7 +21,7 @@ namespace MQTTnet.Implementations
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));
@@ -7,10 +7,10 @@ using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Server;
using MQTTnet.Core.Diagnostics;
using MQTTnet.Core.Serializer;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Server;
namespace MQTTnet.Implementations
{
@@ -30,7 +30,7 @@ namespace MQTTnet.Implementations
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.");
@@ -39,10 +39,10 @@ namespace MQTTnet.Implementations
if (options.DefaultEndpointOptions.IsEnabled)
{
_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);
Task.Run(() => AcceptDefaultEndpointConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
Task.Run(async () => await AcceptDefaultEndpointConnectionsAsync(_cancellationTokenSource.Token).ConfigureAwait(false), _cancellationTokenSource.Token).ConfigureAwait(false);
}
if (options.TlsEndpointOptions.IsEnabled)
@@ -59,10 +59,10 @@ namespace MQTTnet.Implementations
}
_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);
Task.Run(() => AcceptTlsEndpointConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
Task.Run(async () => await AcceptTlsEndpointConnectionsAsync(_cancellationTokenSource.Token).ConfigureAwait(false), _cancellationTokenSource.Token).ConfigureAwait(false);
}
return Task.FromResult(0);
@@ -102,14 +102,22 @@ namespace MQTTnet.Implementations
#else
var clientSocket = await _defaultEndpointSocket.AcceptAsync().ConfigureAwait(false);
#endif
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, null), new MqttPacketSerializer(), _logger);
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 (Exception exception)
{
_logger.Error<MqttServerAdapter>(exception, "Error while accepting connection at default endpoint.");
if (exception is SocketException s && s.SocketErrorCode == SocketError.OperationAborted)
{
return;
}
//excessive CPU consumed if in endless loop of socket errors
_logger.Error<MqttServerAdapter>(exception, "Error while accepting connection at default endpoint.");
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
}
}
@@ -129,15 +137,22 @@ namespace MQTTnet.Implementations
var sslStream = new SslStream(new NetworkStream(clientSocket));
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false);
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer(), _logger);
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 (Exception exception)
{
_logger.Error<MqttServerAdapter>(exception, "Error while accepting connection at TLS endpoint.");
if (exception is SocketException s && s.SocketErrorCode == SocketError.OperationAborted)
{
return;
}
//excessive CPU consumed if in endless loop of socket errors
_logger.Error<MqttServerAdapter>(exception, "Error while accepting connection at TLS endpoint.");
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
}
}
@@ -8,8 +8,8 @@ using System.Threading.Tasks;
using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Security.Cryptography.Certificates;
using MQTTnet.Core.Channel;
using MQTTnet.Core.Client;
using MQTTnet.Channel;
using MQTTnet.Client;
namespace MQTTnet.Implementations
{
@@ -69,12 +69,6 @@ namespace MQTTnet.Implementations
public void Dispose()
{
SendStream?.Dispose();
SendStream = null;
ReceiveStream?.Dispose();
ReceiveStream = null;
_socket?.Dispose();
_socket = null;
}
@@ -5,10 +5,10 @@ using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using MQTTnet.Core.Channel;
using MQTTnet.Core.Client;
using System.IO;
using System.Linq;
using MQTTnet.Channel;
using MQTTnet.Client;
namespace MQTTnet.Implementations
{
@@ -70,7 +70,7 @@ namespace MQTTnet.Implementations
_sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback);
await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(_options), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
}
CreateStreams(_socket, _sslStream);
}
@@ -130,7 +130,7 @@ namespace MQTTnet.Implementations
foreach (var certificate in options.TlsOptions.Certificates)
{
certificates.Add(new X509Certificate(certificate));
certificates.Add(new X509Certificate2(certificate));
}
return certificates;
@@ -139,10 +139,7 @@ namespace MQTTnet.Implementations
private void CreateStreams(Socket socket, Stream sslStream)
{
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
#if NET452 || NET461
SendStream = new BufferedStream(stream, BufferSize);
@@ -155,4 +152,4 @@ namespace MQTTnet.Implementations
}
}
#endif
#endif
@@ -1,11 +1,11 @@
using MQTTnet.Core.Channel;
using MQTTnet.Core.Client;
using System;
using System;
using System.IO;
using System.Net.WebSockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Client;
namespace MQTTnet.Implementations
{
@@ -5,15 +5,15 @@ using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Exceptions;
using MQTTnet.Exceptions;
namespace MQTTnet.Implementations
{
public class WebSocketStream : Stream
{
private readonly WebSocket _webSocket;
private readonly byte[] _chunkBuffer = new byte[MqttWebSocketChannel.BufferSize];
private readonly byte[] _chunckBuffer = new byte[MqttWebSocketChannel.BufferSize];
private readonly Queue<byte> _buffer = new Queue<byte>(MqttWebSocketChannel.BufferSize);
private readonly WebSocket _webSocket;
public WebSocketStream(WebSocket webSocket)
{
@@ -55,25 +55,28 @@ namespace MQTTnet.Implementations
// Use existing date from buffer.
while (count > 0 && _buffer.Any())
{
buffer[offset++] = _buffer.Dequeue();
buffer[offset] = _buffer.Dequeue();
count--;
bytesRead++;
offset++;
}
if (count == 0)
{
return bytesRead;
}
// Fetch new data if the buffer is not full.
while (_webSocket.State == WebSocketState.Open)
{
await FetchChunkAsync(cancellationToken);
await FetchChunkAsync(cancellationToken).ConfigureAwait(false);
while (count > 0 && _buffer.Any())
{
buffer[offset++] = _buffer.Dequeue();
buffer[offset] = _buffer.Dequeue();
count--;
bytesRead++;
offset++;
}
if (count == 0)
@@ -111,19 +114,24 @@ namespace MQTTnet.Implementations
}
private async Task FetchChunkAsync(CancellationToken cancellationToken)
{
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(_chunkBuffer, 0, _chunkBuffer.Length), cancellationToken).ConfigureAwait(false);
for (var i = 0; i < response.Count; i++)
{
var @byte = _chunkBuffer[i];
_buffer.Enqueue(@byte);
}
{
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(_chunckBuffer, 0, _chunckBuffer.Length), cancellationToken).ConfigureAwait(false);
if (response.MessageType == WebSocketMessageType.Close)
{
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false);
}
else if (response.MessageType == WebSocketMessageType.Binary)
{
for (var i = 0; i < response.Count; i++)
{
_buffer.Enqueue(_chunckBuffer[i]);
}
}
else if (response.MessageType == WebSocketMessageType.Text)
{
throw new MqttProtocolViolationException("WebSocket channel received TEXT message.");
}
}
}
}
@@ -1,6 +1,6 @@
using MQTTnet.Core.Packets;
using MQTTnet.Packets;
namespace MQTTnet.Core.Internal
namespace MQTTnet.Internal
{
internal static class MqttApplicationMessageExtensions
{
@@ -1,21 +1,23 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Exceptions;
using MQTTnet.Exceptions;
namespace MQTTnet.Core.Internal
namespace MQTTnet.Internal
{
public static class TaskExtensions
{
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
{
var timeoutTask = Task.Delay(timeout, cancellationTokenSource.Token);
var timeoutTask = Task.Delay(timeout, timeoutCts.Token);
var finishedTask = await Task.WhenAny(timeoutTask, task).ConfigureAwait(false);
if (finishedTask == timeoutTask)
{
throw new MqttCommunicationTimedOutException();
@@ -28,23 +30,25 @@ namespace MQTTnet.Core.Internal
if (task.IsFaulted)
{
throw new MqttCommunicationException(task.Exception.GetBaseException());
throw new MqttCommunicationException(task.Exception?.GetBaseException());
}
}
finally
{
cancellationTokenSource.Cancel();
timeoutCts.Cancel();
}
}
}
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
{
var timeoutTask = Task.Delay(timeout, cancellationTokenSource.Token);
var timeoutTask = Task.Delay(timeout, timeoutCts.Token);
var finishedTask = await Task.WhenAny(timeoutTask, task).ConfigureAwait(false);
if (finishedTask == timeoutTask)
@@ -66,7 +70,7 @@ namespace MQTTnet.Core.Internal
}
finally
{
cancellationTokenSource.Cancel();
timeoutCts.Cancel();
}
}
}
@@ -1,19 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<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>
<RootNamespace>MQTTnet</RootNamespace>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<DebugType>Full</DebugType>
<AssemblyVersion>2.5.3.0</AssemblyVersion>
<FileVersion>2.5.3.0</FileVersion>
<AssemblyVersion>0.0.0.0</AssemblyVersion>
<FileVersion>0.0.0.0</FileVersion>
<Version>0.0.0.0</Version>
<Company />
<Product />
<Description />
<Authors />
<PackageId />
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
@@ -25,17 +28,17 @@
<TargetFrameworkIdentifier>.NETCore</TargetFrameworkIdentifier>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
<DefineConstants>$(DefineConstants);WINDOWS_UWP</DefineConstants>
<DefaultLanguage>en</DefaultLanguage>
<LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
<ItemGroup>
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard1.3|AnyCPU'">
<DefineConstants>RELEASE;NETSTANDARD1_3</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
<PackageReference Include="System.Net.Security" Version="4.3.2" />
@@ -0,0 +1,19 @@
using System;
namespace MQTTnet.ManagedClient
{
public class ApplicationMessageProcessedEventArgs : EventArgs
{
public ApplicationMessageProcessedEventArgs(MqttApplicationMessage applicationMessage, Exception exception)
{
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
Exception = exception;
}
public MqttApplicationMessage ApplicationMessage { get; }
public Exception Exception { get; }
public bool HasFailed => Exception != null;
public bool HasSucceeded => Exception == null;
}
}
@@ -1,21 +1,23 @@
using System;
using System.Collections.Generic;
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, IDisposable
{
bool IsConnected { get; }
event EventHandler<MqttClientConnectedEventArgs> Connected;
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
Task StartAsync(IManagedMqttClientOptions options);
Task StopAsync();
Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
Task UnsubscribeAsync(IEnumerable<TopicFilter> topicFilters);
Task UnsubscribeAsync(IEnumerable<string> topics);
}
}
@@ -1,7 +1,7 @@
using System;
using MQTTnet.Core.Client;
using MQTTnet.Client;
namespace MQTTnet.Core.ManagedClient
namespace MQTTnet.ManagedClient
{
public interface IManagedMqttClientOptions
{
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MQTTnet.Core.ManagedClient
namespace MQTTnet.ManagedClient
{
public interface IManagedMqttClientStorage
{
@@ -4,18 +4,19 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Client;
using MQTTnet.Core.Diagnostics;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Protocol;
using MQTTnet.Client;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Protocol;
namespace MQTTnet.Core.ManagedClient
namespace MQTTnet.ManagedClient
{
public class ManagedMqttClient : IManagedMqttClient
{
private readonly ManagedMqttClientStorageManager _storageManager = new ManagedMqttClientStorageManager();
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 List<string> _unsubscriptions = new List<string>();
private readonly IMqttClient _mqttClient;
private readonly IMqttNetLogger _logger;
@@ -23,9 +24,11 @@ namespace MQTTnet.Core.ManagedClient
private CancellationTokenSource _connectionCancellationToken;
private CancellationTokenSource _publishingCancellationToken;
private ManagedMqttClientStorageManager _storageManager;
private IManagedMqttClientOptions _options;
private bool _subscriptionsNotPushed;
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
@@ -41,6 +44,8 @@ namespace MQTTnet.Core.ManagedClient
public event EventHandler<MqttClientConnectedEventArgs> Connected;
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
public event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
public event EventHandler SynchronizingSubscriptionsFailed;
public async Task StartAsync(IManagedMqttClientOptions options)
{
@@ -55,21 +60,17 @@ namespace MQTTnet.Core.ManagedClient
if (_connectionCancellationToken != null) throw new InvalidOperationException("The managed client is already started.");
_options = options;
await _storageManager.SetStorageAsync(_options.Storage).ConfigureAwait(false);
if (_options.Storage != null)
{
var loadedMessages = await _options.Storage.LoadQueuedMessagesAsync().ConfigureAwait(false);
foreach (var loadedMessage in loadedMessages)
{
_messageQueue.Add(loadedMessage);
}
_storageManager = new ManagedMqttClientStorageManager(_options.Storage);
await _storageManager.LoadQueuedMessagesAsync().ConfigureAwait(false);
}
_connectionCancellationToken = new CancellationTokenSource();
#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 MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token).ConfigureAwait(false);
Task.Run(async () => await MaintainConnectionAsync(_connectionCancellationToken.Token).ConfigureAwait(false), _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
_logger.Info<ManagedMqttClient>("Started");
@@ -77,11 +78,8 @@ namespace MQTTnet.Core.ManagedClient
public Task StopAsync()
{
_connectionCancellationToken?.Cancel(false);
_connectionCancellationToken = null;
_publishingCancellationToken?.Cancel(false);
_publishingCancellationToken = null;
StopPublishing();
StopMaintainingConnection();
while (_messageQueue.Any())
{
@@ -97,43 +95,60 @@ namespace MQTTnet.Core.ManagedClient
foreach (var applicationMessage in applicationMessages)
{
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
if (_storageManager != null)
{
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
}
_messageQueue.Add(applicationMessage);
}
}
public Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
public async Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
lock (_subscriptions)
await _subscriptionsSemaphore.WaitAsync().ConfigureAwait(false);
try
{
foreach (var topicFilter in topicFilters)
{
if (_subscriptions.Add(topicFilter))
{
_subscriptionsNotPushed = true;
}
_subscriptions[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
_subscriptionsNotPushed = true;
}
}
return Task.FromResult(0);
finally
{
_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))
{
_unsubscriptions.Add(topic);
_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)
@@ -142,33 +157,48 @@ namespace MQTTnet.Core.ManagedClient
{
while (!cancellationToken.IsCancellationRequested)
{
var connectionState = await ReconnectIfRequiredAsync().ConfigureAwait(false);
if (connectionState == ReconnectionResult.NotConnected)
{
_publishingCancellationToken?.Cancel(false);
_publishingCancellationToken = null;
await TryMaintainConnectionAsync(cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
}
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);
continue;
}
private async Task TryMaintainConnectionAsync(CancellationToken cancellationToken)
{
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)
{
await PushSubscriptionsAsync();
if (connectionState == ReconnectionResult.Reconnected || _subscriptionsNotPushed)
{
await SynchronizeSubscriptionsAsync().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.Run(async () => await PublishQueuedMessagesAsync(_publishingCancellationToken.Token), _publishingCancellationToken.Token).ConfigureAwait(false);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
continue;
}
return;
}
if (connectionState == ReconnectionResult.StillConnected)
{
await Task.Delay(TimeSpan.FromSeconds(1), _connectionCancellationToken.Token).ConfigureAwait(false);
}
if (connectionState == ReconnectionResult.StillConnected)
{
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
@@ -182,11 +212,6 @@ namespace MQTTnet.Core.ManagedClient
{
_logger.Error<ManagedMqttClient>(exception, "Unhandled exception while maintaining connection.");
}
finally
{
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
_logger.Info<ManagedMqttClient>("Stopped");
}
}
private async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken)
@@ -207,26 +232,37 @@ namespace MQTTnet.Core.ManagedClient
}
await TryPublishQueuedMessageAsync(message).ConfigureAwait(false);
await _storageManager.RemoveAsync(message).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
}
catch (Exception exception)
{
_logger.Error<ManagedMqttClient>(exception, "Unhandled exception while publishing queued application messages.");
}
finally
{
_logger.Info<ManagedMqttClient>("Stopped publishing messages");
_logger.Trace<ManagedMqttClient>("Stopped publishing messages.");
}
}
private async Task TryPublishQueuedMessageAsync(MqttApplicationMessage message)
{
Exception transmitException = null;
try
{
await _mqttClient.PublishAsync(message).ConfigureAwait(false);
if (_storageManager != null)
{
await _storageManager.RemoveAsync(message).ConfigureAwait(false);
}
}
catch (MqttCommunicationException exception)
{
transmitException = exception;
_logger.Warning<ManagedMqttClient>(exception, "Publishing application message failed.");
if (message.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
@@ -236,34 +272,60 @@ namespace MQTTnet.Core.ManagedClient
}
catch (Exception exception)
{
transmitException = exception;
_logger.Error<ManagedMqttClient>(exception, "Unhandled exception while publishing queued application message.");
}
finally
{
ApplicationMessageProcessed?.Invoke(this, new ApplicationMessageProcessedEventArgs(message, transmitException));
}
}
private async Task PushSubscriptionsAsync()
private async Task SynchronizeSubscriptionsAsync()
{
_logger.Info<ManagedMqttClient>(nameof(ManagedMqttClient), "Synchronizing subscriptions");
List<TopicFilter> subscriptions;
lock (_subscriptions)
List<string> unsubscriptions;
await _subscriptionsSemaphore.WaitAsync().ConfigureAwait(false);
try
{
subscriptions = _subscriptions.ToList();
subscriptions = _subscriptions.Select(i => new TopicFilter(i.Key, i.Value)).ToList();
unsubscriptions = new List<string>(_unsubscriptions);
_unsubscriptions.Clear();
_subscriptionsNotPushed = false;
}
if (!_subscriptions.Any())
finally
{
_subscriptionsSemaphore.Release();
}
if (!subscriptions.Any() && !unsubscriptions.Any())
{
return;
}
try
{
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
if (subscriptions.Any())
{
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
}
if (unsubscriptions.Any())
{
await _mqttClient.UnsubscribeAsync(unsubscriptions).ConfigureAwait(false);
}
}
catch (Exception exception)
{
_logger.Warning<ManagedMqttClient>(exception, "Synchronizing subscriptions failed");
_logger.Warning<ManagedMqttClient>(exception, "Synchronizing subscriptions failed.");
_subscriptionsNotPushed = true;
SynchronizingSubscriptionsFailed?.Invoke(this, EventArgs.Empty);
}
}
@@ -299,5 +361,35 @@ namespace MQTTnet.Core.ManagedClient
{
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.Threading.Tasks;
using MQTTnet.Protocol;
namespace MQTTnet.Core.ManagedClient
namespace MQTTnet.ManagedClient
{
public static class ManagedMqttClientExtensions
{
@@ -12,7 +13,15 @@ namespace MQTTnet.Core.ManagedClient
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));
@@ -1,7 +1,7 @@
using System;
using MQTTnet.Core.Client;
using MQTTnet.Client;
namespace MQTTnet.Core.ManagedClient
namespace MQTTnet.ManagedClient
{
public class ManagedMqttClientOptions : IManagedMqttClientOptions
{
@@ -1,11 +1,12 @@
using System;
using MQTTnet.Core.Client;
using MQTTnet.Client;
namespace MQTTnet.Core.ManagedClient
namespace MQTTnet.ManagedClient
{
public class ManagedMqttClientOptionsBuilder
{
private readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions();
private MqttClientOptionsBuilder _clientOptionsBuilder;
public ManagedMqttClientOptionsBuilder WithAutoReconnectDelay(TimeSpan value)
{
@@ -21,7 +22,24 @@ namespace MQTTnet.Core.ManagedClient
public ManagedMqttClientOptionsBuilder WithClientOptions(IMqttClientOptions value)
{
if (_clientOptionsBuilder != null)
{
throw new InvalidOperationException("Cannot use client options builder and client options at the same time.");
}
_options.ClientOptions = value ?? throw new ArgumentNullException(nameof(value));
return this;
}
public ManagedMqttClientOptionsBuilder WithClientOptions(MqttClientOptionsBuilder builder)
{
if (_options.ClientOptions != null)
{
throw new InvalidOperationException("Cannot use client options builder and client options at the same time.");
}
_clientOptionsBuilder = builder;
return this;
}
@@ -29,15 +47,22 @@ namespace MQTTnet.Core.ManagedClient
{
if (options == null) throw new ArgumentNullException(nameof(options));
var builder = new MqttClientOptionsBuilder();
options(builder);
_options.ClientOptions = builder.Build();
if (_clientOptionsBuilder != null)
{
_clientOptionsBuilder = new MqttClientOptionsBuilder();
}
options(_clientOptionsBuilder);
return this;
}
public ManagedMqttClientOptions Build()
{
if (_clientOptionsBuilder != null)
{
_options.ClientOptions = _clientOptionsBuilder.Build();
}
if (_options.ClientOptions == null)
{
throw new InvalidOperationException("The ClientOptions cannot be null.");
@@ -1,25 +1,27 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.Core.ManagedClient
namespace MQTTnet.ManagedClient
{
public class ManagedMqttClientStorageManager
{
private readonly List<MqttApplicationMessage> _applicationMessages = new List<MqttApplicationMessage>();
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);
try
_storage = storage ?? throw new ArgumentNullException(nameof(storage));
}
public async Task LoadQueuedMessagesAsync()
{
var loadedMessages = await _storage.LoadQueuedMessagesAsync().ConfigureAwait(false);
foreach (var loadedMessage in loadedMessages)
{
_storage = storage;
}
finally
{
_semaphore.Release();
_applicationMessages.Add(loadedMessage);
}
}
@@ -28,11 +30,6 @@ namespace MQTTnet.Core.ManagedClient
await _semaphore.WaitAsync().ConfigureAwait(false);
try
{
if (_storage == null)
{
return;
}
_applicationMessages.Add(applicationMessage);
await SaveAsync().ConfigureAwait(false);
}
@@ -47,11 +44,6 @@ namespace MQTTnet.Core.ManagedClient
await _semaphore.WaitAsync().ConfigureAwait(false);
try
{
if (_storage == null)
{
return;
}
var index = _applicationMessages.IndexOf(applicationMessage);
if (index == -1)
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.ManagedClient
namespace MQTTnet.ManagedClient
{
public enum ReconnectionResult
{
@@ -1,7 +1,7 @@
using System;
using MQTTnet.Core.Protocol;
using MQTTnet.Protocol;
namespace MQTTnet.Core
namespace MQTTnet
{
public sealed class MqttApplicationMessage
{
@@ -2,10 +2,10 @@
using System.IO;
using System.Linq;
using System.Text;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Protocol;
using MQTTnet.Exceptions;
using MQTTnet.Protocol;
namespace MQTTnet.Core
namespace MQTTnet
{
public class MqttApplicationMessageBuilder
{
@@ -0,0 +1,25 @@
using System;
using System.Text;
namespace MQTTnet
{
public static class MqttApplicationMessageExtensions
{
public static string ConvertPayloadToString(this MqttApplicationMessage applicationMessage)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
if (applicationMessage.Payload == null)
{
return null;
}
if (applicationMessage.Payload.Length == 0)
{
return string.Empty;
}
return Encoding.UTF8.GetString(applicationMessage.Payload, 0, applicationMessage.Payload.Length);
}
}
}
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Core
namespace MQTTnet
{
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
{
@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Client;
using MQTTnet.Adapter;
using MQTTnet.Client;
using MQTTnet.Diagnostics;
using MQTTnet.Implementations;
using MQTTnet.Core.ManagedClient;
using MQTTnet.Core.Server;
using MQTTnet.Core.Diagnostics;
using MQTTnet.ManagedClient;
using MQTTnet.Server;
namespace MQTTnet
{
@@ -0,0 +1,7 @@
namespace MQTTnet.Packets
{
public interface IMqttPacketWithIdentifier
{
ushort? PacketIdentifier { get; set; }
}
}
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
public abstract class MqttBasePacket
{
@@ -1,7 +1,7 @@
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
public class MqttBasePublishPacket : MqttBasePacket, IMqttPacketWithIdentifier
{
public ushort PacketIdentifier { get; set; }
public ushort? PacketIdentifier { get; set; }
}
}
@@ -1,6 +1,6 @@
using MQTTnet.Core.Protocol;
using MQTTnet.Protocol;
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
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
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
public sealed class MqttDisconnectPacket : MqttBasePacket
{
@@ -1,6 +1,6 @@
using MQTTnet.Core.Protocol;
using MQTTnet.Protocol;
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
public class MqttPacketHeader
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
public sealed class MqttPingReqPacket : MqttBasePacket
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
public sealed class MqttPingRespPacket : MqttBasePacket
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
public sealed class MqttPubAckPacket : MqttBasePublishPacket
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
public sealed class MqttPubCompPacket : MqttBasePublishPacket
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
public sealed class MqttPubRecPacket : MqttBasePublishPacket
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
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
{
@@ -1,12 +1,12 @@
using System.Collections.Generic;
using System.Linq;
using MQTTnet.Core.Protocol;
using MQTTnet.Protocol;
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
public sealed class MqttSubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
{
public ushort PacketIdentifier { get; set; }
public ushort? PacketIdentifier { get; set; }
public IList<MqttSubscribeReturnCode> SubscribeReturnCodes { get; } = new List<MqttSubscribeReturnCode>();
@@ -1,11 +1,11 @@
using System.Collections.Generic;
using System.Linq;
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
public sealed class MqttSubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
{
public ushort PacketIdentifier { get; set; }
public ushort? PacketIdentifier { get; set; }
public IList<TopicFilter> TopicFilters { get; set; } = new List<TopicFilter>();
@@ -1,8 +1,8 @@
namespace MQTTnet.Core.Packets
namespace MQTTnet.Packets
{
public sealed class MqttUnsubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
{
public ushort PacketIdentifier { get; set; }
public ushort? PacketIdentifier { get; set; }
public override string ToString()
{

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