diff --git a/.bettercodehub.yml b/.bettercodehub.yml
deleted file mode 100644
index 496d2d58..00000000
--- a/.bettercodehub.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-component_depth: 2
-languages:
-- csharp
diff --git a/.gitattributes b/.gitattributes
index 1ff0c423..21256661 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,63 +1 @@
-###############################################################################
-# Set default behavior to automatically normalize line endings.
-###############################################################################
-* text=auto
-
-###############################################################################
-# Set default behavior for command prompt diff.
-#
-# This is need for earlier builds of msysgit that does not have it on by
-# default for csharp files.
-# Note: This is only used by command line
-###############################################################################
-#*.cs diff=csharp
-
-###############################################################################
-# Set the merge driver for project and solution files
-#
-# Merging from the command prompt will add diff markers to the files if there
-# are conflicts (Merging from VS is not affected by the settings below, in VS
-# the diff markers are never inserted). Diff markers may cause the following
-# file extensions to fail to load in VS. An alternative would be to treat
-# these files as binary and thus will always conflict and require user
-# intervention with every merge. To do so, just uncomment the entries below
-###############################################################################
-#*.sln merge=binary
-#*.csproj merge=binary
-#*.vbproj merge=binary
-#*.vcxproj merge=binary
-#*.vcproj merge=binary
-#*.dbproj merge=binary
-#*.fsproj merge=binary
-#*.lsproj merge=binary
-#*.wixproj merge=binary
-#*.modelproj merge=binary
-#*.sqlproj merge=binary
-#*.wwaproj merge=binary
-
-###############################################################################
-# behavior for image files
-#
-# image files are treated as binary by default.
-###############################################################################
-#*.jpg binary
-#*.png binary
-#*.gif binary
-
-###############################################################################
-# diff behavior for common document formats
-#
-# Convert binary document formats to text before diffing them. This feature
-# is only available from the command line. Turn it on by uncommenting the
-# entries below.
-###############################################################################
-#*.doc diff=astextplain
-#*.DOC diff=astextplain
-#*.docx diff=astextplain
-#*.DOCX diff=astextplain
-#*.dot diff=astextplain
-#*.DOT diff=astextplain
-#*.pdf diff=astextplain
-#*.PDF diff=astextplain
-#*.rtf diff=astextplain
-#*.RTF diff=astextplain
+* text=auto
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 24dc74fc..6ed785a3 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -7,16 +7,25 @@ assignees: ''
---
+### Verification
+
+Before opening a bug make sure that the following conditions are met.
+
+
+1. Performance issues are also appearing in RELEASE mode of your application.
+2. An increased memory consumption or high CPU load also happens when your application runs in RELEASE mode AND logging is DISABLED.
+3. Client issues with not received messages, connection drops etc. can be reproduced via another client application like "MQTTnetApp" (https://github.com/chkr1011/MQTTnetApp) or similar.
+4. The bug also appears in the VERY LATEST version of this library. There is no support for older version (due to limited resources) but pull requests for older versions are welcome.
+
### Describe the bug
A clear and concise description of what the bug is.
-### Which project is your bug related to?
-
+### Which component is your bug related to?
+
- Client
- ManagedClient
-- MQTTnet.Server standalone
+- RcpClient
- Server
-- Generic
### To Reproduce
Steps to reproduce the behavior:
@@ -39,7 +48,9 @@ Include debugging or logging information here:
\\ Put your logging output here.
```
### Code example
- Please provide full code examples below where possible to make it easier for the developers to check your issues.
+Please provide full code examples below where possible to make it easier for the developers to check your issues.
+
+**Ideally a Unit Test (which shows the error) is provided so that the behavior can be reproduced easily.**
```csharp
\\ Put your code here.
diff --git a/.github/workflows/ReleaseNotes.md b/.github/workflows/ReleaseNotes.md
new file mode 100644
index 00000000..f6ddfada
--- /dev/null
+++ b/.github/workflows/ReleaseNotes.md
@@ -0,0 +1,27 @@
+We have joined the .NET Foundation!
+
+Version 4 comes with a new API so a lot of breaking changes should be expected.
+Checkout the upgrade guide (https://github.com/dotnet/MQTTnet/wiki/Upgrading-guide) for an overview of the changes.
+Checkout the new samples (https://github.com/dotnet/MQTTnet/tree/feature/master/Samples) how to use the new API. The wiki only remains for version 3 of this library.
+
+* [Core] Improved memory management when working with large payloads.
+* [Core] Added support for .NET 6.0.
+* [Core] nuget packages are now created by MSBuild including more information (i.e. commit hash).
+* [Client] Exposed socket linger state in options.
+* [Client] The OS will now choose the best TLS version to use. It is no longer fixed to 1.3 etc. (thanks to @patagonaa, #1271).
+* [Client] Added support for _ServerKeepAlive_ (MQTTv5).
+* [Client] Exposed user properties and reason string in subscribe result.
+* [Client] Exposed user properties and reason string in unsubscribe result.
+* [Client] Migrated application message handler to a regular .NET event (BREAKING CHANGE!).
+* [Client] The will message is longer a regular application message due to not supported properties by the will message (BREAKING CHANGE!).
+* [Client] Timeouts are no longer handled inside the library. Each method (Connect, Publish etc.) supports a cancellation token so that custom timeouts can and must be used (BREAKING CHANGE!).
+* [Server] Exposed socket linger state in options.
+* [Server] Added support for returning individual subscription errors (#80 thanks to @jimch)
+* [Server] Improved topic filter comparisons (support for $).
+* [Server] Added more MQTTv5 response information to all interceptors (BREAKING CHANGE!).
+* [Server] Improved session management for MQTT v5 (#1294, thanks to @logicaloud).
+* [Server] All interceptors and events are migrated from interfaces to simple events. All existing APIs are availble but must be migrated to corresponding events (BREAKING CHANGE!).
+* [Server] Removed all interceptor and event interfaces including the delegate implementations etc. (BREAKING CHANGE!).
+* [Server] Renamed a lot of classes and adjusted namespaces (BREAKING CHANGE!).
+* [Server] Introduced a new queueing approach for internal message process (packet bus).
+* [Server] For security reasons the default endpoint (1883) is no longer enabled by default (BREAKING CHANGE!).
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 36d7ecdd..f8767c3c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,34 +1,68 @@
name: CI
-on: [push]
+on: [push, pull_request]
+
+env:
+ ASSEMBLY_VERSION: "4.0.0.${{github.run_number}}"
+ NUGET_VERSION: "4.0.0.${{github.run_number}}"
jobs:
build:
- runs-on: windows-latest
- strategy:
- matrix:
- dotnet-version: ['6.0.x']
+ runs-on: windows-2022
steps:
- - uses: actions/checkout@v2
- - name: Setup .NET Core SDK ${{ matrix.dotnet-version }}
- uses: actions/setup-dotnet@v1.7.2
+ - name: Setup Windows SDK
+ uses: GuillaumeFalourd/setup-windows10-sdk-action@v1
with:
- dotnet-version: ${{ matrix.dotnet-version }}
+ sdk-version: 18362
- - name: Build
- shell: pwsh
+ - name: Setup .NET SDK
+ uses: actions/setup-dotnet@v1.9.0
+ with:
+ dotnet-version: |
+ 3.1.x
+ 6.0.x
+
+ - name: Setup MSBuild
+ uses: microsoft/setup-msbuild@v1.1
+
+ - name: Checkout Code
+ uses: actions/checkout@v2
+
+ - name: Setup Signing Certificate
run: |
- Set-Location -Path "Build"
- .\build.ps1 1.2.3 4.5.6-rc1
+ $secret = '${{ secrets.SNC_BASE64 }}'
+ $decoded = [System.Convert]::FromBase64CharArray($secret, 0, $secret.Length)
+ Set-Content -Path ${{ github.workspace }}\certificate.snk -Value $decoded -AsByteStream
+ - name: Restore nuget packages
+ run: msbuild MQTTnet.sln /t:Restore /p:Configuration="Release" /verbosity:m
-# - name: Install dependencies
-# run: dotnet restore MQTTnet.sln
-# - name: Build
-# run: dotnet build --configuration Release --no-restore MQTTnet.sln
-# - name: Test MQTTnet
-# run: dotnet test --no-restore --verbosity normal Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj
-# - name: Test AspNetCore
-# run: dotnet test --no-restore --verbosity normal Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj
\ No newline at end of file
+ - name: Build solution
+ run: msbuild MQTTnet.sln /t:Build /p:Configuration="Release" /verbosity:m /p:FileVersion=${{ env.ASSEMBLY_VERSION }} /p:AssemblyVersion=${{ env.ASSEMBLY_VERSION }} /p:PackageVersion=${{ env.NUGET_VERSION }} /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=${{ github.workspace }}\certificate.snk
+
+ - name: Collect nuget Packages
+ uses: actions/upload-artifact@v2
+ with:
+ name: nuget Packages
+ path: |
+ **\*.nupkg
+ **\*.snupkg
+
+ - name: Setup VSTest
+ uses: darenm/Setup-VSTest@v1
+
+ - name: Core Tests
+ run: vstest.console.exe Source\MQTTnet.Tests\bin\Release\net6.0\MQTTnet.Tests.dll
+
+ - name: ASP.NET Tests
+ run: vstest.console.exe Source\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.Tests.dll
+
+ - name: Publish MyGet nugets
+ if: ${{ github.event_name == 'push' }}
+ run: dotnet nuget push **/*.nupkg -k ${{ secrets.MYGET_API_KEY }} -s https://www.myget.org/F/mqttnet/api/v3/index.json --skip-duplicate
+
+# - name: Publish nugets
+# if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
+# run: dotnet nuget push **/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate
\ No newline at end of file
diff --git a/Build/MQTTnet.AspNetCore.nuspec b/Build/MQTTnet.AspNetCore.nuspec
deleted file mode 100644
index 68fa9dbd..00000000
--- a/Build/MQTTnet.AspNetCore.nuspec
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
- MQTTnet.AspNetCore
- 0.0.0
- The contributors of MQTTnet
- Christian Kratky
- LICENSE
- https://github.com/chkr1011/MQTTnet
- https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png
- images\Logo_128x128.png
- true
- This is a support library to integrate MQTTnet into AspNetCore.
- For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).
- Copyright Christian Kratky 2016-2021
- 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 Blazor
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Build/MQTTnet.Extensions.ManagedClient.nuspec b/Build/MQTTnet.Extensions.ManagedClient.nuspec
deleted file mode 100644
index 8c43ed63..00000000
--- a/Build/MQTTnet.Extensions.ManagedClient.nuspec
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
- MQTTnet.Extensions.ManagedClient
- 0.0.0
- The contributors of MQTTnet
- Christian Kratky
- LICENSE
- https://github.com/chkr1011/MQTTnet
- https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png
- images\Logo_128x128.png
- true
- This is an extension library which provides a managed MQTT client with additional features using MQTTnet.
- For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).
- Copyright Christian Kratky 2016-2021
- 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 Blazor
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Build/MQTTnet.Extensions.Rpc.nuspec b/Build/MQTTnet.Extensions.Rpc.nuspec
deleted file mode 100644
index d0384138..00000000
--- a/Build/MQTTnet.Extensions.Rpc.nuspec
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
- MQTTnet.Extensions.Rpc
- 0.0.0
- The contributors of MQTTnet
- Christian Kratky
- LICENSE
- https://github.com/chkr1011/MQTTnet
- https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png
- images\Logo_128x128.png
- true
- This is an extension library which allows executing synchronous device calls including a response using MQTTnet.
- For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).
- Copyright Christian Kratky 2016-2021
- 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 Blazor
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Build/MQTTnet.Extensions.WebSocket4Net.nuspec b/Build/MQTTnet.Extensions.WebSocket4Net.nuspec
deleted file mode 100644
index 21baa845..00000000
--- a/Build/MQTTnet.Extensions.WebSocket4Net.nuspec
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
- MQTTnet.Extensions.WebSocket4Net
- 0.0.0
- The contributors of MQTTnet
- Christian Kratky
- LICENSE
- https://github.com/chkr1011/MQTTnet
- https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png
- images\Logo_128x128.png
- true
- This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients.
- For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).
- Copyright Christian Kratky 2016-2021
- 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 Blazor
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec
deleted file mode 100644
index a5242c4e..00000000
--- a/Build/MQTTnet.nuspec
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
-
- MQTTnet
- 0.0.0
- The contributors of MQTTnet
- Christian Kratky
- LICENSE
- https://github.com/chkr1011/MQTTnet
- https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png
- images\Logo_128x128.png
- true
- MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.
-
-* [Client] Increased delay for keep alive checks do decrease CPU load.
-* [Core] Decreased object allocations (#1324, thanks to @gfoidl).
-* [Core] Decreased object allocations when logging is not active (thanks to @gfoidl, @Tymoniden).
-* [Client] Fixed issue in _MqttApplicationMessageBuilder.WithPayload_ (#1322, thanks to @gfoidl).
-* [Client] Adjusted default SslProtocol values to Tls12 and Tls13 (#1347).
-* [Extensions.WebSocket4Net] Adjusted default SslProtocol values to Tls12 and Tls13 (#1347).
-
-Git commit: $gitCommit
-
- Copyright Christian Kratky 2016-2021
- 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 Blazor
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Build/build.ps1 b/Build/build.ps1
deleted file mode 100644
index 7af37d8b..00000000
--- a/Build/build.ps1
+++ /dev/null
@@ -1,99 +0,0 @@
-param([string]$assemblyVersion, [string]$nugetVersion)
-
-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'
-$msbuild = &$vswhere -products * -requires Microsoft.Component.MSBuild -latest -find MSBuild\**\Bin\MSBuild.exe
-$vstest = &$vswhere -products * -latest -find **\TestWindow\**\vstest.console.exe
-
-Write-Host
-Write-Host "Assembly version = $assemblyVersion"
-Write-Host "Nuget version = $nugetVersion"
-Write-Host "MSBuild path = $msbuild"
-Write-Host
-
-Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile "nuget.exe"
-
-.\nuget.exe restore ..\MQTTnet.sln
-
-# Build and execute tests
-&$msbuild ..\Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /verbosity:m
-&$msbuild ..\Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /verbosity:m
-
-&$vstest ..\Tests\MQTTnet.Core.Tests\bin\Release\netcoreapp3.1\MQTTnet.Tests.dll
-&$vstest ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.Tests.dll
-
-# Build the core library
-&$msbuild ..\Source\MQTTnet\MQTTnet.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 ..\Source\MQTTnet\MQTTnet.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 ..\Source\MQTTnet\MQTTnet.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 ..\Source\MQTTnet\MQTTnet.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 ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
-&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
-&$msbuild ..\Source\MQTTnet\MQTTnet.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 ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
-
-# Build the ASP.NET Core 2.0 extension
-&$msbuild ..\Source\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"
-&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
-&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
-
-# Build the RPC extension
-&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.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 ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.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 ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.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 ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.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 ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
-&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.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 ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
-
-# Build the Managed Client extension
-&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.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 ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.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 ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.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 ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.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 ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
-&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.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 ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
-
-# Build the WebSocket4Net extension
-&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.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 ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.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 ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.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 ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.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 ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
-&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.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 ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
-
-# Create NuGet packages.
-
-Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
-
-$gitCommit = git log -1 --format=%h
-
-Copy-Item MQTTnet.nuspec -Destination MQTTnet.nuspec.old -Force
-(Get-Content MQTTnet.nuspec) -replace '\$gitCommit', $gitCommit | Set-Content MQTTnet.nuspec
-Copy-Item MQTTnet.AspNetCore.nuspec -Destination MQTTnet.AspNetCore.nuspec.old -Force
-(Get-Content MQTTnet.AspNetCore.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.AspNetCore.nuspec
-Copy-Item MQTTnet.Extensions.Rpc.nuspec -Destination MQTTnet.Extensions.Rpc.nuspec.old -Force
-(Get-Content MQTTnet.Extensions.Rpc.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.Rpc.nuspec
-Copy-Item MQTTnet.Extensions.ManagedClient.nuspec -Destination MQTTnet.Extensions.ManagedClient.nuspec.old -Force
-(Get-Content MQTTnet.Extensions.ManagedClient.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.ManagedClient.nuspec
-Copy-Item MQTTnet.Extensions.WebSocket4Net.nuspec -Destination MQTTnet.Extensions.WebSocket4Net.nuspec.old -Force
-(Get-Content MQTTnet.Extensions.WebSocket4Net.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.WebSocket4Net.nuspec
-
-New-Item -ItemType Directory -Force -Path .\NuGet
-.\nuget.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -OutputDir "NuGet" -Version $nugetVersion
-.\nuget.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -OutputDir "NuGet" -Version $nugetVersion
-.\nuget.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -OutputDir "NuGet" -Version $nugetVersion
-.\nuget.exe pack MQTTnet.Extensions.ManagedClient.nuspec -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -OutputDir "NuGet" -Version $nugetVersion
-.\nuget.exe pack MQTTnet.Extensions.WebSocket4Net.nuspec -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -OutputDir "NuGet" -Version $nugetVersion
-
-Move-Item MQTTnet.nuspec.old -Destination MQTTnet.nuspec -Force
-Move-Item MQTTnet.AspNetCore.nuspec.old -Destination MQTTnet.AspNetCore.nuspec -Force
-Move-Item MQTTnet.Extensions.Rpc.nuspec.old -Destination MQTTnet.Extensions.Rpc.nuspec -Force
-Move-Item MQTTnet.Extensions.ManagedClient.nuspec.old -Destination MQTTnet.Extensions.ManagedClient.nuspec -Force
-Move-Item MQTTnet.Extensions.WebSocket4Net.nuspec.old -Destination MQTTnet.Extensions.WebSocket4Net.nuspec -Force
-
-Remove-Item "nuget.exe" -Force -Recurse -ErrorAction SilentlyContinue
\ No newline at end of file
diff --git a/Build/codeSigningKey.pfx b/Build/codeSigningKey.pfx
deleted file mode 100644
index 9374a5db..00000000
Binary files a/Build/codeSigningKey.pfx and /dev/null differ
diff --git a/Build/upload.ps1 b/Build/upload.ps1
deleted file mode 100644
index 794fe87f..00000000
--- a/Build/upload.ps1
+++ /dev/null
@@ -1,13 +0,0 @@
-param([string]$apiKey)
-
-Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile "nuget.exe"
-
-$files = Get-ChildItem -Path ".\NuGet" -Filter "*.nupkg"
-foreach ($file in $files)
-{
- Write-Host "Uploading: " $file
-
- .\nuget.exe push $file.Fullname $apiKey -Source https://api.nuget.org/v3/index.json
-}
-
-Remove-Item "nuget.exe" -Force -Recurse -ErrorAction SilentlyContinue
\ No newline at end of file
diff --git a/Documents/Import_CodeSigningKey.md b/Documents/Import_CodeSigningKey.md
deleted file mode 100644
index f14add98..00000000
--- a/Documents/Import_CodeSigningKey.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# 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_62CA73019ED23333
-
-The container name may be different.
-
-# Check if the assembly has a strong name
-sn -vf MQTTnet.dll
\ No newline at end of file
diff --git a/Images/nuget.png b/Images/nuget.png
new file mode 100644
index 00000000..b725537a
Binary files /dev/null and b/Images/nuget.png differ
diff --git a/LICENSE b/LICENSE
index 525b699a..d18aef99 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,7 @@
-MIT License
+The MIT License (MIT)
-Copyright (c) 2016-2019 Christian Kratky
+Copyright (c) .NET Foundation and Contributors
+All Rights Reserved
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/MQTTnet.noUWP.sln b/MQTTnet.noUWP.sln
deleted file mode 100644
index 5fcfbb87..00000000
--- a/MQTTnet.noUWP.sln
+++ /dev/null
@@ -1,258 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27004.2010
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}"
- ProjectSection(SolutionItems) = preProject
- Build\build.ps1 = Build\build.ps1
- Build\MQTTnet.AspNetCore.nuspec = Build\MQTTnet.AspNetCore.nuspec
- Build\MQTTnet.Extensions.ManagedClient.nuspec = Build\MQTTnet.Extensions.ManagedClient.nuspec
- Build\MQTTnet.Extensions.Rpc.nuspec = Build\MQTTnet.Extensions.Rpc.nuspec
- Build\MQTTnet.nuspec = Build\MQTTnet.nuspec
- Build\upload.ps1 = Build\upload.ps1
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}"
- ProjectSection(SolutionItems) = preProject
- .bettercodehub.yml = .bettercodehub.yml
- appveyor.yml = appveyor.yml
- LICENSE = LICENSE
- README.md = README.md
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.NetCore", "Tests\MQTTnet.TestApp.NetCore\MQTTnet.TestApp.NetCore.csproj", "{3D283AAD-AAA8-4339-8394-52F80B6304DB}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.AspNetCore2", "Tests\MQTTnet.TestApp.AspNetCore2\MQTTnet.TestApp.AspNetCore2.csproj", "{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore", "Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{12816BCC-AF9E-44A9-9AE5-C246AF2A0587}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.Rpc", "Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj", "{C444E9C8-95FA-430E-9126-274129DE16CD}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Benchmarks", "Tests\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{998D04DD-7CB0-45F5-A393-E2495C16399E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedClient", "Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj", "{C400533A-8EBA-4F0B-BF4D-295C3708604B}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj", "{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.WebSocket4Net", "Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj", "{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Debug|ARM = Debug|ARM
- Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
- Release|Any CPU = Release|Any CPU
- Release|ARM = Release|ARM
- Release|x64 = Release|x64
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.Build.0 = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.ActiveCfg = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.Build.0 = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.ActiveCfg = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.Build.0 = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.Build.0 = Release|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.ActiveCfg = Release|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.Build.0 = Release|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.ActiveCfg = Release|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.Build.0 = Release|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x86.ActiveCfg = Release|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x86.Build.0 = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|ARM.Build.0 = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x64.ActiveCfg = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x64.Build.0 = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x86.ActiveCfg = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x86.Build.0 = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|Any CPU.Build.0 = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|ARM.ActiveCfg = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|ARM.Build.0 = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.ActiveCfg = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.Build.0 = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.ActiveCfg = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.Build.0 = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.Build.0 = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.ActiveCfg = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.Build.0 = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.Build.0 = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.Build.0 = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.ActiveCfg = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.Build.0 = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.ActiveCfg = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.Build.0 = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.ActiveCfg = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.Build.0 = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.Build.0 = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.ActiveCfg = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.Build.0 = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.ActiveCfg = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.Build.0 = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.Build.0 = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.ActiveCfg = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.Build.0 = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.ActiveCfg = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.Build.0 = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.ActiveCfg = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.Build.0 = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.Build.0 = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.ActiveCfg = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.Build.0 = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.Build.0 = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.Build.0 = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.ActiveCfg = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.Build.0 = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.ActiveCfg = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.Build.0 = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.ActiveCfg = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.Build.0 = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.Build.0 = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.ActiveCfg = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.Build.0 = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.ActiveCfg = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.Build.0 = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.Build.0 = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.ActiveCfg = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.Build.0 = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.ActiveCfg = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.Build.0 = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.ActiveCfg = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.Build.0 = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.Build.0 = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.ActiveCfg = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.Build.0 = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.Build.0 = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.Build.0 = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.ActiveCfg = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.Build.0 = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.ActiveCfg = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.Build.0 = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.ActiveCfg = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.Build.0 = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.Build.0 = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.ActiveCfg = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.Build.0 = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.ActiveCfg = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.Build.0 = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.Build.0 = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.ActiveCfg = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.Build.0 = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.ActiveCfg = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.Build.0 = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.ActiveCfg = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.Build.0 = Release|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|ARM.Build.0 = Debug|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x64.ActiveCfg = Debug|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x64.Build.0 = Debug|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x86.ActiveCfg = Debug|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x86.Build.0 = Debug|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|Any CPU.Build.0 = Release|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|ARM.ActiveCfg = Release|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|ARM.Build.0 = Release|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x64.ActiveCfg = Release|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x64.Build.0 = Release|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x86.ActiveCfg = Release|Any CPU
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x86.Build.0 = Release|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|ARM.Build.0 = Debug|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x64.ActiveCfg = Debug|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x64.Build.0 = Debug|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x86.ActiveCfg = Debug|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x86.Build.0 = Debug|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|Any CPU.Build.0 = Release|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|ARM.ActiveCfg = Release|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|ARM.Build.0 = Release|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x64.ActiveCfg = Release|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x64.Build.0 = Release|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x86.ActiveCfg = Release|Any CPU
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x86.Build.0 = Release|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|ARM.Build.0 = Debug|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|x64.ActiveCfg = Debug|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|x64.Build.0 = Debug|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|x86.ActiveCfg = Debug|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|x86.Build.0 = Debug|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|Any CPU.Build.0 = Release|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|ARM.ActiveCfg = Release|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|ARM.Build.0 = Release|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|x64.ActiveCfg = Release|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|x64.Build.0 = Release|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|x86.ActiveCfg = Release|Any CPU
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|x86.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
- {3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
- {F10C4060-F7EE-4A83-919F-FF723E72F94A} = {32A630A7-2598-41D7-B625-204CD906F5FB}
- {C444E9C8-95FA-430E-9126-274129DE16CD} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
- {998D04DD-7CB0-45F5-A393-E2495C16399E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
- {C400533A-8EBA-4F0B-BF4D-295C3708604B} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
- {9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
- {DADEA836-5CC3-474C-A2D8-4D0F31118CD7} = {32A630A7-2598-41D7-B625-204CD906F5FB}
- {9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
- EndGlobalSection
-EndGlobal
diff --git a/MQTTnet.sln b/MQTTnet.sln
index 65b577b8..34abf629 100644
--- a/MQTTnet.sln
+++ b/MQTTnet.sln
@@ -1,301 +1,92 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29009.5
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31919.166
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}"
- ProjectSection(SolutionItems) = preProject
- Build\build.ps1 = Build\build.ps1
- Build\MQTTnet.AspNetCore.nuspec = Build\MQTTnet.AspNetCore.nuspec
- Build\MQTTnet.Extensions.ManagedClient.nuspec = Build\MQTTnet.Extensions.ManagedClient.nuspec
- Build\MQTTnet.Extensions.Rpc.nuspec = Build\MQTTnet.Extensions.Rpc.nuspec
- Build\MQTTnet.Extensions.WebSocket4Net.nuspec = Build\MQTTnet.Extensions.WebSocket4Net.nuspec
- Build\MQTTnet.nuspec = Build\MQTTnet.nuspec
- Build\upload.ps1 = Build\upload.ps1
- EndProjectSection
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}"
ProjectSection(SolutionItems) = preProject
- .bettercodehub.yml = .bettercodehub.yml
- .editorconfig = .editorconfig
- appveyor.yml = appveyor.yml
LICENSE = LICENSE
README.md = README.md
+ CODE-OF-CONDUCT.md = CODE-OF-CONDUCT.md
EndProjectSection
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.NetCore", "Tests\MQTTnet.TestApp.NetCore\MQTTnet.TestApp.NetCore.csproj", "{3D283AAD-AAA8-4339-8394-52F80B6304DB}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.AspNetCore2", "Tests\MQTTnet.TestApp.AspNetCore2\MQTTnet.TestApp.AspNetCore2.csproj", "{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore", "Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{12816BCC-AF9E-44A9-9AE5-C246AF2A0587}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.Rpc", "Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj", "{C444E9C8-95FA-430E-9126-274129DE16CD}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Benchmarks", "Tests\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{998D04DD-7CB0-45F5-A393-E2495C16399E}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedClient", "Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj", "{C400533A-8EBA-4F0B-BF4D-295C3708604B}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.WebSocket4Net", "Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj", "{2BD01D53-4CA5-4142-BE8D-313876395E3E}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Blazor", "Blazor", "{61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Samples", "Samples\MQTTnet.Samples.csproj", "{71CF35F5-3327-4A91-AAF4-5340F6701771}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Test.BlazorApp.Server", "Tests\MQTTnet.Test.BlazorApp\Server\MQTTnet.Test.BlazorApp.Server.csproj", "{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Tests", "Source\MQTTnet.Tests\MQTTnet.Tests.csproj", "{B270F32A-9F3E-42EE-A989-813E35E29ADB}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Test.BlazorApp.Client", "Tests\MQTTnet.Test.BlazorApp\Client\MQTTnet.Test.BlazorApp.Client.csproj", "{D260D63D-7902-4C55-9665-84C5CACBBB24}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.AspNetCore.Tests", "Source\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{A238BBBF-C75F-482D-9CC3-BB34ABA9B675}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Test.BlazorApp.Shared", "Tests\MQTTnet.Test.BlazorApp\Shared\MQTTnet.Test.BlazorApp.Shared.csproj", "{DDB069BA-6E1A-48C7-B374-5D903B553CAD}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Benchmarks", "Source\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp", "Source\MQTTnet.TestApp\MQTTnet.TestApp.csproj", "{175D5340-CC5B-4542-939D-4E7D15A0BC8D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.AspTestApp", "Source\MQTTnet.AspTestApp\MQTTnet.AspTestApp.csproj", "{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
- Debug|ARM = Debug|ARM
- Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
- Release|ARM = Release|ARM
- Release|x64 = Release|x64
- Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.Build.0 = Debug|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x64.ActiveCfg = Debug|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x64.Build.0 = Debug|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.ActiveCfg = Debug|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.Build.0 = Debug|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|Any CPU.Build.0 = Release|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|ARM.ActiveCfg = Release|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|ARM.Build.0 = Release|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.ActiveCfg = Release|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|Any CPU
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|Any CPU
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.Build.0 = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.ActiveCfg = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.Build.0 = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.ActiveCfg = Debug|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.Build.0 = Debug|Any CPU
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.Build.0 = Release|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.ActiveCfg = Release|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.Build.0 = Release|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.ActiveCfg = Release|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.Build.0 = Release|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x86.ActiveCfg = Release|Any CPU
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x86.Build.0 = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|ARM.Build.0 = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x64.ActiveCfg = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x64.Build.0 = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x86.ActiveCfg = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x86.Build.0 = Debug|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|Any CPU.Build.0 = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|ARM.ActiveCfg = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|ARM.Build.0 = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.ActiveCfg = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.Build.0 = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.ActiveCfg = Release|Any CPU
- {3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.Build.0 = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.Build.0 = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.ActiveCfg = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.Build.0 = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.Build.0 = Debug|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.Build.0 = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.ActiveCfg = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.Build.0 = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.ActiveCfg = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.Build.0 = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.ActiveCfg = Release|Any CPU
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.Build.0 = Release|Any CPU
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.Build.0 = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.ActiveCfg = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.Build.0 = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.ActiveCfg = Debug|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.Build.0 = Debug|Any CPU
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.Build.0 = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.ActiveCfg = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.Build.0 = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.ActiveCfg = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.Build.0 = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.ActiveCfg = Release|Any CPU
- {F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.Build.0 = Release|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.Build.0 = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.ActiveCfg = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.Build.0 = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.Build.0 = Debug|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.Build.0 = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.ActiveCfg = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.Build.0 = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.ActiveCfg = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.Build.0 = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.ActiveCfg = Release|Any CPU
- {C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.Build.0 = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.Build.0 = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.ActiveCfg = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.Build.0 = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.ActiveCfg = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.Build.0 = Debug|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.Build.0 = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.ActiveCfg = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.Build.0 = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.ActiveCfg = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.Build.0 = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.ActiveCfg = Release|Any CPU
- {998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.Build.0 = Release|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.Build.0 = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.ActiveCfg = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.Build.0 = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.Build.0 = Debug|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.Build.0 = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.ActiveCfg = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.Build.0 = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.ActiveCfg = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.Build.0 = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.ActiveCfg = Release|Any CPU
- {C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.Build.0 = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.Build.0 = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.ActiveCfg = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.Build.0 = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.ActiveCfg = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.Build.0 = Debug|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.Build.0 = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.ActiveCfg = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.Build.0 = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.ActiveCfg = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.Build.0 = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.ActiveCfg = Release|Any CPU
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.Build.0 = Release|Any CPU
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|ARM.Build.0 = Debug|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|x64.ActiveCfg = Debug|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|x64.Build.0 = Debug|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|x86.ActiveCfg = Debug|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|x86.Build.0 = Debug|Any CPU
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|Any CPU.Build.0 = Release|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|ARM.ActiveCfg = Release|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|ARM.Build.0 = Release|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x64.ActiveCfg = Release|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x64.Build.0 = Release|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x86.ActiveCfg = Release|Any CPU
- {2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x86.Build.0 = Release|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|ARM.Build.0 = Debug|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|x64.ActiveCfg = Debug|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|x64.Build.0 = Debug|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|x86.ActiveCfg = Debug|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|x86.Build.0 = Debug|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|Any CPU.Build.0 = Release|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|ARM.ActiveCfg = Release|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|ARM.Build.0 = Release|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|x64.ActiveCfg = Release|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|x64.Build.0 = Release|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|x86.ActiveCfg = Release|Any CPU
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|x86.Build.0 = Release|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|ARM.Build.0 = Debug|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|x64.ActiveCfg = Debug|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|x64.Build.0 = Debug|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|x86.ActiveCfg = Debug|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|x86.Build.0 = Debug|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|Any CPU.Build.0 = Release|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|ARM.ActiveCfg = Release|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|ARM.Build.0 = Release|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|x64.ActiveCfg = Release|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|x64.Build.0 = Release|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|x86.ActiveCfg = Release|Any CPU
- {D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|x86.Build.0 = Release|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|ARM.Build.0 = Debug|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|x64.ActiveCfg = Debug|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|x64.Build.0 = Debug|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|x86.ActiveCfg = Debug|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|x86.Build.0 = Debug|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|Any CPU.Build.0 = Release|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|ARM.ActiveCfg = Release|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|ARM.Build.0 = Release|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|x64.ActiveCfg = Release|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|x64.Build.0 = Release|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|x86.ActiveCfg = Release|Any CPU
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|x86.Build.0 = Release|Any CPU
+ {71CF35F5-3327-4A91-AAF4-5340F6701771}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {71CF35F5-3327-4A91-AAF4-5340F6701771}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {71CF35F5-3327-4A91-AAF4-5340F6701771}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {71CF35F5-3327-4A91-AAF4-5340F6701771}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B270F32A-9F3E-42EE-A989-813E35E29ADB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B270F32A-9F3E-42EE-A989-813E35E29ADB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B270F32A-9F3E-42EE-A989-813E35E29ADB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B270F32A-9F3E-42EE-A989-813E35E29ADB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A238BBBF-C75F-482D-9CC3-BB34ABA9B675}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A238BBBF-C75F-482D-9CC3-BB34ABA9B675}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A238BBBF-C75F-482D-9CC3-BB34ABA9B675}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A238BBBF-C75F-482D-9CC3-BB34ABA9B675}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2F516E76-AAC4-4219-B7D1-34CDD3CFF381}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2F516E76-AAC4-4219-B7D1-34CDD3CFF381}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2F516E76-AAC4-4219-B7D1-34CDD3CFF381}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2F516E76-AAC4-4219-B7D1-34CDD3CFF381}.Release|Any CPU.Build.0 = Release|Any CPU
+ {175D5340-CC5B-4542-939D-4E7D15A0BC8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {175D5340-CC5B-4542-939D-4E7D15A0BC8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {175D5340-CC5B-4542-939D-4E7D15A0BC8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {175D5340-CC5B-4542-939D-4E7D15A0BC8D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
- {3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
- {3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
- {C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
- {F10C4060-F7EE-4A83-919F-FF723E72F94A} = {32A630A7-2598-41D7-B625-204CD906F5FB}
- {C444E9C8-95FA-430E-9126-274129DE16CD} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
- {998D04DD-7CB0-45F5-A393-E2495C16399E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
- {C400533A-8EBA-4F0B-BF4D-295C3708604B} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
- {61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
- {2BD01D53-4CA5-4142-BE8D-313876395E3E} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
- {61B165A0-5AA8-4E04-A53D-A22A84AA6EB7} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
- {A9662AF3-3520-4BF8-9DFF-C55C0C33D08F} = {61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}
- {D260D63D-7902-4C55-9665-84C5CACBBB24} = {61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}
- {DDB069BA-6E1A-48C7-B374-5D903B553CAD} = {61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
diff --git a/MQTTnet.sln.DotSettings b/MQTTnet.sln.DotSettings
index e81ffe21..504fc3a2 100644
--- a/MQTTnet.sln.DotSettings
+++ b/MQTTnet.sln.DotSettings
@@ -1,16 +1,241 @@
+ Required
+ Required
+ Required
+ Required
+ False
+ False
+ False
+ False
+ False
+ FalseNEVER
+ False
+ NEVER
+ False
+ FalseFalseFalseFalse
+ True
+ True
+ CHOP_IF_LONG
+ CHOP_IF_LONG
+ CHOP_IF_LONG
+ CHOP_IF_LONG
+ 180
+ CHOP_IF_LONG
+ True
+ OnDifferentLines
+ OnDifferentLines
+ <?xml version="1.0" encoding="utf-16"?>
+<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
+ <TypePattern DisplayName="Non-reorderable types">
+ <TypePattern.Match>
+ <Or>
+ <And>
+ <Kind Is="Interface" />
+ <Or>
+ <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" />
+ <HasAttribute Name="System.Runtime.InteropServices.ComImport" />
+ </Or>
+ </And>
+ <Kind Is="Struct" />
+ <HasAttribute Name="JetBrains.Annotations.NoReorderAttribute" />
+ <HasAttribute Name="JetBrains.Annotations.NoReorder" />
+ </Or>
+ </TypePattern.Match>
+ </TypePattern>
+ <TypePattern DisplayName="xUnit.net Test Classes" RemoveRegions="All">
+ <TypePattern.Match>
+ <And>
+ <Kind Is="Class" />
+ <HasMember>
+ <And>
+ <Kind Is="Method" />
+ <HasAttribute Name="Xunit.FactAttribute" Inherited="True" />
+ <HasAttribute Name="Xunit.TheoryAttribute" Inherited="True" />
+ </And>
+ </HasMember>
+ </And>
+ </TypePattern.Match>
+ <Entry DisplayName="Setup/Teardown Methods">
+ <Entry.Match>
+ <Or>
+ <Kind Is="Constructor" />
+ <And>
+ <Kind Is="Method" />
+ <ImplementsInterface Name="System.IDisposable" />
+ </And>
+ </Or>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Kind Order="Constructor" />
+ </Entry.SortBy>
+ </Entry>
+ <Entry DisplayName="All other members" />
+ <Entry Priority="100" DisplayName="Test Methods">
+ <Entry.Match>
+ <And>
+ <Kind Is="Method" />
+ <HasAttribute Name="Xunit.FactAttribute" />
+ <HasAttribute Name="Xunit.TheoryAttribute" />
+ </And>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ </TypePattern>
+ <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All">
+ <TypePattern.Match>
+ <And>
+ <Kind Is="Class" />
+ <Or>
+ <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" />
+ <HasAttribute Name="NUnit.Framework.TestFixtureSourceAttribute" Inherited="True" />
+ <HasMember>
+ <And>
+ <Kind Is="Method" />
+ <HasAttribute Name="NUnit.Framework.TestAttribute" />
+ <HasAttribute Name="NUnit.Framework.TestCaseAttribute" />
+ <HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" />
+ </And>
+ </HasMember>
+ </Or>
+ </And>
+ </TypePattern.Match>
+ <Entry DisplayName="Setup/Teardown Methods">
+ <Entry.Match>
+ <And>
+ <Kind Is="Method" />
+ <Or>
+ <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" />
+ <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" />
+ <HasAttribute Name="NUnit.Framework.TestFixtureSetUpAttribute" Inherited="True" />
+ <HasAttribute Name="NUnit.Framework.TestFixtureTearDownAttribute" Inherited="True" />
+ <HasAttribute Name="NUnit.Framework.OneTimeSetUpAttribute" Inherited="True" />
+ <HasAttribute Name="NUnit.Framework.OneTimeTearDownAttribute" Inherited="True" />
+ </Or>
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="All other members" />
+ <Entry Priority="100" DisplayName="Test Methods">
+ <Entry.Match>
+ <And>
+ <Kind Is="Method" />
+ <HasAttribute Name="NUnit.Framework.TestAttribute" />
+ <HasAttribute Name="NUnit.Framework.TestCaseAttribute" />
+ <HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" />
+ </And>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ </TypePattern>
+ <TypePattern DisplayName="Default Pattern">
+ <Entry Priority="100" DisplayName="Public Delegates">
+ <Entry.Match>
+ <And>
+ <Access Is="Public" />
+ <Kind Is="Delegate" />
+ </And>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ <Entry Priority="100" DisplayName="Public Enums">
+ <Entry.Match>
+ <And>
+ <Access Is="Public" />
+ <Kind Is="Enum" />
+ </And>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ <Entry DisplayName="Static Fields and Constants">
+ <Entry.Match>
+ <Or>
+ <Kind Is="Constant" />
+ <And>
+ <Kind Is="Field" />
+ <Static />
+ </And>
+ </Or>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Kind Order="Constant Field" />
+ </Entry.SortBy>
+ </Entry>
+ <Entry DisplayName="Fields">
+ <Entry.Match>
+ <And>
+ <Kind Is="Field" />
+ <Not>
+ <Static />
+ </Not>
+ </And>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Readonly />
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ <Entry DisplayName="Constructors">
+ <Entry.Match>
+ <Kind Is="Constructor" />
+ </Entry.Match>
+ <Entry.SortBy>
+ <Static />
+ </Entry.SortBy>
+ </Entry>
+ <Entry DisplayName="Events">
+ <Entry.Match>
+ <Kind Is="Event" />
+ </Entry.Match>
+ <Entry.SortBy>
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ <Entry DisplayName="Properties, Indexers">
+ <Entry.Match>
+ <Or>
+ <Kind Is="Property" />
+ <Kind Is="Indexer" />
+ </Or>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ <Entry DisplayName="All other members">
+ <Entry.SortBy>
+ <Access Order="Public Internal Protected Private" />
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ <Entry DisplayName="Nested Types">
+ <Entry.Match>
+ <Kind Is="Type" />
+ </Entry.Match>
+ </Entry>
+ </TypePattern>
+</Patterns>TrueTrueTrueTrueTrueTrue
+ TrueTrueTrueTrueTrue
+ TrueTrue
\ No newline at end of file
diff --git a/README.md b/README.md
index 6cecfa57..6fd25db1 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,19 @@
-
+
[](https://www.nuget.org/packages/MQTTnet)
-[](https://bettercodehub.com/)
+[](https://github.com/dotnet/MQTTnet/actions/workflows/ci.yml)
+[](https://www.myget.org/feed/mqttnet/package/nuget/MQTTnet)
+
[](https://gitter.im/MQTTnet/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[](https://raw.githubusercontent.com/chkr1011/MQTTnet/master/LICENSE)
+[](https://raw.githubusercontent.com/dotnet/MQTTnet/master/LICENSE)
# MQTTnet
-MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker). The implementation is based on the documentation from .
+MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports the MQTT protocol up to version 5.
## Features
@@ -21,11 +23,11 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
* TLS support for client and server (but not UWP servers)
* Extensible communication channels (e.g. In-Memory, TCP, TCP+TLS, WS)
* Lightweight (only the low level implementation of MQTT, no overhead)
-* Performance optimized (processing ~70.000 messages / second)*
+* Performance optimized (processing ~150.000 messages / second)*
* Uniform API across all supported versions of the MQTT protocol
* Interfaces included for mocking and testing
* Access to internal trace messages
-* Unit tested (~250 tests)
+* Unit tested (~636 tests)
* No external dependencies
\* Tested on local machine (Intel i7 8700K) with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetCore_.
@@ -51,63 +53,20 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
* Connect clients with different protocol versions at the same time.
-## MQTTnet Server
-
-_MQTTnet Server_ is a standalone cross platform MQTT server (like mosquitto) basing on this library. It has the following features.
-* Running portable (no installation required)
-* Runs under Windows, Linux, macOS, Raspberry Pi
-* Python scripting support for manipulating messages, validation of clients, building business logic etc.
-* Supports WebSocket and TCP (with and without TLS) connections
-* Provides a HTTP based API (including Swagger endpoint)
-* Extensive configuration parameters and customization supported
-
## Supported frameworks
-* .NET 5.0+
-* .NET Standard 1.3+
-* .NET Core 1.1+
-* .NET Core App 1.1+
-* .NET Framework 4.5.2+
-* Mono 5.2+
-* Universal Windows Platform (UWP) 10.0.10240+ (Windows 10 IoT Core)
-* Xamarin.Android 7.5+
-* Xamarin.iOS 10.14+
-* Blazor WebAssembly 3.2.0+
-
-## Supported platforms
-
-* x86
-* x64
-* AnyCPU
-* ARM
-
-## Supported OS
-
-* Windows
-* Windows 10 IoT Core
-* Linux (Ubuntu, Raspbian etc.)
-* macOS
-* Android
-* iOS
-
-## Supported MQTT versions
-
-* 5.0.0
-* 3.1.1
-* 3.1.0
-
-## Nuget
-
-This library is available as a nuget package:
-
-## Examples
-
-Please find examples and the documentation at the Wiki of this repository ().
-
-## Contributions
-
-If you want to contribute to this project just create a pull request. But only pull requests which are matching the code style of this library will be accepted. Before creating a pull request please have a look at the library to get an overview of the required style.
-Also additions and updates in the Wiki are welcome.
+| Framwork | Version |
+| ------------------ | ----------- |
+|.NET | 5.0+ |
+|.NET Framework | 4.5.2+ |
+|.NET Standard | 1.3+ |
+|.NET Core | 1.1+ |
+|.NET Core App | 1.1+ |
+| Mono | 5.2+ |
+| UWP | 10.0.10240+ |
+| Xamarin.Android | 7.5+ |
+| Xamarin.iOS | 10.14+ |
+| Blazor WebAssembly | 3.2.0+ |
## References
@@ -118,35 +77,20 @@ This library is used in the following projects:
* MQTT Client Rx (Wrapper for Reactive Extensions, )
* MQTT Client Rx (Managed Client Wrapper for Reactive Extensions, )
* MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8))
-* MQTTnet App (Cross platform client application for MQTT debugging, inspection etc., )
+* MQTTnet App (Cross platform client application for MQTT debugging, inspection etc., )
* Wirehome.Core (Open Source Home Automation system for .NET Core, )
* SparkplugNet (Sparkplug library for .Net, )
* Silverback (Framework to build event-driven applications - support for MQTT, Kafka & RabbitMQ)
-Further projects using this project can be found under https://github.com/chkr1011/MQTTnet/network/dependents.
+Further projects using this project can be found under https://github.com/dotnet/MQTTnet/network/dependents.
If you use this library and want to see your project here please create a pull request.
-## License
+## Code of Conduct
-MIT License
+This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
+For more information see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).
-MQTTnet Copyright (c) 2016-2021 Christian Kratky
+## .NET Foundation
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+This project is supported by the [.NET Foundation](https://dotnetfoundation.org).
\ No newline at end of file
diff --git a/Samples/Client/Client_Connection_Samples.cs b/Samples/Client/Client_Connection_Samples.cs
new file mode 100644
index 00000000..97cb4668
--- /dev/null
+++ b/Samples/Client/Client_Connection_Samples.cs
@@ -0,0 +1,271 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Security.Authentication;
+using MQTTnet.Client;
+using MQTTnet.Formatter;
+using MQTTnet.Samples.Helpers;
+
+namespace MQTTnet.Samples.Client;
+
+public static class Client_Connection_Samples
+{
+ public static async Task Connect_Client()
+ {
+ /*
+ * This sample creates a simple MQTT client and connects to a public broker.
+ *
+ * Always dispose the client when it is no longer used.
+ * The default version of MQTT is 3.1.1.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ // Use builder classes where possible in this project.
+ var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
+
+ // This will throw an exception if the server is not available.
+ // The result from this message returns additional data which was sent
+ // from the server. Please refer to the MQTT protocol specification for details.
+ var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ Console.WriteLine("The MQTT client is connected.");
+
+ response.DumpToConsole();
+
+ // Send a clean disconnect to the server by calling _DisconnectAsync_. Without this the TCP connection
+ // gets dropped and the server will handle this as a non clean disconnect (see MQTT spec for details).
+ var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder().Build();
+
+ await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
+ }
+ }
+
+ public static async Task Connect_Client_Timeout()
+ {
+ /*
+ * This sample creates a simple MQTT client and connects to an invalid broker using a timeout.
+ *
+ * This is a modified version of the sample _Connect_Client_! See other sample for more details.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build();
+
+ try
+ {
+ using (var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(1)))
+ {
+ await mqttClient.ConnectAsync(mqttClientOptions, timeoutToken.Token);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ Console.WriteLine("Timeout while connecting.");
+ }
+ }
+ }
+
+ public static async Task Connect_Client_Using_MQTTv5()
+ {
+ /*
+ * This sample creates a simple MQTT client and connects to a public broker using MQTTv5.
+ *
+ * This is a modified version of the sample _Connect_Client_! See other sample for more details.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").WithProtocolVersion(MqttProtocolVersion.V500).Build();
+
+ // In MQTTv5 the response contains much more information.
+ var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ Console.WriteLine("The MQTT client is connected.");
+
+ response.DumpToConsole();
+ }
+ }
+
+ public static async Task Connect_Client_Using_TLS_1_2()
+ {
+ /*
+ * This sample creates a simple MQTT client and connects to a public broker using TLS 1.2 encryption.
+ *
+ * This is a modified version of the sample _Connect_Client_! See other sample for more details.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io")
+ .WithTls(
+ o =>
+ {
+ o.SslProtocol = SslProtocols.Tls12;
+ })
+ .Build();
+
+ await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ Console.WriteLine("The MQTT client is connected.");
+ }
+ }
+
+ public static async Task Connect_Client_Using_WebSockets()
+ {
+ /*
+ * This sample creates a simple MQTT client and connects to a public broker using a WebSocket connection.
+ *
+ * This is a modified version of the sample _Connect_Client_! See other sample for more details.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ var mqttClientOptions = new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt").Build();
+
+ var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ Console.WriteLine("The MQTT client is connected.");
+
+ response.DumpToConsole();
+ }
+ }
+
+ public static async Task Connect_Client_With_TLS_Encryption()
+ {
+ /*
+ * This sample creates a simple MQTT client and connects to a public broker with enabled TLS encryption.
+ *
+ * This is a modified version of the sample _Connect_Client_! See other sample for more details.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
+ .WithTls(
+ o =>
+ {
+ o.SslProtocol = SslProtocols.Tls12; // The default value is determined by the OS. Set manually to force version.
+ })
+ .Build();
+
+ // In MQTTv5 the response contains much more information.
+ var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ Console.WriteLine("The MQTT client is connected.");
+
+ response.DumpToConsole();
+ }
+ }
+
+ public static async Task Ping_Server()
+ {
+ /*
+ * This sample sends a PINGREQ packet to the server and waits for a reply.
+ *
+ * This is only supported in METTv5.0.0+.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
+
+ await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ // This will throw an exception if the server does not reply.
+ await mqttClient.PingAsync(CancellationToken.None);
+
+ Console.WriteLine("The MQTT server replied to the ping request.");
+ }
+ }
+
+ public static async Task Reconnect_Using_Event()
+ {
+ /*
+ * This sample shows how to reconnect when the connection was dropped.
+ * This approach uses one of the events from the client.
+ * This approach has a risk of dead locks! Consider using the timer approach (see sample).
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
+
+ mqttClient.DisconnectedAsync += async e =>
+ {
+ if (e.ClientWasConnected)
+ {
+ // Use the current options as the new options.
+ await mqttClient.ConnectAsync(mqttClient.Options);
+ }
+ };
+
+ await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+ }
+ }
+
+ public static void Reconnect_Using_Timer()
+ {
+ /*
+ * This sample shows how to reconnect when the connection was dropped.
+ * This approach uses a custom Task/Thread which will monitor the connection status.
+ * This is the recommended way but requires more custom code!
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
+
+ _ = Task.Run(
+ async () =>
+ {
+ // User proper cancellation and no while(true).
+ while (true)
+ {
+ try
+ {
+ // This code will also do the very first connect! So no call to _ConnectAsync_ is required
+ // in the first place.
+ if (!mqttClient.IsConnected)
+ {
+ await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ // Subscribe to topics when session is clean etc.
+
+ Console.WriteLine("The MQTT client is connected.");
+ }
+ }
+ catch
+ {
+ // Handle the exception properly (logging etc.).
+ }
+ finally
+ {
+ // Check the connection state every 5 seconds and perform a reconnect if required.
+ await Task.Delay(TimeSpan.FromSeconds(5));
+ }
+ }
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/Samples/Client/Client_Publish_Samples.cs b/Samples/Client/Client_Publish_Samples.cs
new file mode 100644
index 00000000..7bfe72bb
--- /dev/null
+++ b/Samples/Client/Client_Publish_Samples.cs
@@ -0,0 +1,42 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MQTTnet.Client;
+
+namespace MQTTnet.Samples.Client;
+
+public static class Client_Publish_Samples
+{
+ public static async Task Publish_Application_Message()
+ {
+ /*
+ * This sample pushes a simple application message including a topic and a payload.
+ *
+ * Always use builders where they exist. Builders (in this project) are designed to be
+ * backward compatible. Creating an _MqttApplicationMessage_ via its constructor is also
+ * supported but the class might change often in future releases where the builder does not
+ * or at least provides backward compatibility where possible.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ var mqttClientOptions = new MqttClientOptionsBuilder()
+ .WithTcpServer("broker.hivemq.com")
+ .Build();
+
+ await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ var applicationMessage = new MqttApplicationMessageBuilder()
+ .WithTopic("samples/temperature/living_room")
+ .WithPayload("19.5")
+ .Build();
+
+ await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
+
+ Console.WriteLine("MQTT application message is published.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Samples/Client/Client_Subscribe_Samples.cs b/Samples/Client/Client_Subscribe_Samples.cs
new file mode 100644
index 00000000..7cedf056
--- /dev/null
+++ b/Samples/Client/Client_Subscribe_Samples.cs
@@ -0,0 +1,80 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MQTTnet.Client;
+using MQTTnet.Samples.Helpers;
+
+namespace MQTTnet.Samples.Client;
+
+public static class Client_Subscribe_Samples
+{
+ public static async Task Handle_Received_Application_Message()
+ {
+ /*
+ * This sample subscribes to a topic and processes the received message.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ var mqttClientOptions = new MqttClientOptionsBuilder()
+ .WithTcpServer("broker.hivemq.com")
+ .Build();
+
+ // Setup message handling before connecting so that queued messages
+ // are also handled properly. When there is no event handler attached all
+ // received messages get lost.
+ mqttClient.ApplicationMessageReceivedAsync += e =>
+ {
+ Console.WriteLine("Received application message.");
+ e.DumpToConsole();
+
+ return Task.CompletedTask;
+ };
+
+ await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
+ .WithTopicFilter(f => { f.WithTopic("mqttnet/samples/topic/2"); })
+ .Build();
+
+ await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
+
+ Console.WriteLine("MQTT client subscribed to topic.");
+
+ Console.WriteLine("Press enter to exit.");
+ Console.ReadLine();
+ }
+ }
+
+ public static async Task Subscribe_Topic()
+ {
+ /*
+ * This sample subscribes to a topic.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ var mqttClientOptions = new MqttClientOptionsBuilder()
+ .WithTcpServer("broker.hivemq.com")
+ .Build();
+
+ await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
+ .WithTopicFilter(f => { f.WithTopic("mqttnet/samples/topic/1"); })
+ .Build();
+
+ var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
+
+ Console.WriteLine("MQTT client subscribed to topic.");
+
+ // The response contains additional data sent by the server after subscribing.
+ response.DumpToConsole();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Samples/Diagnostics/Logger_Samples.cs b/Samples/Diagnostics/Logger_Samples.cs
new file mode 100644
index 00000000..313d3413
--- /dev/null
+++ b/Samples/Diagnostics/Logger_Samples.cs
@@ -0,0 +1,91 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text;
+using MQTTnet.Diagnostics;
+
+namespace MQTTnet.Samples.Diagnostics;
+
+public static class Logger_Samples
+{
+ public static async Task Create_Custom_Logger()
+ {
+ /*
+ * This sample covers the creation of a custom logger which can be used to forward MQTTnet log messages
+ * to other loggers like Microsoft logger or Serilog or log4net etc.
+ */
+
+ var mqttFactory = new MqttFactory(new MyLogger());
+
+ var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
+ .WithTcpServer("broker.hivemq.com")
+ .Build();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ Console.WriteLine("MQTT client is connected.");
+
+ var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder()
+ .Build();
+
+ await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
+ }
+ }
+
+ public static async Task Use_Event_Logger()
+ {
+ /*
+ * This sample shows how to get logs from the library.
+ *
+ * ATTENTION: Only use the logger for debugging etc. The performance is heavily decreased when a logger is used.
+ */
+
+ // The logger ID is optional but can be set do distinguish different logger instances.
+ var mqttEventLogger = new MqttNetEventLogger("MyCustomLogger");
+
+ mqttEventLogger.LogMessagePublished += (sender, args) =>
+ {
+ var output = new StringBuilder();
+ output.AppendLine($">> [{args.LogMessage.Timestamp:O}] [{args.LogMessage.ThreadId}] [{args.LogMessage.Source}] [{args.LogMessage.Level}]: {args.LogMessage.Message}");
+ if (args.LogMessage.Exception != null)
+ {
+ output.AppendLine(args.LogMessage.Exception.ToString());
+ }
+
+ Console.Write(output);
+ };
+
+ var mqttFactory = new MqttFactory(mqttEventLogger);
+
+ var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
+ .WithTcpServer("broker.hivemq.com")
+ .Build();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ Console.WriteLine("MQTT client is connected.");
+
+ var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder()
+ .Build();
+
+ await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
+ }
+ }
+
+ sealed class MyLogger : IMqttNetLogger
+ {
+ public bool IsEnabled { get; set; } = true;
+
+ public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
+ {
+ // Forward the log message to other loggers.
+ // 1. Convert log level to matching log level in target logger.
+ // 2. Call target logger and pass data accordingly.
+ }
+ }
+}
\ No newline at end of file
diff --git a/Samples/Diagnostics/PackageInspection_Samples.cs b/Samples/Diagnostics/PackageInspection_Samples.cs
new file mode 100644
index 00000000..58c7a546
--- /dev/null
+++ b/Samples/Diagnostics/PackageInspection_Samples.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MQTTnet.Diagnostics;
+
+namespace MQTTnet.Samples.Diagnostics;
+
+public static class PackageInspection_Samples
+{
+ public static async Task Inspect_Outgoing_Package()
+ {
+ /*
+ * This sample covers the inspection of outgoing packages from the client.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
+ .WithTcpServer("broker.hivemq.com")
+ .Build();
+
+ mqttClient.InspectPackage += OnInspectPackage;
+
+ await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ Console.WriteLine("MQTT client is connected.");
+
+ var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder()
+ .Build();
+
+ await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
+ }
+ }
+
+ static Task OnInspectPackage(InspectMqttPacketEventArgs eventArgs)
+ {
+ if (eventArgs.Direction == MqttPacketFlowDirection.Inbound)
+ {
+ Console.WriteLine($"IN: {Convert.ToBase64String(eventArgs.Buffer)}");
+ }
+ else
+ {
+ Console.WriteLine($"OUT: {Convert.ToBase64String(eventArgs.Buffer)}");
+ }
+
+ return Task.CompletedTask;
+ }
+}
\ No newline at end of file
diff --git a/Samples/Helpers/ObjectExtensions.cs b/Samples/Helpers/ObjectExtensions.cs
new file mode 100644
index 00000000..4384fa92
--- /dev/null
+++ b/Samples/Helpers/ObjectExtensions.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json;
+
+namespace MQTTnet.Samples.Helpers;
+
+internal static class ObjectExtensions
+{
+ public static TObject DumpToConsole(this TObject @object)
+ {
+ var output = "NULL";
+ if (@object != null)
+ {
+ output = JsonSerializer.Serialize(@object, new JsonSerializerOptions
+ {
+ WriteIndented = true
+ });
+ }
+
+ Console.WriteLine($"[{@object?.GetType().Name}]:\r\n{output}");
+ return @object;
+ }
+}
\ No newline at end of file
diff --git a/Samples/MQTTnet.Samples.csproj b/Samples/MQTTnet.Samples.csproj
new file mode 100644
index 00000000..f711e001
--- /dev/null
+++ b/Samples/MQTTnet.Samples.csproj
@@ -0,0 +1,26 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Samples/ManagedClient/Managed_Client_Simple_Samples.cs b/Samples/ManagedClient/Managed_Client_Simple_Samples.cs
new file mode 100644
index 00000000..39a6892e
--- /dev/null
+++ b/Samples/ManagedClient/Managed_Client_Simple_Samples.cs
@@ -0,0 +1,44 @@
+using MQTTnet.Client;
+using MQTTnet.Extensions.ManagedClient;
+
+namespace MQTTnet.Samples.ManagedClient;
+
+public sealed class Managed_Client_Simple_Samples
+{
+ public static async Task Connect_Client()
+ {
+ /*
+ * This sample creates a simple managed MQTT client and connects to a public broker.
+ *
+ * The managed client extends the existing _MqttClient_. It adds the following features.
+ * - Reconnecting when connection is lost.
+ * - Storing pending messages in an internal queue so that an enqueue is possible while the client remains not connected.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ using (var managedMqttClient = mqttFactory.CreateManagedMqttClient())
+ {
+ var mqttClientOptions = new MqttClientOptionsBuilder()
+ .WithTcpServer("broker.hivemq.com")
+ .Build();
+
+ var managedMqttClientOptions = new ManagedMqttClientOptionsBuilder()
+ .WithClientOptions(mqttClientOptions)
+ .Build();
+
+ await managedMqttClient.StartAsync(managedMqttClientOptions);
+
+ // The application message is not sent. It is stored in an internal queue and
+ // will be sent when the client is connected.
+ await managedMqttClient.EnqueueAsync("Topic", "Payload");
+
+ Console.WriteLine("The managed MQTT client is connected.");
+
+ // Wait until the queue is fully processed.
+ SpinWait.SpinUntil(() => managedMqttClient.PendingApplicationMessagesCount == 0, 10000);
+
+ Console.WriteLine($"Pending messages = {managedMqttClient.PendingApplicationMessagesCount}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Samples/Program.cs b/Samples/Program.cs
new file mode 100644
index 00000000..fdfe0e56
--- /dev/null
+++ b/Samples/Program.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Reflection;
+
+Console.WriteLine("Welcome to MQTTnet samples!");
+Console.WriteLine();
+
+var sampleClasses = Assembly.GetExecutingAssembly().GetExportedTypes().OrderBy(c => c.Name).ToList();
+
+var index = 0;
+foreach (var sampleClass in sampleClasses)
+{
+ Console.WriteLine($"{index} = {sampleClass.Name}");
+ index++;
+}
+
+Console.Write("Please choose sample class (press Enter to continue): ");
+var input = Console.ReadLine();
+var selectedIndex = int.Parse(input ?? "0");
+var selectedSampleClass = sampleClasses[selectedIndex];
+var sampleMethods = selectedSampleClass.GetMethods(BindingFlags.Static | BindingFlags.Public).OrderBy(m => m.Name).ToList();
+
+index = 0;
+foreach (var sampleMethod in sampleMethods)
+{
+ Console.WriteLine($"{index} = {sampleMethod.Name}");
+ index++;
+}
+
+Console.Write("Please choose sample (press Enter to continue): ");
+input = Console.ReadLine();
+selectedIndex = int.Parse(input ?? "0");
+var selectedSampleMethod = sampleMethods[selectedIndex];
+
+Console.WriteLine("Executing sample...");
+Console.WriteLine();
+
+try
+{
+ var task = selectedSampleMethod.Invoke(null, null) as Task;
+ task?.Wait();
+}
+catch (Exception exception)
+{
+ Console.WriteLine(exception.ToString());
+}
\ No newline at end of file
diff --git a/Samples/RpcClient/RcpClient_Samples.cs b/Samples/RpcClient/RcpClient_Samples.cs
new file mode 100644
index 00000000..21f2b952
--- /dev/null
+++ b/Samples/RpcClient/RcpClient_Samples.cs
@@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MQTTnet.Client;
+using MQTTnet.Extensions.Rpc;
+using MQTTnet.Protocol;
+
+namespace MQTTnet.Samples.RpcClient;
+
+public static class RcpClient_Samples
+{
+ /*
+ * The extension MQTTnet.Extensions.Rpc (available as nuget) allows sending a request and waiting for the matching reply.
+ * This is done via defining a pattern which uses the topic to correlate the request and the response.
+ * From client usage it is possible to define a timeout.
+ */
+
+ public static async Task Send_Request()
+ {
+ var mqttFactory = new MqttFactory();
+
+ // The RPC client is an addon for the existing client. So we need a regular client
+ // which is wrapped later.
+
+ using (var mqttClient = mqttFactory.CreateMqttClient())
+ {
+ var mqttClientOptions = new MqttClientOptionsBuilder()
+ .WithTcpServer("broker.hivemq.com")
+ .Build();
+
+ await mqttClient.ConnectAsync(mqttClientOptions);
+
+ using (var mqttRpcClient = mqttFactory.CreateMqttRpcClient(mqttClient))
+ {
+ // Access to a fully featured application message is not supported for RCP calls!
+ // The method will throw an exception when the response was not received in time.
+ await mqttRpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
+ }
+
+ Console.WriteLine("The RPC call was successful.");
+ }
+ }
+
+ /*
+ * The device must respond to the request using the correct topic. The following C code shows how a
+ * smart device like an ESP8266 must respond to the above sample.
+ *
+ // If using the MQTT client PubSubClient it must be ensured
+ // that the request topic for each method is subscribed like the following.
+ mqttClient.subscribe("MQTTnet.RPC/+/ping");
+ mqttClient.subscribe("MQTTnet.RPC/+/do_something");
+
+ // It is not allowed to change the structure of the topic.
+ // Otherwise RPC will not work.
+ // So method names can be separated using an _ or . but no +, # or /.
+ // If it is required to distinguish between devices
+ // own rules can be defined like the following:
+ mqttClient.subscribe("MQTTnet.RPC/+/deviceA.ping");
+ mqttClient.subscribe("MQTTnet.RPC/+/deviceB.ping");
+ mqttClient.subscribe("MQTTnet.RPC/+/deviceC.getTemperature");
+
+ // Within the callback of the MQTT client the topic must be checked
+ // if it belongs to MQTTnet RPC. The following code shows one
+ // possible way of doing this.
+ void mqtt_Callback(char *topic, byte *payload, unsigned int payloadLength)
+ {
+ String topicString = String(topic);
+
+ if (topicString.startsWith("MQTTnet.RPC/")) {
+ String responseTopic = topicString + String("/response");
+
+ if (topicString.endsWith("/deviceA.ping")) {
+ mqtt_publish(responseTopic, "pong", false);
+ return;
+ }
+ }
+ }
+
+ // Important notes:
+ // ! Do not send response message with the _retain_ flag set to true.
+ // ! All required data for a RPC call and the result must be placed into the payload.
+ */
+}
\ No newline at end of file
diff --git a/Samples/Server/Server_Simple_Samples.cs b/Samples/Server/Server_Simple_Samples.cs
new file mode 100644
index 00000000..4017c5aa
--- /dev/null
+++ b/Samples/Server/Server_Simple_Samples.cs
@@ -0,0 +1,91 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MQTTnet.Protocol;
+using MQTTnet.Server;
+
+namespace MQTTnet.Samples.Server;
+
+public static class Server_Simple_Samples
+{
+ public static async Task Run_Minimal_Server()
+ {
+ /*
+ * This sample starts a simple MQTT server which will accept any TCP connection.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ // The port for the default endpoint is 1883.
+ // The default endpoint is NOT encrypted!
+ // Use the builder classes where possible.
+ var mqttServerOptions = new MqttServerOptionsBuilder()
+ .WithDefaultEndpoint()
+ .Build();
+
+ // The port can be changed using the following API (not used in this example).
+ new MqttServerOptionsBuilder()
+ .WithDefaultEndpoint()
+ .WithDefaultEndpointPort(1234)
+ .Build();
+
+ using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
+ {
+ await mqttServer.StartAsync();
+
+ Console.WriteLine("Press Enter to exit.");
+ Console.ReadLine();
+
+ // Stop and dispose the MQTT server if it is no longer needed!
+ await mqttServer.StopAsync();
+ }
+ }
+
+ public static async Task Validating_Connections()
+ {
+ /*
+ * This sample starts a simple MQTT server which will check for valid credentials and client ID.
+ *
+ * See _Run_Minimal_Server_ for more information.
+ */
+
+ var mqttFactory = new MqttFactory();
+
+ var mqttServerOptions = new MqttServerOptionsBuilder()
+ .WithDefaultEndpoint()
+ .Build();
+
+ using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
+ {
+ // Setup connection validation before starting the server so that there is
+ // no change to connect without valid credentials.
+ mqttServer.ValidatingConnectionAsync += e =>
+ {
+ if (e.ClientId != "ValidClientId")
+ {
+ e.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid;
+ }
+
+ if (e.Username != "ValidUser")
+ {
+ e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
+ }
+
+ if (e.Password != "SecretPassword")
+ {
+ e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
+ }
+
+ return Task.CompletedTask;
+ };
+
+ await mqttServer.StartAsync();
+
+ Console.WriteLine("Press Enter to exit.");
+ Console.ReadLine();
+
+ await mqttServer.StopAsync();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj b/Source/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj
new file mode 100644
index 00000000..830c7c04
--- /dev/null
+++ b/Source/MQTTnet.AspNetCore.Tests/MQTTnet.AspNetCore.Tests.csproj
@@ -0,0 +1,35 @@
+
+
+
+ netcoreapp3.1
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/MQTTnet.AspNetCore.Tests/Mockups/ConnectionContextMockup.cs b/Source/MQTTnet.AspNetCore.Tests/Mockups/ConnectionContextMockup.cs
similarity index 71%
rename from Tests/MQTTnet.AspNetCore.Tests/Mockups/ConnectionContextMockup.cs
rename to Source/MQTTnet.AspNetCore.Tests/Mockups/ConnectionContextMockup.cs
index 296959ef..0ec946de 100644
--- a/Tests/MQTTnet.AspNetCore.Tests/Mockups/ConnectionContextMockup.cs
+++ b/Source/MQTTnet.AspNetCore.Tests/Mockups/ConnectionContextMockup.cs
@@ -1,4 +1,8 @@
-using System.Collections.Generic;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
using System.IO.Pipelines;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features;
diff --git a/Tests/MQTTnet.AspNetCore.Tests/Mockups/ConnectionHandlerMockup.cs b/Source/MQTTnet.AspNetCore.Tests/Mockups/ConnectionHandlerMockup.cs
similarity index 70%
rename from Tests/MQTTnet.AspNetCore.Tests/Mockups/ConnectionHandlerMockup.cs
rename to Source/MQTTnet.AspNetCore.Tests/Mockups/ConnectionHandlerMockup.cs
index 9adcebd2..5f372c99 100644
--- a/Tests/MQTTnet.AspNetCore.Tests/Mockups/ConnectionHandlerMockup.cs
+++ b/Source/MQTTnet.AspNetCore.Tests/Mockups/ConnectionHandlerMockup.cs
@@ -1,9 +1,14 @@
-using Microsoft.AspNetCore.Connections;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.AspNetCore.Connections;
using MQTTnet.Adapter;
using MQTTnet.Formatter;
using MQTTnet.Server;
using System;
using System.Threading.Tasks;
+using MQTTnet.Diagnostics;
namespace MQTTnet.AspNetCore.Tests.Mockups
{
@@ -12,16 +17,11 @@ namespace MQTTnet.AspNetCore.Tests.Mockups
public TaskCompletionSource Context { get; } = new TaskCompletionSource();
public Func ClientHandler { get; set; }
- public ConnectionHandlerMockup()
- {
- }
-
public async Task OnConnectedAsync(ConnectionContext connection)
{
try
{
- var writer = new SpanBasedMqttPacketWriter();
- var formatter = new MqttPacketFormatterAdapter(writer);
+ var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
var context = new MqttConnectionContext(formatter, connection);
Context.TrySetResult(context);
@@ -33,7 +33,7 @@ namespace MQTTnet.AspNetCore.Tests.Mockups
}
}
- public Task StartAsync(IMqttServerOptions options)
+ public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
{
return Task.CompletedTask;
}
diff --git a/Tests/MQTTnet.AspNetCore.Tests/Mockups/DuplexPipeMockup.cs b/Source/MQTTnet.AspNetCore.Tests/Mockups/DuplexPipeMockup.cs
similarity index 70%
rename from Tests/MQTTnet.AspNetCore.Tests/Mockups/DuplexPipeMockup.cs
rename to Source/MQTTnet.AspNetCore.Tests/Mockups/DuplexPipeMockup.cs
index 306749b9..24f85529 100644
--- a/Tests/MQTTnet.AspNetCore.Tests/Mockups/DuplexPipeMockup.cs
+++ b/Source/MQTTnet.AspNetCore.Tests/Mockups/DuplexPipeMockup.cs
@@ -1,4 +1,8 @@
-using System.IO.Pipelines;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO.Pipelines;
namespace MQTTnet.AspNetCore.Tests.Mockups
{
diff --git a/Tests/MQTTnet.AspNetCore.Tests/Mockups/LimitedMemoryPool.cs b/Source/MQTTnet.AspNetCore.Tests/Mockups/LimitedMemoryPool.cs
similarity index 62%
rename from Tests/MQTTnet.AspNetCore.Tests/Mockups/LimitedMemoryPool.cs
rename to Source/MQTTnet.AspNetCore.Tests/Mockups/LimitedMemoryPool.cs
index ac5c23cf..c2f40502 100644
--- a/Tests/MQTTnet.AspNetCore.Tests/Mockups/LimitedMemoryPool.cs
+++ b/Source/MQTTnet.AspNetCore.Tests/Mockups/LimitedMemoryPool.cs
@@ -1,4 +1,8 @@
-using System.Buffers;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
namespace MQTTnet.AspNetCore.Tests.Mockups
{
diff --git a/Tests/MQTTnet.AspNetCore.Tests/Mockups/MemoryOwner.cs b/Source/MQTTnet.AspNetCore.Tests/Mockups/MemoryOwner.cs
similarity index 74%
rename from Tests/MQTTnet.AspNetCore.Tests/Mockups/MemoryOwner.cs
rename to Source/MQTTnet.AspNetCore.Tests/Mockups/MemoryOwner.cs
index 1b7b02f3..55e4e318 100644
--- a/Tests/MQTTnet.AspNetCore.Tests/Mockups/MemoryOwner.cs
+++ b/Source/MQTTnet.AspNetCore.Tests/Mockups/MemoryOwner.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Buffers;
namespace MQTTnet.AspNetCore.Tests.Mockups
diff --git a/Tests/MQTTnet.AspNetCore.Tests/MqttConnectionContextTest.cs b/Source/MQTTnet.AspNetCore.Tests/MqttConnectionContextTest.cs
similarity index 91%
rename from Tests/MQTTnet.AspNetCore.Tests/MqttConnectionContextTest.cs
rename to Source/MQTTnet.AspNetCore.Tests/MqttConnectionContextTest.cs
index 710f2263..190ff485 100644
--- a/Tests/MQTTnet.AspNetCore.Tests/MqttConnectionContextTest.cs
+++ b/Source/MQTTnet.AspNetCore.Tests/MqttConnectionContextTest.cs
@@ -1,21 +1,23 @@
-using Microsoft.AspNetCore.Builder;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
-using System.Linq;
+using System.Net;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Tests.Mockups;
-using MQTTnet.Client.Options;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Packets;
-using System.Net;
-using MQTTnet.AspNetCore.Extensions;
+using MQTTnet.Client;
using MQTTnet.Protocol;
using MQTTnet.Tests.Extensions;
@@ -27,7 +29,7 @@ namespace MQTTnet.AspNetCore.Tests
[TestMethod]
public async Task TestReceivePacketAsyncThrowsWhenReaderCompleted()
{
- var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311);
+ var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
var pipe = new DuplexPipeMockup();
var connection = new DefaultConnectionContext();
connection.Transport = pipe;
@@ -41,7 +43,7 @@ namespace MQTTnet.AspNetCore.Tests
[TestMethod]
public async Task TestCorruptedConnectPacket()
{
- var writer = new MqttPacketWriter();
+ var writer = new MqttBufferWriter(4096, 65535);
var serializer = new MqttPacketFormatterAdapter(writer);
var pipe = new DuplexPipeMockup();
var connection = new DefaultConnectionContext();
@@ -80,7 +82,7 @@ namespace MQTTnet.AspNetCore.Tests
[TestMethod]
public async Task TestLargePacket()
{
- var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311);
+ var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
var pipe = new DuplexPipeMockup();
var connection = new DefaultConnectionContext();
connection.Transport = pipe;
@@ -114,6 +116,7 @@ namespace MQTTnet.AspNetCore.Tests
services.AddSingleton(mockup);
})
.Build())
+
using (var client = new MqttFactory().CreateMqttClient())
{
host.Start();
diff --git a/Tests/MQTTnet.AspNetCore.Tests/ReaderExtensionsTest.cs b/Source/MQTTnet.AspNetCore.Tests/ReaderExtensionsTest.cs
similarity index 60%
rename from Tests/MQTTnet.AspNetCore.Tests/ReaderExtensionsTest.cs
rename to Source/MQTTnet.AspNetCore.Tests/ReaderExtensionsTest.cs
index a98e98ed..056a6abd 100644
--- a/Tests/MQTTnet.AspNetCore.Tests/ReaderExtensionsTest.cs
+++ b/Source/MQTTnet.AspNetCore.Tests/ReaderExtensionsTest.cs
@@ -1,7 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
#if NETCOREAPP3_1
using System.Buffers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using MQTTnet.AspNetCore.Extensions;
+using MQTTnet.AspNetCore;
using MQTTnet.Formatter;
using MQTTnet.Packets;
@@ -13,35 +17,33 @@ namespace MQTTnet.AspNetCore.Tests
[TestMethod]
public void TestTryDeserialize()
{
- var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311);
+ var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
- var buffer = serializer.Encode(new MqttPublishPacket() {Topic = "a", Payload = new byte[5]});
+ var buffer = serializer.Encode(new MqttPublishPacket {Topic = "a", Payload = new byte[5]}).Join();
var sequence = new ReadOnlySequence(buffer.Array, buffer.Offset, buffer.Count);
var part = sequence;
- MqttBasePacket packet;
+ MqttPacket packet;
var consumed = part.Start;
var observed = part.Start;
var result = false;
var read = 0;
-
- var reader = new SpanBasedMqttPacketBodyReader();
-
+
part = sequence.Slice(sequence.Start, 0); // empty message should fail
- result = serializer.TryDecode(reader, part, out packet, out consumed, out observed, out read);
+ result = serializer.TryDecode(part, out packet, out consumed, out observed, out read);
Assert.IsFalse(result);
part = sequence.Slice(sequence.Start, 1); // partial fixed header should fail
- result = serializer.TryDecode(reader, part, out packet, out consumed, out observed, out read);
+ result = serializer.TryDecode(part, out packet, out consumed, out observed, out read);
Assert.IsFalse(result);
part = sequence.Slice(sequence.Start, 4); // partial body should fail
- result = serializer.TryDecode(reader, part, out packet, out consumed, out observed, out read);
+ result = serializer.TryDecode(part, out packet, out consumed, out observed, out read);
Assert.IsFalse(result);
part = sequence; // complete msg should work
- result = serializer.TryDecode(reader, part, out packet, out consumed, out observed, out read);
+ result = serializer.TryDecode(part, out packet, out consumed, out observed, out read);
Assert.IsTrue(result);
}
}
diff --git a/Source/MQTTnet.AspTestApp/MQTTnet.AspTestApp.csproj b/Source/MQTTnet.AspTestApp/MQTTnet.AspTestApp.csproj
new file mode 100644
index 00000000..973f5d7d
--- /dev/null
+++ b/Source/MQTTnet.AspTestApp/MQTTnet.AspTestApp.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net6.0
+ enable
+ enable
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/MQTTnet.AspTestApp/Pages/Index.cshtml b/Source/MQTTnet.AspTestApp/Pages/Index.cshtml
new file mode 100644
index 00000000..88d6dbe3
--- /dev/null
+++ b/Source/MQTTnet.AspTestApp/Pages/Index.cshtml
@@ -0,0 +1,34 @@
+@page
+@model IndexModel
+@{
+ ViewData["Title"] = "Home page";
+}
+
+
+
MQTTnet ASP.NET Core Test App
+
+
+
+
+
diff --git a/Source/MQTTnet.AspTestApp/Pages/Index.cshtml.cs b/Source/MQTTnet.AspTestApp/Pages/Index.cshtml.cs
new file mode 100644
index 00000000..558db0f5
--- /dev/null
+++ b/Source/MQTTnet.AspTestApp/Pages/Index.cshtml.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace MQTTnet.AspTestApp.Pages
+{
+ public class IndexModel : PageModel
+ {
+ private readonly ILogger _logger;
+
+ public IndexModel(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public void OnGet()
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspTestApp/Pages/Shared/_Layout.cshtml b/Source/MQTTnet.AspTestApp/Pages/Shared/_Layout.cshtml
new file mode 100644
index 00000000..45469844
--- /dev/null
+++ b/Source/MQTTnet.AspTestApp/Pages/Shared/_Layout.cshtml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ @ViewData["Title"] - MQTTnet
+
+
+
+ @RenderBody()
+
+ @await RenderSectionAsync("Scripts", required: false)
+
+
\ No newline at end of file
diff --git a/Source/MQTTnet.AspTestApp/Pages/_ViewImports.cshtml b/Source/MQTTnet.AspTestApp/Pages/_ViewImports.cshtml
new file mode 100644
index 00000000..c8fbf311
--- /dev/null
+++ b/Source/MQTTnet.AspTestApp/Pages/_ViewImports.cshtml
@@ -0,0 +1,2 @@
+@namespace MQTTnet.AspTestApp.Pages
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
diff --git a/Source/MQTTnet.AspTestApp/Pages/_ViewStart.cshtml b/Source/MQTTnet.AspTestApp/Pages/_ViewStart.cshtml
new file mode 100644
index 00000000..a5f10045
--- /dev/null
+++ b/Source/MQTTnet.AspTestApp/Pages/_ViewStart.cshtml
@@ -0,0 +1,3 @@
+@{
+ Layout = "_Layout";
+}
diff --git a/Source/MQTTnet.AspTestApp/Program.cs b/Source/MQTTnet.AspTestApp/Program.cs
new file mode 100644
index 00000000..18a9ef31
--- /dev/null
+++ b/Source/MQTTnet.AspTestApp/Program.cs
@@ -0,0 +1,73 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MQTTnet;
+using MQTTnet.AspNetCore;
+using MQTTnet.Server;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddRazorPages();
+
+// Setup MQTT stuff.
+builder.Services.AddMqttServer();
+builder.Services.AddConnections();
+
+var app = builder.Build();
+
+if (!app.Environment.IsDevelopment())
+{
+ app.UseExceptionHandler("/Error");
+}
+
+app.UseStaticFiles();
+
+app.UseRouting();
+
+app.UseAuthorization();
+
+app.MapRazorPages();
+
+// Setup MQTT stuff.
+app.UseEndpoints(endpoints =>
+{
+ endpoints.MapMqtt("/mqtt");
+});
+
+app.UseMqttServer(server =>
+{
+ server.StartedAsync += args =>
+ {
+ _ = Task.Run(async () =>
+ {
+ var mqttApplicationMessage = new MqttApplicationMessageBuilder()
+ .WithPayload($"Test application message from MQTTnet server.")
+ .WithTopic("message")
+ .Build();
+
+ while (true)
+ {
+ try
+ {
+ await server.InjectApplicationMessage(new InjectedMqttApplicationMessage(mqttApplicationMessage)
+ {
+ SenderClientId = "server"
+ });
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ }
+ finally
+ {
+ await Task.Delay(TimeSpan.FromSeconds(5));
+ }
+ }
+ });
+
+ return Task.CompletedTask;
+ };
+});
+
+app.Run();
diff --git a/Source/MQTTnet.AspTestApp/appsettings.Development.json b/Source/MQTTnet.AspTestApp/appsettings.Development.json
new file mode 100644
index 00000000..770d3e93
--- /dev/null
+++ b/Source/MQTTnet.AspTestApp/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "DetailedErrors": true,
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/Source/MQTTnet.AspTestApp/appsettings.json b/Source/MQTTnet.AspTestApp/appsettings.json
new file mode 100644
index 00000000..10f68b8c
--- /dev/null
+++ b/Source/MQTTnet.AspTestApp/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/Source/MQTTnet.AspTestApp/libman.json b/Source/MQTTnet.AspTestApp/libman.json
new file mode 100644
index 00000000..ceee2710
--- /dev/null
+++ b/Source/MQTTnet.AspTestApp/libman.json
@@ -0,0 +1,5 @@
+{
+ "version": "1.0",
+ "defaultProvider": "cdnjs",
+ "libraries": []
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/Extensions/ApplicationBuilderExtensions.cs b/Source/MQTTnet.AspnetCore/ApplicationBuilderExtensions.cs
similarity index 84%
rename from Source/MQTTnet.AspnetCore/Extensions/ApplicationBuilderExtensions.cs
rename to Source/MQTTnet.AspnetCore/ApplicationBuilderExtensions.cs
index d9e7ed81..d7bc3e3d 100644
--- a/Source/MQTTnet.AspnetCore/Extensions/ApplicationBuilderExtensions.cs
+++ b/Source/MQTTnet.AspnetCore/ApplicationBuilderExtensions.cs
@@ -1,9 +1,13 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using MQTTnet.Server;
-namespace MQTTnet.AspNetCore.Extensions
+namespace MQTTnet.AspNetCore
{
public static class ApplicationBuilderExtensions
{
@@ -36,9 +40,9 @@ namespace MQTTnet.AspNetCore.Extensions
return app;
}
- public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action configure)
+ public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action configure)
{
- var server = app.ApplicationServices.GetRequiredService();
+ var server = app.ApplicationServices.GetRequiredService();
configure(server);
diff --git a/Source/MQTTnet.AspnetCore/AspNetMqttServerOptionsBuilder.cs b/Source/MQTTnet.AspnetCore/AspNetMqttServerOptionsBuilder.cs
index e0ae3330..a95e0017 100644
--- a/Source/MQTTnet.AspnetCore/AspNetMqttServerOptionsBuilder.cs
+++ b/Source/MQTTnet.AspnetCore/AspNetMqttServerOptionsBuilder.cs
@@ -1,13 +1,17 @@
-using MQTTnet.Server;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MQTTnet.Server;
using System;
namespace MQTTnet.AspNetCore
{
- public class AspNetMqttServerOptionsBuilder : MqttServerOptionsBuilder
+ public sealed class AspNetMqttServerOptionsBuilder : MqttServerOptionsBuilder
{
public AspNetMqttServerOptionsBuilder(IServiceProvider serviceProvider)
{
- ServiceProvider = serviceProvider;
+ ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
public IServiceProvider ServiceProvider { get; }
diff --git a/Source/MQTTnet.AspnetCore/Client/MqttClientConnectionContextFactory.cs b/Source/MQTTnet.AspnetCore/Client/MqttClientConnectionContextFactory.cs
index 93581335..f8e38f4e 100644
--- a/Source/MQTTnet.AspnetCore/Client/MqttClientConnectionContextFactory.cs
+++ b/Source/MQTTnet.AspnetCore/Client/MqttClientConnectionContextFactory.cs
@@ -1,15 +1,20 @@
-using MQTTnet.Adapter;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
-using MQTTnet.Client.Options;
using MQTTnet.Formatter;
using System;
using System.Net;
+using MQTTnet.Client;
+using MQTTnet.Diagnostics;
namespace MQTTnet.AspNetCore.Client
{
- public class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
+ public sealed class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
{
- public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options)
+ public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger)
{
if (options == null) throw new ArgumentNullException(nameof(options));
@@ -19,9 +24,8 @@ namespace MQTTnet.AspNetCore.Client
{
var endpoint = new DnsEndPoint(tcpOptions.Server, tcpOptions.GetPort());
var tcpConnection = new TcpConnection(endpoint);
-
- var writer = new SpanBasedMqttPacketWriter();
- var formatter = new MqttPacketFormatterAdapter(options.ProtocolVersion, writer);
+
+ var formatter = new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(4096, 65535));
return new MqttConnectionContext(formatter, tcpConnection);
}
default:
diff --git a/Source/MQTTnet.AspnetCore/Client/Tcp/BufferExtensions.cs b/Source/MQTTnet.AspnetCore/Client/Tcp/BufferExtensions.cs
index 5911a3a1..62736592 100644
--- a/Source/MQTTnet.AspnetCore/Client/Tcp/BufferExtensions.cs
+++ b/Source/MQTTnet.AspnetCore/Client/Tcp/BufferExtensions.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Runtime.InteropServices;
namespace MQTTnet.AspNetCore.Client.Tcp
diff --git a/Source/MQTTnet.AspnetCore/Client/Tcp/DuplexPipe.cs b/Source/MQTTnet.AspnetCore/Client/Tcp/DuplexPipe.cs
index e234da55..84bbf990 100644
--- a/Source/MQTTnet.AspnetCore/Client/Tcp/DuplexPipe.cs
+++ b/Source/MQTTnet.AspnetCore/Client/Tcp/DuplexPipe.cs
@@ -1,4 +1,8 @@
-using System.IO.Pipelines;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO.Pipelines;
namespace MQTTnet.AspNetCore.Client.Tcp
{
diff --git a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketAwaitable.cs b/Source/MQTTnet.AspnetCore/Client/Tcp/SocketAwaitable.cs
index dbc26120..6a53a7a1 100644
--- a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketAwaitable.cs
+++ b/Source/MQTTnet.AspnetCore/Client/Tcp/SocketAwaitable.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Net.Sockets;
diff --git a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketReceiver.cs b/Source/MQTTnet.AspnetCore/Client/Tcp/SocketReceiver.cs
index 7d11fa2b..a10708ec 100644
--- a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketReceiver.cs
+++ b/Source/MQTTnet.AspnetCore/Client/Tcp/SocketReceiver.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.IO.Pipelines;
using System.Net.Sockets;
diff --git a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketSender.cs b/Source/MQTTnet.AspnetCore/Client/Tcp/SocketSender.cs
index f5bb0b4c..d4a5dcc7 100644
--- a/Source/MQTTnet.AspnetCore/Client/Tcp/SocketSender.cs
+++ b/Source/MQTTnet.AspnetCore/Client/Tcp/SocketSender.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
diff --git a/Source/MQTTnet.AspnetCore/Client/Tcp/TcpConnection.cs b/Source/MQTTnet.AspnetCore/Client/Tcp/TcpConnection.cs
index 7814567b..845a67de 100644
--- a/Source/MQTTnet.AspnetCore/Client/Tcp/TcpConnection.cs
+++ b/Source/MQTTnet.AspnetCore/Client/Tcp/TcpConnection.cs
@@ -1,4 +1,8 @@
-using Microsoft.AspNetCore.Connections;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features;
using MQTTnet.Exceptions;
using System;
@@ -40,7 +44,7 @@ namespace MQTTnet.AspNetCore.Client.Tcp
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
}
-#if NETCOREAPP3_1 || NET5_0
+#if NETCOREAPP3_1 || NET5_0_OR_GREATER
public override ValueTask DisposeAsync()
#else
public Task DisposeAsync()
@@ -53,7 +57,7 @@ namespace MQTTnet.AspNetCore.Client.Tcp
_socket?.Dispose();
-#if NETCOREAPP3_1 || NET5_0
+#if NETCOREAPP3_1 || NET5_0_OR_GREATER
return base.DisposeAsync();
}
diff --git a/Source/MQTTnet.AspnetCore/ConnectionBuilderExtensions.cs b/Source/MQTTnet.AspnetCore/ConnectionBuilderExtensions.cs
new file mode 100644
index 00000000..9ea8922e
--- /dev/null
+++ b/Source/MQTTnet.AspnetCore/ConnectionBuilderExtensions.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.AspNetCore.Connections;
+
+namespace MQTTnet.AspNetCore
+{
+ public static class ConnectionBuilderExtensions
+ {
+ public static IConnectionBuilder UseMqtt(this IConnectionBuilder builder)
+ {
+ return builder.UseConnectionHandler();
+ }
+ }
+}
diff --git a/Source/MQTTnet.AspnetCore/Extensions/ConnectionRouteBuilderExtensions.cs b/Source/MQTTnet.AspnetCore/ConnectionRouteBuilderExtensions.cs
similarity index 73%
rename from Source/MQTTnet.AspnetCore/Extensions/ConnectionRouteBuilderExtensions.cs
rename to Source/MQTTnet.AspnetCore/ConnectionRouteBuilderExtensions.cs
index 0595b981..cbbe9f3b 100644
--- a/Source/MQTTnet.AspnetCore/Extensions/ConnectionRouteBuilderExtensions.cs
+++ b/Source/MQTTnet.AspnetCore/ConnectionRouteBuilderExtensions.cs
@@ -1,11 +1,15 @@
-using Microsoft.AspNetCore.Http;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Connections;
#if NETCOREAPP3_1
using System;
#endif
-namespace MQTTnet.AspNetCore.Extensions
+namespace MQTTnet.AspNetCore
{
public static class ConnectionRouteBuilderExtensions
{
diff --git a/Source/MQTTnet.AspnetCore/Extensions/EndpointRouterExtensions.cs b/Source/MQTTnet.AspnetCore/EndpointRouterExtensions.cs
similarity index 67%
rename from Source/MQTTnet.AspnetCore/Extensions/EndpointRouterExtensions.cs
rename to Source/MQTTnet.AspnetCore/EndpointRouterExtensions.cs
index 7cbed8c8..f5c72419 100644
--- a/Source/MQTTnet.AspnetCore/Extensions/EndpointRouterExtensions.cs
+++ b/Source/MQTTnet.AspnetCore/EndpointRouterExtensions.cs
@@ -1,5 +1,9 @@
-
-#if NETCOREAPP3_1 || NET5_0
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+#if NETCOREAPP3_1 || NET5_0_OR_GREATER
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
diff --git a/Source/MQTTnet.AspnetCore/Extensions/ConnectionBuilderExtensions.cs b/Source/MQTTnet.AspnetCore/Extensions/ConnectionBuilderExtensions.cs
deleted file mode 100644
index f62f8704..00000000
--- a/Source/MQTTnet.AspnetCore/Extensions/ConnectionBuilderExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Microsoft.AspNetCore.Connections;
-
-namespace MQTTnet.AspNetCore.Extensions
-{
- public static class ConnectionBuilderExtensions
- {
- public static IConnectionBuilder UseMqtt(this IConnectionBuilder builder)
- {
- return builder.UseConnectionHandler();
- }
- }
-}
diff --git a/Source/MQTTnet.AspnetCore/MQTTnet.AspNetCore.csproj b/Source/MQTTnet.AspnetCore/MQTTnet.AspNetCore.csproj
index f2a891d5..94ccb9fa 100644
--- a/Source/MQTTnet.AspnetCore/MQTTnet.AspNetCore.csproj
+++ b/Source/MQTTnet.AspnetCore/MQTTnet.AspNetCore.csproj
@@ -1,36 +1,71 @@
-
- netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0
-
-
-
-
- 7.2
- true
- true
- true
- snupkg
-
+
+ netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0;net6.0
-
-
-
+ MQTTnet.AspNetCore
+ MQTTnet.AspNetCore
+ True
+ The contributors of MQTTnet
+ MQTTnet
+ MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.
+ The contributors of MQTTnet
+ MQTTnet.AspNetCore
+ false
+ false
+ true
+ true
+ snupkg
+ Christian Kratky 2016-2022
+ https://github.com/dotnet/MQTTnet
+ https://github.com/dotnet/MQTTnet.git
+ git
+ 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 Blazor
+ en-US
+ false
+ false
+ nuget.png
+ true
+ true
+ LICENSE
+ For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).
+ true
+
+ true
+
-
- RELEASE;NETSTANDARD2_0
-
+
+
+ True
+ \
+
+
-
-
-
-
-
-
-
+
+
+ True
+ \
+
+
-
-
-
+
+
+
+
+
+ RELEASE;NETSTANDARD2_0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/MQTTnet.AspnetCore/MqttConnectionContext.cs b/Source/MQTTnet.AspnetCore/MqttConnectionContext.cs
index e8a62e4e..1268d25e 100644
--- a/Source/MQTTnet.AspnetCore/MqttConnectionContext.cs
+++ b/Source/MQTTnet.AspnetCore/MqttConnectionContext.cs
@@ -1,4 +1,8 @@
-using Microsoft.AspNetCore.Connections;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Connections.Features;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
@@ -10,7 +14,6 @@ using System.IO.Pipelines;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
-using MQTTnet.AspNetCore.Extensions;
using MQTTnet.Internal;
namespace MQTTnet.AspNetCore
@@ -18,7 +21,6 @@ namespace MQTTnet.AspNetCore
public sealed class MqttConnectionContext : IMqttChannelAdapter
{
readonly AsyncLock _writerLock = new AsyncLock();
- readonly SpanBasedMqttPacketBodyReader _reader;
PipeReader _input;
PipeWriter _output;
@@ -33,8 +35,6 @@ namespace MQTTnet.AspNetCore
_input = Connection.Transport.Input;
_output = Connection.Transport.Output;
}
-
- _reader = new SpanBasedMqttPacketBodyReader();
}
public string Endpoint
@@ -73,7 +73,7 @@ namespace MQTTnet.AspNetCore
IHttpContextFeature Http => Connection.Features.Get();
- public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
+ public async Task ConnectAsync(CancellationToken cancellationToken)
{
if (Connection is TcpConnection tcp && !tcp.IsConnected)
{
@@ -84,7 +84,7 @@ namespace MQTTnet.AspNetCore
_output = Connection.Transport.Output;
}
- public Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
+ public Task DisconnectAsync(CancellationToken cancellationToken)
{
_input?.Complete();
_output?.Complete();
@@ -92,7 +92,7 @@ namespace MQTTnet.AspNetCore
return Task.CompletedTask;
}
- public async Task ReceivePacketAsync(CancellationToken cancellationToken)
+ public async Task ReceivePacketAsync(CancellationToken cancellationToken)
{
var input = Connection.Transport.Input;
@@ -120,7 +120,7 @@ namespace MQTTnet.AspNetCore
{
if (!buffer.IsEmpty)
{
- if (PacketFormatterAdapter.TryDecode(_reader, buffer, out var packet, out consumed, out observed, out var received))
+ if (PacketFormatterAdapter.TryDecode(buffer, out var packet, out consumed, out observed, out var received))
{
BytesReceived += received;
return packet;
@@ -167,13 +167,14 @@ namespace MQTTnet.AspNetCore
BytesSent = 0;
}
- public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
+ public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
{
var formatter = PacketFormatterAdapter;
+
using (await _writerLock.WaitAsync(cancellationToken).ConfigureAwait(false))
{
var buffer = formatter.Encode(packet);
- var msg = buffer.AsMemory();
+ var msg = buffer.Join().AsMemory();
var output = _output;
var result = await output.WriteAsync(msg, cancellationToken).ConfigureAwait(false);
if (result.IsCompleted)
@@ -181,7 +182,7 @@ namespace MQTTnet.AspNetCore
BytesSent += msg.Length;
}
- PacketFormatterAdapter.FreeBuffer();
+ PacketFormatterAdapter.Cleanup();
}
}
diff --git a/Source/MQTTnet.AspnetCore/MqttConnectionHandler.cs b/Source/MQTTnet.AspnetCore/MqttConnectionHandler.cs
index 8cc1022d..b86de301 100644
--- a/Source/MQTTnet.AspnetCore/MqttConnectionHandler.cs
+++ b/Source/MQTTnet.AspnetCore/MqttConnectionHandler.cs
@@ -1,15 +1,22 @@
-using Microsoft.AspNetCore.Connections;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using MQTTnet.Adapter;
using MQTTnet.Server;
using System;
using System.Threading.Tasks;
+using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
namespace MQTTnet.AspNetCore
{
- public class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
+ public sealed class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
{
+ MqttServerOptions _serverOptions;
+
public Func ClientHandler { get; set; }
public override async Task OnConnectedAsync(ConnectionContext connection)
@@ -20,9 +27,8 @@ namespace MQTTnet.AspNetCore
{
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
}
-
- var writer = new SpanBasedMqttPacketWriter();
- var formatter = new MqttPacketFormatterAdapter(writer);
+
+ var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax));
using (var adapter = new MqttConnectionContext(formatter, connection))
{
var clientHandler = ClientHandler;
@@ -33,8 +39,10 @@ namespace MQTTnet.AspNetCore
}
}
- public Task StartAsync(IMqttServerOptions options)
+ public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
{
+ _serverOptions = options;
+
return Task.CompletedTask;
}
diff --git a/Source/MQTTnet.AspnetCore/MqttHostedServer.cs b/Source/MQTTnet.AspnetCore/MqttHostedServer.cs
index 3a9c882d..81d0eee7 100644
--- a/Source/MQTTnet.AspnetCore/MqttHostedServer.cs
+++ b/Source/MQTTnet.AspnetCore/MqttHostedServer.cs
@@ -1,27 +1,27 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using MQTTnet.Adapter;
-using MQTTnet.Diagnostics.Logger;
+using MQTTnet.Diagnostics;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore
{
public sealed class MqttHostedServer : MqttServer, IHostedService
{
- readonly IMqttServerOptions _options;
-
- public MqttHostedServer(IMqttServerOptions options, IEnumerable adapters, IMqttNetLogger logger)
- : base(adapters, logger)
+ public MqttHostedServer(MqttServerOptions options, IEnumerable adapters, IMqttNetLogger logger)
+ : base(options, adapters, logger)
{
- _options = options ?? throw new ArgumentNullException(nameof(options));
}
public Task StartAsync(CancellationToken cancellationToken)
{
- _ = StartAsync(_options);
+ _ = StartAsync();
return Task.CompletedTask;
}
@@ -30,4 +30,4 @@ namespace MQTTnet.AspNetCore
return StopAsync();
}
}
-}
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.AspnetCore/MqttSubProtocolSelector.cs b/Source/MQTTnet.AspnetCore/MqttSubProtocolSelector.cs
index 6386b4fe..6f318b1b 100644
--- a/Source/MQTTnet.AspnetCore/MqttSubProtocolSelector.cs
+++ b/Source/MQTTnet.AspnetCore/MqttSubProtocolSelector.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
diff --git a/Source/MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs b/Source/MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs
index 270305d1..a83eb026 100644
--- a/Source/MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs
+++ b/Source/MQTTnet.AspnetCore/MqttWebSocketServerAdapter.cs
@@ -1,4 +1,8 @@
-using Microsoft.AspNetCore.Http;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.AspNetCore.Http;
using MQTTnet.Adapter;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
@@ -6,23 +10,19 @@ using MQTTnet.Server;
using System;
using System.Net.WebSockets;
using System.Threading.Tasks;
-using MQTTnet.Diagnostics.Logger;
+using MQTTnet.Diagnostics;
namespace MQTTnet.AspNetCore
{
public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
{
- readonly IMqttNetLogger _rootLogger;
-
- public MqttWebSocketServerAdapter(IMqttNetLogger logger)
- {
- _rootLogger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
+ IMqttNetLogger _logger = new MqttNetNullLogger();
+
public Func ClientHandler { get; set; }
- public Task StartAsync(IMqttServerOptions options)
+ public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
{
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
return Task.CompletedTask;
}
@@ -45,11 +45,10 @@ namespace MQTTnet.AspNetCore
var clientHandler = ClientHandler;
if (clientHandler != null)
{
- var writer = new SpanBasedMqttPacketWriter();
- var formatter = new MqttPacketFormatterAdapter(writer);
+ var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
- using (var channelAdapter = new MqttChannelAdapter(channel, formatter, null, _rootLogger))
+ using (var channelAdapter = new MqttChannelAdapter(channel, formatter, null, _logger))
{
await clientHandler(channelAdapter).ConfigureAwait(false);
}
diff --git a/Source/MQTTnet.AspnetCore/Extensions/ReaderExtensions.cs b/Source/MQTTnet.AspnetCore/ReaderExtensions.cs
similarity index 80%
rename from Source/MQTTnet.AspnetCore/Extensions/ReaderExtensions.cs
rename to Source/MQTTnet.AspnetCore/ReaderExtensions.cs
index 0560beae..d3b00ab6 100644
--- a/Source/MQTTnet.AspnetCore/Extensions/ReaderExtensions.cs
+++ b/Source/MQTTnet.AspnetCore/ReaderExtensions.cs
@@ -1,18 +1,21 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Buffers;
using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Packets;
-namespace MQTTnet.AspNetCore.Extensions
+namespace MQTTnet.AspNetCore
{
public static class ReaderExtensions
{
public static bool TryDecode(this MqttPacketFormatterAdapter formatter,
- SpanBasedMqttPacketBodyReader reader,
in ReadOnlySequence input,
- out MqttBasePacket packet,
+ out MqttPacket packet,
out SequencePosition consumed,
out SequencePosition observed,
out int bytesRead)
@@ -30,7 +33,7 @@ namespace MQTTnet.AspNetCore.Extensions
return false;
}
- var fixedheader = copy.First.Span[0];
+ var fixedHeader = copy.First.Span[0];
if (!TryReadBodyLength(ref copy, out int headerLength, out var bodyLength))
{
return false;
@@ -42,10 +45,9 @@ namespace MQTTnet.AspNetCore.Extensions
}
var bodySlice = copy.Slice(0, bodyLength);
- var buffer = bodySlice.GetMemory();
- reader.SetBuffer(buffer);
-
- var receivedMqttPacket = new ReceivedMqttPacket(fixedheader, reader, buffer.Length + 2);
+ var buffer = bodySlice.GetMemory().ToArray();
+
+ var receivedMqttPacket = new ReceivedMqttPacket(fixedHeader, new ArraySegment(buffer, 0, buffer.Length), buffer.Length + 2);
if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
{
@@ -59,7 +61,7 @@ namespace MQTTnet.AspNetCore.Extensions
return true;
}
- private static ReadOnlyMemory GetMemory(this in ReadOnlySequence input)
+ static ReadOnlyMemory GetMemory(this in ReadOnlySequence input)
{
if (input.IsSingleSegment)
{
@@ -70,7 +72,7 @@ namespace MQTTnet.AspNetCore.Extensions
return input.ToArray();
}
- private static bool TryReadBodyLength(ref ReadOnlySequence input, out int headerLength, out int bodyLength)
+ static bool TryReadBodyLength(ref ReadOnlySequence input, out int headerLength, out int bodyLength)
{
// Alorithm taken from https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html.
var multiplier = 1;
@@ -88,6 +90,7 @@ namespace MQTTnet.AspNetCore.Extensions
{
return false;
}
+
encodedByte = temp[index];
index++;
diff --git a/Source/MQTTnet.AspnetCore/Extensions/ServiceCollectionExtensions.cs b/Source/MQTTnet.AspnetCore/ServiceCollectionExtensions.cs
similarity index 66%
rename from Source/MQTTnet.AspnetCore/Extensions/ServiceCollectionExtensions.cs
rename to Source/MQTTnet.AspnetCore/ServiceCollectionExtensions.cs
index 683fe1cc..8933bca0 100644
--- a/Source/MQTTnet.AspnetCore/Extensions/ServiceCollectionExtensions.cs
+++ b/Source/MQTTnet.AspnetCore/ServiceCollectionExtensions.cs
@@ -1,17 +1,33 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
-using MQTTnet.Diagnostics.Logger;
using MQTTnet.Implementations;
using MQTTnet.Server;
-namespace MQTTnet.AspNetCore.Extensions
+namespace MQTTnet.AspNetCore
{
public static class ServiceCollectionExtensions
{
- public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, IMqttServerOptions options)
+ public static IServiceCollection AddMqttServer(this IServiceCollection serviceCollection, Action configure = null)
+ {
+ if (serviceCollection is null)
+ {
+ throw new ArgumentNullException(nameof(serviceCollection));
+ }
+
+ serviceCollection.AddMqttConnectionHandler();
+ serviceCollection.AddHostedMqttServer(configure);
+
+ return serviceCollection;
+ }
+
+ public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, MqttServerOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
@@ -22,13 +38,13 @@ namespace MQTTnet.AspNetCore.Extensions
return services;
}
- public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action configure)
+ public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action configure = null)
{
- services.AddSingleton(s =>
+ services.AddSingleton(s =>
{
- var builder = new MqttServerOptionsBuilder();
- configure(builder);
- return builder.Build();
+ var serverOptionsBuilder = new MqttServerOptionsBuilder();
+ configure?.Invoke(serverOptionsBuilder);
+ return serverOptionsBuilder.Build();
});
services.AddHostedMqttServer();
@@ -38,7 +54,7 @@ namespace MQTTnet.AspNetCore.Extensions
public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action configure)
{
- services.AddSingleton(s =>
+ services.AddSingleton(s =>
{
var builder = new AspNetMqttServerOptionsBuilder(s);
configure(builder);
@@ -50,24 +66,14 @@ namespace MQTTnet.AspNetCore.Extensions
return services;
}
- public static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
- where TOptions : class, IMqttServerOptions
- {
- services.AddSingleton();
-
- services.AddHostedMqttServer();
-
- return services;
- }
-
- private static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
+ static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
{
var logger = new MqttNetEventLogger();
services.AddSingleton(logger);
services.AddSingleton();
services.AddSingleton(s => s.GetService());
- services.AddSingleton(s => s.GetService());
+ services.AddSingleton(s => s.GetService());
return services;
}
diff --git a/Source/MQTTnet.AspnetCore/SpanBasedMqttPacketBodyReader.cs b/Source/MQTTnet.AspnetCore/SpanBasedMqttPacketBodyReader.cs
deleted file mode 100644
index 2f921db8..00000000
--- a/Source/MQTTnet.AspnetCore/SpanBasedMqttPacketBodyReader.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-using MQTTnet.Exceptions;
-using MQTTnet.Formatter;
-using System;
-using System.Buffers.Binary;
-using System.Text;
-
-namespace MQTTnet.AspNetCore
-{
- public class SpanBasedMqttPacketBodyReader : IMqttPacketBodyReader
- {
- ReadOnlyMemory _buffer;
-
- int _offset;
-
- public void SetBuffer(ReadOnlyMemory buffer)
- {
- _buffer = buffer;
- _offset = 0;
- }
-
- public int Length => _buffer.Length;
-
- public bool EndOfStream => _buffer.Length.Equals(_offset);
-
- public int Offset => _offset;
-
- public byte ReadByte()
- {
- return _buffer.Span[_offset++];
- }
-
- public byte[] ReadRemainingData()
- {
- return _buffer.Slice(_offset).ToArray();
- }
-
- public byte[] ReadWithLengthPrefix()
- {
- return ReadSegmentWithLengthPrefix().ToArray();
- }
-
- public unsafe string ReadStringWithLengthPrefix()
- {
- var buffer = ReadSegmentWithLengthPrefix();
- if (buffer.Length == 0)
- {
- return string.Empty;
- }
-
- fixed (byte* bytes = &buffer.GetPinnableReference())
- {
- var result = Encoding.UTF8.GetString(bytes, buffer.Length);
- return result;
- }
- }
-
- public ushort ReadTwoByteInteger()
- {
- var result = BinaryPrimitives.ReadUInt16BigEndian(_buffer.Span.Slice(_offset));
- _offset += 2;
- return result;
- }
-
- public uint ReadFourByteInteger()
- {
- var result = BinaryPrimitives.ReadUInt32BigEndian(_buffer.Span.Slice(_offset));
- _offset += 4;
- return result;
- }
-
- public uint ReadVariableLengthInteger()
- {
- var multiplier = 1;
- var value = 0U;
- byte encodedByte;
-
- do
- {
- encodedByte = ReadByte();
- value += (uint)((encodedByte & 127) * multiplier);
-
- if (multiplier > 2097152)
- {
- throw new MqttProtocolViolationException("Variable length integer is invalid.");
- }
-
- multiplier *= 128;
- } while ((encodedByte & 128) != 0);
-
- return value;
- }
-
- public bool ReadBoolean()
- {
- var buffer = ReadByte();
-
- if (buffer == 0)
- {
- return false;
- }
-
- if (buffer == 1)
- {
- return true;
- }
-
- throw new MqttProtocolViolationException("Boolean values can be 0 or 1 only.");
- }
-
- public void Seek(int position)
- {
- _offset = position;
- }
-
- ReadOnlySpan ReadSegmentWithLengthPrefix()
- {
- var span = _buffer.Span;
- var length = BinaryPrimitives.ReadUInt16BigEndian(span.Slice(_offset));
-
- if (Length < _offset + length)
- {
- throw new MqttProtocolViolationException($"Expected at least {_offset + 2 + length} bytes but there are only {Length} bytes");
- }
- var result = span.Slice(_offset + 2, length);
- _offset += 2 + length;
- return result;
- }
- }
-}
diff --git a/Source/MQTTnet.AspnetCore/SpanBasedMqttPacketWriter.cs b/Source/MQTTnet.AspnetCore/SpanBasedMqttPacketWriter.cs
deleted file mode 100644
index 62f678b2..00000000
--- a/Source/MQTTnet.AspnetCore/SpanBasedMqttPacketWriter.cs
+++ /dev/null
@@ -1,144 +0,0 @@
-using MQTTnet.Formatter;
-using System;
-using System.Buffers;
-using System.Buffers.Binary;
-using System.Text;
-
-namespace MQTTnet.AspNetCore
-{
- public class SpanBasedMqttPacketWriter : IMqttPacketWriter
- {
- readonly ArrayPool _pool = ArrayPool.Create();
-
- public SpanBasedMqttPacketWriter()
- {
- Reset(0);
- }
-
- byte[] _buffer;
- int _position;
-
- public int Length { get; set; }
-
- public void FreeBuffer()
- {
- _pool.Return(_buffer);
- }
-
- public byte[] GetBuffer()
- {
- return _buffer;
- }
-
- public void Reset(int v)
- {
- _buffer = _pool.Rent(1500);
- Length = v;
- _position = v;
- }
-
- public void Seek(int v)
- {
- _position = v;
- }
-
- public void Write(byte value)
- {
- GrowIfNeeded(1);
- _buffer[_position] = value;
- Commit(1);
- }
-
- public void Write(ushort value)
- {
- GrowIfNeeded(2);
-
- BinaryPrimitives.WriteUInt16BigEndian(_buffer.AsSpan(_position), value);
- Commit(2);
- }
-
- public void Write(IMqttPacketWriter propertyWriter)
- {
- if (propertyWriter == null) throw new ArgumentNullException(nameof(propertyWriter));
-
- GrowIfNeeded(propertyWriter.Length);
- Write(propertyWriter.GetBuffer(), 0, propertyWriter.Length);
- }
-
- public void Write(byte[] payload, int start, int length)
- {
- GrowIfNeeded(length);
-
- payload.AsSpan(start, length).CopyTo(_buffer.AsSpan(_position));
- Commit(length);
- }
-
- public void WriteVariableLengthInteger(uint value)
- {
- GrowIfNeeded(4);
-
- var x = value;
- do
- {
- var encodedByte = x % 128;
- x = x / 128;
- if (x > 0)
- {
- encodedByte = encodedByte | 128;
- }
-
- _buffer[_position] = (byte)encodedByte;
- Commit(1);
- } while (x > 0);
- }
-
- public void WriteWithLengthPrefix(string value)
- {
- var bytesLength = Encoding.UTF8.GetByteCount(value ?? string.Empty);
- GrowIfNeeded(bytesLength + 2);
-
- Write((ushort)bytesLength);
- Encoding.UTF8.GetBytes(value ?? string.Empty, 0, value?.Length ?? 0, _buffer, _position);
- Commit(bytesLength);
- }
-
- public void WriteWithLengthPrefix(byte[] payload)
- {
- GrowIfNeeded(payload.Length + 2);
-
- Write((ushort)payload.Length);
- payload.CopyTo(_buffer, _position);
- Commit(payload.Length);
- }
-
- void Commit(int count)
- {
- if (_position == Length)
- {
- Length += count;
- }
-
- _position += count;
- }
-
- void GrowIfNeeded(int requiredAdditional)
- {
- var requiredTotal = _position + requiredAdditional;
- if (_buffer.Length >= requiredTotal)
- {
- return;
- }
-
- var newBufferLength = _buffer.Length;
- while (newBufferLength < requiredTotal)
- {
- newBufferLength *= 2;
- }
-
- var newBuffer = _pool.Rent(newBufferLength);
- Array.Copy(_buffer, newBuffer, _buffer.Length);
- _pool.Return(_buffer);
- _buffer = newBuffer;
- }
- }
-}
diff --git a/Source/MQTTnet.Benchmarks/BaseBenchmark.cs b/Source/MQTTnet.Benchmarks/BaseBenchmark.cs
new file mode 100644
index 00000000..a33ad7a1
--- /dev/null
+++ b/Source/MQTTnet.Benchmarks/BaseBenchmark.cs
@@ -0,0 +1,6 @@
+namespace MQTTnet.Benchmarks
+{
+ public abstract class BaseBenchmark
+ {
+ }
+}
\ No newline at end of file
diff --git a/Tests/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs b/Source/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs
similarity index 86%
rename from Tests/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs
rename to Source/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs
index 34a05a9b..96c6419b 100644
--- a/Tests/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/ChannelAdapterBenchmark.cs
@@ -1,11 +1,15 @@
-using BenchmarkDotNet.Attributes;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using BenchmarkDotNet.Attributes;
using MQTTnet.Adapter;
using MQTTnet.Internal;
using MQTTnet.Packets;
using System;
using System.IO;
using System.Threading;
-using MQTTnet.Diagnostics.Logger;
+using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
namespace MQTTnet.Benchmarks
@@ -26,9 +30,9 @@ namespace MQTTnet.Benchmarks
Topic = "A"
};
- var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311);
+ var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
- var serializedPacket = Join(serializer.Encode(_packet));
+ var serializedPacket = Join(serializer.Encode(_packet).Join());
_iterations = 10000;
diff --git a/Tests/MQTTnet.Benchmarks/Configurations/AllowNonOptimized.cs b/Source/MQTTnet.Benchmarks/Configurations/AllowNonOptimized.cs
similarity index 69%
rename from Tests/MQTTnet.Benchmarks/Configurations/AllowNonOptimized.cs
rename to Source/MQTTnet.Benchmarks/Configurations/AllowNonOptimized.cs
index c40eb09c..2ae1e19b 100644
--- a/Tests/MQTTnet.Benchmarks/Configurations/AllowNonOptimized.cs
+++ b/Source/MQTTnet.Benchmarks/Configurations/AllowNonOptimized.cs
@@ -1,4 +1,8 @@
-using BenchmarkDotNet.Jobs;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Validators;
namespace MQTTnet.Benchmarks.Configurations
diff --git a/Tests/MQTTnet.Benchmarks/Configurations/BaseConfig.cs b/Source/MQTTnet.Benchmarks/Configurations/BaseConfig.cs
similarity index 69%
rename from Tests/MQTTnet.Benchmarks/Configurations/BaseConfig.cs
rename to Source/MQTTnet.Benchmarks/Configurations/BaseConfig.cs
index ff5b148d..15324a6a 100644
--- a/Tests/MQTTnet.Benchmarks/Configurations/BaseConfig.cs
+++ b/Source/MQTTnet.Benchmarks/Configurations/BaseConfig.cs
@@ -1,4 +1,8 @@
-using BenchmarkDotNet.Configs;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using BenchmarkDotNet.Configs;
using System.Linq;
namespace MQTTnet.Benchmarks.Configurations
diff --git a/Source/MQTTnet.Benchmarks/Configurations/RuntimeCompareConfig.cs b/Source/MQTTnet.Benchmarks/Configurations/RuntimeCompareConfig.cs
new file mode 100644
index 00000000..cd01b723
--- /dev/null
+++ b/Source/MQTTnet.Benchmarks/Configurations/RuntimeCompareConfig.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using BenchmarkDotNet.Environments;
+using BenchmarkDotNet.Jobs;
+using BenchmarkDotNet.Toolchains.CsProj;
+
+namespace MQTTnet.Benchmarks.Configurations
+{
+ public class RuntimeCompareConfig : BaseConfig
+ {
+ public RuntimeCompareConfig()
+ {
+ AddJob(Job.Default.WithRuntime(ClrRuntime.Net48));
+ AddJob(Job.Default.WithRuntime(CoreRuntime.Core50).WithToolchain(CsProjCoreToolchain.NetCoreApp50));
+ }
+ }
+}
diff --git a/Tests/MQTTnet.Benchmarks/LoggerBenchmark.cs b/Source/MQTTnet.Benchmarks/LoggerBenchmark.cs
similarity index 88%
rename from Tests/MQTTnet.Benchmarks/LoggerBenchmark.cs
rename to Source/MQTTnet.Benchmarks/LoggerBenchmark.cs
index f31f89ef..f7814e8f 100644
--- a/Tests/MQTTnet.Benchmarks/LoggerBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/LoggerBenchmark.cs
@@ -1,10 +1,14 @@
-using BenchmarkDotNet.Attributes;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
-using MQTTnet.Diagnostics.Logger;
+using MQTTnet.Diagnostics;
namespace MQTTnet.Benchmarks
{
- [SimpleJob(RuntimeMoniker.Net461)]
+ [SimpleJob(RuntimeMoniker.NetCoreApp50)]
[RPlotExporter]
[MemoryDiagnoser]
public class LoggerBenchmark
diff --git a/Source/MQTTnet.Benchmarks/MQTTnet.Benchmarks.csproj b/Source/MQTTnet.Benchmarks/MQTTnet.Benchmarks.csproj
new file mode 100644
index 00000000..10a38c15
--- /dev/null
+++ b/Source/MQTTnet.Benchmarks/MQTTnet.Benchmarks.csproj
@@ -0,0 +1,39 @@
+
+
+
+ Exe
+ Full
+ net5.0;net6.0
+ 7.2
+ false
+ false
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/MQTTnet.Benchmarks/MessageDeliveryBenchmark.cs b/Source/MQTTnet.Benchmarks/MessageDeliveryBenchmark.cs
new file mode 100644
index 00000000..e904e029
--- /dev/null
+++ b/Source/MQTTnet.Benchmarks/MessageDeliveryBenchmark.cs
@@ -0,0 +1,234 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+using MQTTnet.Client;
+using MQTTnet.Server;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+
+namespace MQTTnet.Benchmarks
+{
+ ///
+ /// Create a number of topics, publish, subscribe, and wait for response
+ ///
+ [MemoryDiagnoser]
+ public class MessageDeliveryBenchmark
+ {
+ List _topicPublishMessages;
+
+ [Params(1, 5)]
+ public int NumTopicsPerPublisher;
+
+ [Params(1000, 10000)]
+ public int NumPublishers;
+
+ [Params(10)]
+ public int NumSubscribers;
+
+ [Params(5, 10, 20, 50)]
+ public int NumSubscribedTopicsPerSubscriber;
+
+ object _lockMsgCount;
+ int _messagesReceivedCount;
+ int _messagesExpectedCount;
+ CancellationTokenSource _cancellationTokenSource;
+
+ MqttServer _mqttServer;
+ List _mqttSubscriberClients;
+ Dictionary _mqttPublisherClientsByPublisherName;
+
+ Dictionary> _topicsByPublisher;
+ Dictionary _publisherByTopic;
+ List _allSubscribedTopics; // Keep track of the subset of topics that are subscribed
+
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ _lockMsgCount = new object();
+
+ Dictionary> singleWildcardTopicsByPublisher;
+ Dictionary> multiWildcardTopicsByPublisher;
+
+ TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out _topicsByPublisher, out singleWildcardTopicsByPublisher, out multiWildcardTopicsByPublisher);
+
+ var topics = _topicsByPublisher.First().Value;
+ _topicPublishMessages = new List();
+ // Prepare messages, same for each publisher
+ foreach (var topic in topics)
+ {
+ var message = new MqttApplicationMessageBuilder()
+ .WithTopic(topic)
+ .WithPayload(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 })
+ .Build();
+ _topicPublishMessages.Add(message);
+ }
+
+ // Create server
+ var factory = new MqttFactory();
+ var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
+ _mqttServer = factory.CreateMqttServer(serverOptions);
+ _mqttServer.StartAsync().GetAwaiter().GetResult();
+
+ // Create publisher clients
+ _mqttPublisherClientsByPublisherName = new Dictionary();
+ foreach (var pt in _topicsByPublisher)
+ {
+ var publisherName = pt.Key;
+ var mqttClient = factory.CreateMqttClient();
+ var publisherOptions = new MqttClientOptionsBuilder()
+ .WithTcpServer("localhost")
+ .WithClientId(publisherName)
+ .WithKeepAlivePeriod(TimeSpan.FromSeconds(30))
+ .Build();
+ mqttClient.ConnectAsync(publisherOptions).GetAwaiter().GetResult();
+ _mqttPublisherClientsByPublisherName.Add(publisherName, mqttClient);
+ }
+
+ // Create subscriber clients
+ _mqttSubscriberClients = new List();
+ for (var i = 0; i < NumSubscribers; i++)
+ {
+ var mqttSubscriberClient = factory.CreateMqttClient();
+ _mqttSubscriberClients.Add(mqttSubscriberClient);
+
+ var subscriberOptions = new MqttClientOptionsBuilder()
+ .WithTcpServer("localhost")
+ .WithClientId("subscriber" + i)
+ .Build();
+ mqttSubscriberClient.ApplicationMessageReceivedAsync += r =>
+ {
+ // count messages and signal cancellation when expected message count is reached
+ lock (_lockMsgCount)
+ {
+ ++_messagesReceivedCount;
+ if (_messagesReceivedCount == _messagesExpectedCount)
+ {
+ _cancellationTokenSource.Cancel();
+ }
+ }
+ return Task.CompletedTask;
+ };
+ mqttSubscriberClient.ConnectAsync(subscriberOptions).GetAwaiter().GetResult();
+ }
+
+
+ List allTopics = new List();
+ _publisherByTopic = new Dictionary();
+ foreach (var t in _topicsByPublisher)
+ {
+ foreach (var topic in t.Value)
+ {
+ _publisherByTopic.Add(topic, t.Key);
+ allTopics.Add(topic);
+ }
+ }
+
+ // Subscribe to NumSubscribedTopics topics spread across all topics
+ _allSubscribedTopics = new List();
+
+ var totalNumTopics = NumPublishers * NumTopicsPerPublisher;
+ int topicIndexStep = totalNumTopics / (NumSubscribedTopicsPerSubscriber * NumSubscribers);
+ if (topicIndexStep * NumSubscribedTopicsPerSubscriber * NumSubscribers != totalNumTopics)
+ {
+ throw new System.Exception(
+ String.Format("The total number of topics must be divisible by the number of subscribed topics across all subscribers. Total number of topics: {0}, topic step: {1}",
+ totalNumTopics, topicIndexStep
+ ));
+ }
+
+ var topicIndex = 0;
+ foreach (var mqttSubscriber in _mqttSubscriberClients)
+ {
+ for (var i = 0; i < NumSubscribedTopicsPerSubscriber; ++i, topicIndex += topicIndexStep)
+ {
+ var topic = allTopics[topicIndex];
+ _allSubscribedTopics.Add(topic);
+ var subOptions = new Client.MqttClientSubscribeOptionsBuilder().WithTopicFilter(
+ new Packets.MqttTopicFilter() { Topic = topic })
+ .Build();
+ mqttSubscriber.SubscribeAsync(subOptions).GetAwaiter().GetResult();
+ }
+ }
+
+ Task.Delay(1000).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Publish messages and wait for messages sent to subscribers
+ ///
+ [Benchmark]
+ public void DeliverMessages()
+ {
+ // There should be one message received per publish for each subscribed topic
+ _messagesExpectedCount = NumSubscribedTopicsPerSubscriber * NumSubscribers;
+
+ // Loop for a while and exchange messages
+
+ _messagesReceivedCount = 0;
+
+ _cancellationTokenSource = new CancellationTokenSource();
+
+ // same payload for all messages
+ var payload = new byte[] { 1, 2, 3, 4 };
+
+ var tasks = new List();
+
+ // publish a message for each subscribed topic
+ foreach (var topic in _allSubscribedTopics)
+ {
+ var message = new MqttApplicationMessageBuilder()
+ .WithTopic(topic)
+ .WithPayload(payload)
+ .Build();
+ // pick the correct publisher
+ var publisherName = _publisherByTopic[topic];
+ var publisherClient = _mqttPublisherClientsByPublisherName[publisherName];
+ _ = publisherClient.PublishAsync(message);
+ }
+
+ // Wait one message per publish to be received by subscriber (in the subscriber's application message handler)
+ try
+ {
+ Task.Delay(30000, _cancellationTokenSource.Token).GetAwaiter().GetResult();
+ }
+ catch
+ {
+
+ }
+
+ _cancellationTokenSource.Dispose();
+
+ if (_messagesReceivedCount < _messagesExpectedCount)
+ {
+ throw new Exception(string.Format("Messages Received Count mismatch, expected {0}, received {1}", _messagesExpectedCount, _messagesReceivedCount));
+ }
+ }
+
+ [GlobalCleanup]
+ public void Cleanup()
+ {
+ foreach (var mp in _mqttPublisherClientsByPublisherName)
+ {
+ var mqttPublisherClient = mp.Value;
+ mqttPublisherClient.DisconnectAsync().GetAwaiter().GetResult();
+ mqttPublisherClient.Dispose();
+ }
+ _mqttPublisherClientsByPublisherName.Clear();
+
+ foreach (var mqttSubscriber in _mqttSubscriberClients)
+ {
+ mqttSubscriber.DisconnectAsync().GetAwaiter().GetResult();
+ mqttSubscriber.Dispose();
+ }
+ _mqttSubscriberClients.Clear();
+
+ _mqttServer.StopAsync().GetAwaiter().GetResult();
+ _mqttServer.Dispose();
+ _mqttServer = null;
+ }
+ }
+}
diff --git a/Tests/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs b/Source/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs
similarity index 66%
rename from Tests/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs
rename to Source/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs
index 91718f15..175e6a2c 100644
--- a/Tests/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/MessageProcessingBenchmark.cs
@@ -1,29 +1,34 @@
-using BenchmarkDotNet.Attributes;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using MQTTnet.Client;
-using MQTTnet.Client.Options;
using MQTTnet.Server;
+using MqttClient = MQTTnet.Client.MqttClient;
namespace MQTTnet.Benchmarks
{
- [SimpleJob(RuntimeMoniker.Net461)]
+ [SimpleJob(RuntimeMoniker.NetCoreApp50)]
[RPlotExporter, RankColumn]
[MemoryDiagnoser]
public class MessageProcessingBenchmark
{
- IMqttServer _mqttServer;
- IMqttClient _mqttClient;
+ MqttServer _mqttServer;
+ MqttClient _mqttClient;
MqttApplicationMessage _message;
[GlobalSetup]
public void Setup()
{
+ var serverOptions = new MqttServerOptionsBuilder().Build();
+
var factory = new MqttFactory();
- _mqttServer = factory.CreateMqttServer();
+ _mqttServer = factory.CreateMqttServer(serverOptions);
_mqttClient = factory.CreateMqttClient();
- var serverOptions = new MqttServerOptionsBuilder().Build();
- _mqttServer.StartAsync(serverOptions).GetAwaiter().GetResult();
+ _mqttServer.StartAsync().GetAwaiter().GetResult();
var clientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("localhost").Build();
diff --git a/Tests/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs b/Source/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs
similarity index 83%
rename from Tests/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs
rename to Source/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs
index 651f4007..6b65a9a6 100644
--- a/Tests/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/MessageProcessingMqttConnectionContextBenchmark.cs
@@ -1,19 +1,24 @@
-using BenchmarkDotNet.Attributes;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
using MQTTnet.Client;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using MQTTnet.AspNetCore.Client;
-using MQTTnet.AspNetCore.Extensions;
-using MQTTnet.Client.Options;
-using MQTTnet.Diagnostics.Logger;
+using MQTTnet.AspNetCore;
+using MQTTnet.Diagnostics;
namespace MQTTnet.Benchmarks
{
+ [SimpleJob(RuntimeMoniker.NetCoreApp50)]
[MemoryDiagnoser]
public class MessageProcessingMqttConnectionContextBenchmark
{
IWebHost _host;
- IMqttClient _mqttClient;
+ MqttClient _mqttClient;
MqttApplicationMessage _message;
[GlobalSetup]
diff --git a/Source/MQTTnet.Benchmarks/MqttPacketReaderWriterBenchmark.cs b/Source/MQTTnet.Benchmarks/MqttPacketReaderWriterBenchmark.cs
new file mode 100644
index 00000000..abe0ab09
--- /dev/null
+++ b/Source/MQTTnet.Benchmarks/MqttPacketReaderWriterBenchmark.cs
@@ -0,0 +1,93 @@
+using System;
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+using MQTTnet.AspNetCore;
+using MQTTnet.Formatter;
+using MQTTnet.Tests.Mockups;
+
+namespace MQTTnet.Benchmarks
+{
+ [SimpleJob(RuntimeMoniker.NetCoreApp50)]
+ [MemoryDiagnoser]
+ public class MqttPacketReaderWriterBenchmark
+ {
+ readonly byte[] _demoPayload = new byte[1024];
+
+ byte[] _readPayload;
+
+ [GlobalCleanup]
+ public void GlobalCleanup()
+ {
+ }
+
+ [GlobalSetup]
+ public void GlobalSetup()
+ {
+ TestEnvironment.EnableLogger = false;
+
+ var writer = new MqttBufferWriter(4096, 65535);
+ writer.WriteString("A relative short string.");
+ writer.WriteBinaryData(_demoPayload);
+ writer.WriteByte(0x01);
+ writer.WriteByte(0x02);
+ writer.WriteVariableByteInteger(5647382);
+ writer.WriteString("A relative short string.");
+ writer.WriteVariableByteInteger(8574489);
+ writer.WriteBinaryData(_demoPayload);
+ writer.WriteByte(2);
+ writer.WriteByte(0x02);
+ writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
+ writer.WriteBinaryData(_demoPayload);
+
+ _readPayload = new ArraySegment(writer.GetBuffer(), 0, writer.Length).ToArray();
+ }
+
+ [Benchmark]
+ public void Read_100_000_Messages()
+ {
+ var reader = new MqttBufferReader();
+ reader.SetBuffer(_readPayload, 0, _readPayload.Length);
+
+ for (var i = 0; i < 100000; i++)
+ {
+ reader.Seek(0);
+
+ reader.ReadString();
+ reader.ReadBinaryData();
+ reader.ReadByte();
+ reader.ReadByte();
+ reader.ReadVariableByteInteger();
+ reader.ReadString();
+ reader.ReadVariableByteInteger();
+ reader.ReadBinaryData();
+ reader.ReadByte();
+ reader.ReadByte();
+ reader.ReadString();
+ reader.ReadBinaryData();
+ }
+ }
+
+ [Benchmark]
+ public void Write_100_000_Messages()
+ {
+ var writer = new MqttBufferWriter(4096, 65535);
+
+ for (var i = 0; i < 100000; i++)
+ {
+ writer.WriteString("A relative short string.");
+ writer.WriteByte(0x01);
+ writer.WriteByte(0x02);
+ writer.WriteVariableByteInteger(5647382);
+ writer.WriteString("A relative short string.");
+ writer.WriteVariableByteInteger(8574589);
+ writer.WriteBinaryData(_demoPayload);
+ writer.WriteByte(2);
+ writer.WriteByte(0x02);
+ writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
+ writer.WriteBinaryData(_demoPayload);
+
+ writer.Reset(0);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tests/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs b/Source/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs
similarity index 77%
rename from Tests/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs
rename to Source/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs
index 32ca9e79..5a84403c 100644
--- a/Tests/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/MqttTcpChannelBenchmark.cs
@@ -1,18 +1,24 @@
-using BenchmarkDotNet.Attributes;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using BenchmarkDotNet.Attributes;
using MQTTnet.Channel;
-using MQTTnet.Client.Options;
using MQTTnet.Implementations;
using MQTTnet.Server;
using System.Threading;
using System.Threading.Tasks;
-using MQTTnet.Diagnostics.Logger;
+using BenchmarkDotNet.Jobs;
+using MQTTnet.Client;
+using MQTTnet.Diagnostics;
namespace MQTTnet.Benchmarks
{
+ [SimpleJob(RuntimeMoniker.NetCoreApp50)]
[MemoryDiagnoser]
public sealed class MqttTcpChannelBenchmark
{
- IMqttServer _mqttServer;
+ MqttServer _mqttServer;
IMqttChannel _serverChannel;
IMqttChannel _clientChannel;
@@ -20,7 +26,7 @@ namespace MQTTnet.Benchmarks
public void Setup()
{
var factory = new MqttFactory();
- var tcpServer = new MqttTcpServerAdapter(new MqttNetEventLogger());
+ var tcpServer = new MqttTcpServerAdapter();
tcpServer.ClientHandler += args =>
{
_serverChannel =
@@ -31,10 +37,11 @@ namespace MQTTnet.Benchmarks
return Task.CompletedTask;
};
- _mqttServer = factory.CreateMqttServer(new[] { tcpServer }, new MqttNetEventLogger());
-
var serverOptions = new MqttServerOptionsBuilder().Build();
- _mqttServer.StartAsync(serverOptions).GetAwaiter().GetResult();
+ _mqttServer = factory.CreateMqttServer(serverOptions, new[] { tcpServer }, new MqttNetEventLogger());
+
+
+ _mqttServer.StartAsync().GetAwaiter().GetResult();
var clientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("localhost").Build();
diff --git a/Tests/MQTTnet.Benchmarks/Program.cs b/Source/MQTTnet.Benchmarks/Program.cs
similarity index 53%
rename from Tests/MQTTnet.Benchmarks/Program.cs
rename to Source/MQTTnet.Benchmarks/Program.cs
index b2d19194..13921d88 100644
--- a/Tests/MQTTnet.Benchmarks/Program.cs
+++ b/Source/MQTTnet.Benchmarks/Program.cs
@@ -1,7 +1,11 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using BenchmarkDotNet.Running;
using MQTTnet.Benchmarks.Configurations;
-using MQTTnet.Diagnostics.Runtime;
+using MQTTnet.Diagnostics;
namespace MQTTnet.Benchmarks
{
@@ -9,7 +13,8 @@ namespace MQTTnet.Benchmarks
{
public static void Main(string[] args)
{
- Console.WriteLine($"MQTTnet - BenchmarkApp.{TargetFrameworkProvider.TargetFramework}");
+ Console.WriteLine($"MQTTnet - Benchmarks ({TargetFrameworkProvider.TargetFramework})");
+ Console.WriteLine("--------------------------------------------------------");
Console.WriteLine("1 = MessageProcessingBenchmark");
Console.WriteLine("2 = SerializerBenchmark");
Console.WriteLine("3 = LoggerBenchmark");
@@ -18,6 +23,12 @@ namespace MQTTnet.Benchmarks
Console.WriteLine("6 = MqttTcpChannelBenchmark");
Console.WriteLine("7 = TcpPipesBenchmark");
Console.WriteLine("8 = MessageProcessingMqttConnectionContextBenchmark");
+ Console.WriteLine("9 = ServerProcessingBenchmark");
+ Console.WriteLine("a = MqttPacketReaderWriterBenchmark");
+ Console.WriteLine("b = RoundtripBenchmark");
+ Console.WriteLine("c = SubscribeBenchmark");
+ Console.WriteLine("d = UnsubscribeBenchmark");
+ Console.WriteLine("e = MessageDeliveryBenchmark");
var pressedKey = Console.ReadKey(true);
switch (pressedKey.KeyChar)
@@ -44,7 +55,25 @@ namespace MQTTnet.Benchmarks
BenchmarkRunner.Run();
break;
case '8':
- BenchmarkRunner.Run(new RuntimeCompareConfig()/*new AllowNonOptimized()*/);
+ BenchmarkRunner.Run(new RuntimeCompareConfig());
+ break;
+ case '9':
+ BenchmarkRunner.Run();
+ break;
+ case 'a':
+ BenchmarkRunner.Run(typeof(MqttPacketReaderWriterBenchmark));
+ break;
+ case 'b':
+ BenchmarkRunner.Run();
+ break;
+ case 'c':
+ BenchmarkRunner.Run();
+ break;
+ case 'd':
+ BenchmarkRunner.Run();
+ break;
+ case 'e':
+ BenchmarkRunner.Run();
break;
}
diff --git a/Source/MQTTnet.Benchmarks/RoundtripProcessingBenchmark.cs b/Source/MQTTnet.Benchmarks/RoundtripProcessingBenchmark.cs
new file mode 100644
index 00000000..c944ac56
--- /dev/null
+++ b/Source/MQTTnet.Benchmarks/RoundtripProcessingBenchmark.cs
@@ -0,0 +1,30 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+using MQTTnet.Tests.Mockups;
+using MQTTnet.Tests.Server;
+
+namespace MQTTnet.Benchmarks
+{
+ [SimpleJob(RuntimeMoniker.NetCoreApp50)]
+ [RPlotExporter, RankColumn]
+ [MemoryDiagnoser]
+ public class RoundtripProcessingBenchmark
+ {
+ [GlobalSetup]
+ public void GlobalSetup()
+ {
+ TestEnvironment.EnableLogger = false;
+ }
+
+ [GlobalCleanup]
+ public void GlobalCleanup()
+ {
+ }
+
+ [Benchmark]
+ public void Handle_100_000_Messages_In_Receiving_Client()
+ {
+ new Load_Tests().Handle_100_000_Messages_In_Receiving_Client().GetAwaiter().GetResult();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tests/MQTTnet.Benchmarks/SerializerBenchmark.cs b/Source/MQTTnet.Benchmarks/SerializerBenchmark.cs
similarity index 76%
rename from Tests/MQTTnet.Benchmarks/SerializerBenchmark.cs
rename to Source/MQTTnet.Benchmarks/SerializerBenchmark.cs
index e2b2bcd8..9bb94c4d 100644
--- a/Tests/MQTTnet.Benchmarks/SerializerBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/SerializerBenchmark.cs
@@ -1,4 +1,8 @@
-using BenchmarkDotNet.Attributes;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using BenchmarkDotNet.Attributes;
using MQTTnet.Packets;
using System;
using System.Security.Cryptography.X509Certificates;
@@ -9,29 +13,31 @@ using MQTTnet.Channel;
using MQTTnet.Formatter;
using MQTTnet.Formatter.V3;
using BenchmarkDotNet.Jobs;
-using MQTTnet.Diagnostics.Logger;
+using MQTTnet.Diagnostics;
namespace MQTTnet.Benchmarks
{
- [SimpleJob(RuntimeMoniker.Net461)]
+ [SimpleJob(RuntimeMoniker.NetCoreApp50)]
[RPlotExporter]
[MemoryDiagnoser]
- public class SerializerBenchmark
+ public class SerializerBenchmark : BaseBenchmark
{
- MqttBasePacket _packet;
+ MqttPacket _packet;
ArraySegment _serializedPacket;
IMqttPacketFormatter _serializer;
+ MqttBufferWriter _bufferWriter;
[GlobalSetup]
- public void Setup()
+ public void GlobalSetup()
{
_packet = new MqttPublishPacket
{
Topic = "A"
};
- _serializer = new MqttV311PacketFormatter(new MqttPacketWriter());
- _serializedPacket = _serializer.Encode(_packet);
+ _bufferWriter = new MqttBufferWriter(4096, 65535);
+ _serializer = new MqttV3PacketFormatter(_bufferWriter, MqttProtocolVersion.V311);
+ _serializedPacket = _serializer.Encode(_packet).Join();
}
[Benchmark]
@@ -40,7 +46,7 @@ namespace MQTTnet.Benchmarks
for (var i = 0; i < 10000; i++)
{
_serializer.Encode(_packet);
- _serializer.FreeBuffer();
+ _bufferWriter.Cleanup();
}
}
@@ -48,8 +54,7 @@ namespace MQTTnet.Benchmarks
public void Deserialize_10000_Messages()
{
var channel = new BenchmarkMqttChannel(_serializedPacket);
- var fixedHeader = new byte[2];
- var reader = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(new MqttPacketWriter()), null, new MqttNetEventLogger());
+ var reader = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535)), null, new MqttNetEventLogger());
for (var i = 0; i < 10000; i++)
{
diff --git a/Source/MQTTnet.Benchmarks/ServerProcessingBenchmark.cs b/Source/MQTTnet.Benchmarks/ServerProcessingBenchmark.cs
new file mode 100644
index 00000000..453a7ac7
--- /dev/null
+++ b/Source/MQTTnet.Benchmarks/ServerProcessingBenchmark.cs
@@ -0,0 +1,36 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+using MQTTnet.Tests.Mockups;
+using MQTTnet.Tests.Server;
+
+namespace MQTTnet.Benchmarks
+{
+ [SimpleJob(RuntimeMoniker.NetCoreApp50)]
+ [RPlotExporter, RankColumn]
+ [MemoryDiagnoser]
+ public class ServerProcessingBenchmark
+ {
+ [GlobalSetup]
+ public void GlobalSetup()
+ {
+ TestEnvironment.EnableLogger = false;
+ }
+
+ [GlobalCleanup]
+ public void GlobalCleanup()
+ {
+ }
+
+ [Benchmark]
+ public void Handle_100_000_Messages_In_Server_MqttClient()
+ {
+ new Load_Tests().Handle_100_000_Messages_In_Server().GetAwaiter().GetResult();
+ }
+
+ //[Benchmark]
+ public void Handle_100_000_Messages_In_Server_LowLevelMqttClient()
+ {
+ new Load_Tests().Handle_100_000_Messages_In_Low_Level_Client().GetAwaiter().GetResult();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.Benchmarks/SubscribeBenchmark.cs b/Source/MQTTnet.Benchmarks/SubscribeBenchmark.cs
new file mode 100644
index 00000000..b5acf814
--- /dev/null
+++ b/Source/MQTTnet.Benchmarks/SubscribeBenchmark.cs
@@ -0,0 +1,61 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+using MQTTnet.Client;
+using MQTTnet.Server;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MQTTnet.Benchmarks
+{
+ [MemoryDiagnoser]
+ public class SubscribeBenchmark
+ {
+ MqttServer _mqttServer;
+ MQTTnet.Client.MqttClient _mqttClient;
+
+ const int NumPublishers = 1;
+ const int NumTopicsPerPublisher = 10000;
+
+ List _topics;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out var topicsByPublisher, out var singleWildcardTopicsByPublisher, out var multiWildcardTopicsByPublisher);
+ _topics = topicsByPublisher.Values.First();
+
+ var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
+
+ var factory = new MqttFactory();
+ _mqttServer = factory.CreateMqttServer(serverOptions);
+ _mqttClient = factory.CreateMqttClient();
+
+ _mqttServer.StartAsync().GetAwaiter().GetResult();
+
+ var clientOptions = new MqttClientOptionsBuilder()
+ .WithTcpServer("localhost").Build();
+
+ _mqttClient.ConnectAsync(clientOptions).GetAwaiter().GetResult();
+ }
+
+ [GlobalCleanup]
+ public void Cleanup()
+ {
+ _mqttClient.DisconnectAsync().GetAwaiter().GetResult();
+ _mqttServer.StopAsync().GetAwaiter().GetResult();
+ _mqttServer.Dispose();
+ }
+
+ [Benchmark]
+ public void Subscribe_10000_Topics()
+ {
+ foreach (var topic in _topics)
+ {
+ var subscribeOptions = new MqttClientSubscribeOptionsBuilder()
+ .WithTopicFilter(topic, Protocol.MqttQualityOfServiceLevel.AtMostOnce)
+ .Build();
+ _mqttClient.SubscribeAsync(subscribeOptions).GetAwaiter().GetResult();
+ }
+ }
+ }
+}
diff --git a/Tests/MQTTnet.Benchmarks/TcpPipesBenchmark.cs b/Source/MQTTnet.Benchmarks/TcpPipesBenchmark.cs
similarity index 87%
rename from Tests/MQTTnet.Benchmarks/TcpPipesBenchmark.cs
rename to Source/MQTTnet.Benchmarks/TcpPipesBenchmark.cs
index 636d693e..c2f419c0 100644
--- a/Tests/MQTTnet.Benchmarks/TcpPipesBenchmark.cs
+++ b/Source/MQTTnet.Benchmarks/TcpPipesBenchmark.cs
@@ -1,13 +1,19 @@
-using System.IO.Pipelines;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO.Pipelines;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
using MQTTnet.AspNetCore.Client.Tcp;
namespace MQTTnet.Benchmarks
{
+ [SimpleJob(RuntimeMoniker.NetCoreApp50)]
[MemoryDiagnoser]
public class TcpPipesBenchmark
{
diff --git a/Source/MQTTnet.Benchmarks/TopicFilterComparerBenchmark.cs b/Source/MQTTnet.Benchmarks/TopicFilterComparerBenchmark.cs
new file mode 100644
index 00000000..c3f95f35
--- /dev/null
+++ b/Source/MQTTnet.Benchmarks/TopicFilterComparerBenchmark.cs
@@ -0,0 +1,277 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+
+namespace MQTTnet.Benchmarks
+{
+ [SimpleJob(RuntimeMoniker.NetCoreApp50)]
+ [RPlotExporter]
+ [MemoryDiagnoser]
+ public class TopicFilterComparerBenchmark
+ {
+ static readonly char[] TopicLevelSeparator = { '/' };
+
+ readonly string _longTopic =
+ "AAAAAAAAAAAAAssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
+
+ [Benchmark]
+ public void MqttTopicFilterComparer_10000_LoopMethod()
+ {
+ for (var i = 0; i < 100000; i++)
+ {
+ MqttTopicFilterComparer.Compare("sport/tennis/player1", "sport/#");
+ MqttTopicFilterComparer.Compare("sport/tennis/player1/ranking", "sport/#/ranking");
+ MqttTopicFilterComparer.Compare("sport/tennis/player1/score/wimbledon", "sport/+/player1/#");
+ MqttTopicFilterComparer.Compare("sport/tennis/player1", "sport/tennis/+");
+ MqttTopicFilterComparer.Compare("/finance", "+/+");
+ MqttTopicFilterComparer.Compare("/finance", "/+");
+ MqttTopicFilterComparer.Compare("/finance", "+");
+ MqttTopicFilterComparer.Compare(_longTopic, _longTopic);
+ }
+ }
+
+ [Benchmark]
+ public void MqttTopicFilterComparer_10000_LoopMethod_Without_Pointer()
+ {
+ for (var i = 0; i < 100000; i++)
+ {
+ MqttTopicFilterComparerWithoutPointer.Compare("sport/tennis/player1", "sport/#");
+ MqttTopicFilterComparerWithoutPointer.Compare("sport/tennis/player1/ranking", "sport/#/ranking");
+ MqttTopicFilterComparerWithoutPointer.Compare("sport/tennis/player1/score/wimbledon", "sport/+/player1/#");
+ MqttTopicFilterComparerWithoutPointer.Compare("sport/tennis/player1", "sport/tennis/+");
+ MqttTopicFilterComparerWithoutPointer.Compare("/finance", "+/+");
+ MqttTopicFilterComparerWithoutPointer.Compare("/finance", "/+");
+ MqttTopicFilterComparerWithoutPointer.Compare("/finance", "+");
+ MqttTopicFilterComparerWithoutPointer.Compare(_longTopic, _longTopic);
+ }
+ }
+
+ [Benchmark]
+ public void MqttTopicFilterComparer_10000_StringSplitMethod()
+ {
+ for (var i = 0; i < 100000; i++)
+ {
+ LegacyMethodByStringSplit("sport/tennis/player1", "sport/#");
+ LegacyMethodByStringSplit("sport/tennis/player1/ranking", "sport/#/ranking");
+ LegacyMethodByStringSplit("sport/tennis/player1/score/wimbledon", "sport/+/player1/#");
+ LegacyMethodByStringSplit("sport/tennis/player1", "sport/tennis/+");
+ LegacyMethodByStringSplit("/finance", "+/+");
+ LegacyMethodByStringSplit("/finance", "/+");
+ LegacyMethodByStringSplit("/finance", "+");
+ MqttTopicFilterComparer.Compare(_longTopic, _longTopic);
+ }
+ }
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ }
+
+ static bool LegacyMethodByStringSplit(string topic, string filter)
+ {
+ if (topic == null)
+ {
+ throw new ArgumentNullException(nameof(topic));
+ }
+
+ if (filter == null)
+ {
+ throw new ArgumentNullException(nameof(filter));
+ }
+
+ if (string.Equals(topic, filter, StringComparison.Ordinal))
+ {
+ return true;
+ }
+
+ var fragmentsTopic = topic.Split(TopicLevelSeparator, StringSplitOptions.None);
+ var fragmentsFilter = filter.Split(TopicLevelSeparator, StringSplitOptions.None);
+
+ // # > In either case it MUST be the last character specified in the Topic Filter [MQTT-4.7.1-2].
+ for (var i = 0; i < fragmentsFilter.Length; i++)
+ {
+ if (fragmentsFilter[i] == "+")
+ {
+ continue;
+ }
+
+ if (fragmentsFilter[i] == "#")
+ {
+ return true;
+ }
+
+ if (i >= fragmentsTopic.Length)
+ {
+ return false;
+ }
+
+ if (!string.Equals(fragmentsFilter[i], fragmentsTopic[i], StringComparison.Ordinal))
+ {
+ return false;
+ }
+ }
+
+ return fragmentsTopic.Length == fragmentsFilter.Length;
+ }
+
+ public static class MqttTopicFilterComparerWithoutPointer
+ {
+ public const char LevelSeparator = '/';
+ public const char MultiLevelWildcard = '#';
+ public const char SingleLevelWildcard = '+';
+ public const char ReservedTopicPrefix = '$';
+
+ public static MqttTopicFilterCompareResult Compare(string topic, string filter)
+ {
+ if (string.IsNullOrEmpty(topic))
+ {
+ return MqttTopicFilterCompareResult.TopicInvalid;
+ }
+
+ if (string.IsNullOrEmpty(filter))
+ {
+ return MqttTopicFilterCompareResult.FilterInvalid;
+ }
+
+ var filterOffset = 0;
+ var filterLength = filter.Length;
+
+ var topicOffset = 0;
+ var topicLength = topic.Length;
+
+ var topicPointer = topic;
+ var filterPointer = filter;
+
+ var isMultiLevelFilter = filterPointer[filterLength - 1] == MultiLevelWildcard;
+ var isReservedTopic = topicPointer[0] == ReservedTopicPrefix;
+
+ if (isReservedTopic && filterLength == 1 && isMultiLevelFilter)
+ {
+ // It is not allowed to receive i.e. '$foo/bar' with filter '#'.
+ return MqttTopicFilterCompareResult.NoMatch;
+ }
+
+ if (isReservedTopic && filterPointer[0] == SingleLevelWildcard)
+ {
+ // It is not allowed to receive i.e. '$SYS/monitor/Clients' with filter '+/monitor/Clients'.
+ return MqttTopicFilterCompareResult.NoMatch;
+ }
+
+ if (filterLength == 1 && isMultiLevelFilter)
+ {
+ // Filter '#' matches basically everything.
+ return MqttTopicFilterCompareResult.IsMatch;
+ }
+
+ // Go through the filter char by char.
+ while (filterOffset < filterLength && topicOffset < topicLength)
+ {
+ // Check if the current char is a multi level wildcard. The char is only allowed
+ // at the very las position.
+ if (filterPointer[filterOffset] == MultiLevelWildcard && filterOffset != filterLength - 1)
+ {
+ return MqttTopicFilterCompareResult.FilterInvalid;
+ }
+
+ if (filterPointer[filterOffset] == topicPointer[topicOffset])
+ {
+ if (topicOffset == topicLength - 1)
+ {
+ // Check for e.g. "foo" matching "foo/#"
+ if (filterOffset == filterLength - 3 && filterPointer[filterOffset + 1] == LevelSeparator && isMultiLevelFilter)
+ {
+ return MqttTopicFilterCompareResult.IsMatch;
+ }
+
+ // Check for e.g. "foo/" matching "foo/#"
+ if (filterOffset == filterLength - 2 && filterPointer[filterOffset] == LevelSeparator && isMultiLevelFilter)
+ {
+ return MqttTopicFilterCompareResult.IsMatch;
+ }
+ }
+
+ filterOffset++;
+ topicOffset++;
+
+ // Check if the end was reached and i.e. "foo/bar" matches "foo/bar"
+ if (filterOffset == filterLength && topicOffset == topicLength)
+ {
+ return MqttTopicFilterCompareResult.IsMatch;
+ }
+
+ var endOfTopic = topicOffset == topicLength;
+
+ if (endOfTopic && filterOffset == filterLength - 1 && filterPointer[filterOffset] == SingleLevelWildcard)
+ {
+ if (filterOffset > 0 && filterPointer[filterOffset - 1] != LevelSeparator)
+ {
+ return MqttTopicFilterCompareResult.FilterInvalid;
+ }
+
+ return MqttTopicFilterCompareResult.IsMatch;
+ }
+ }
+ else
+ {
+ if (filterPointer[filterOffset] == SingleLevelWildcard)
+ {
+ // Check for invalid "+foo" or "a/+foo" subscription
+ if (filterOffset > 0 && filterPointer[filterOffset - 1] != LevelSeparator)
+ {
+ return MqttTopicFilterCompareResult.FilterInvalid;
+ }
+
+ // Check for bad "foo+" or "foo+/a" subscription
+ if (filterOffset < filterLength - 1 && filterPointer[filterOffset + 1] != LevelSeparator)
+ {
+ return MqttTopicFilterCompareResult.FilterInvalid;
+ }
+
+ filterOffset++;
+ while (topicOffset < topicLength && topicPointer[topicOffset] != LevelSeparator)
+ {
+ topicOffset++;
+ }
+
+ if (topicOffset == topicLength && filterOffset == filterLength)
+ {
+ return MqttTopicFilterCompareResult.IsMatch;
+ }
+ }
+ else if (filterPointer[filterOffset] == MultiLevelWildcard)
+ {
+ if (filterOffset > 0 && filterPointer[filterOffset - 1] != LevelSeparator)
+ {
+ return MqttTopicFilterCompareResult.FilterInvalid;
+ }
+
+ if (filterOffset + 1 != filterLength)
+ {
+ return MqttTopicFilterCompareResult.FilterInvalid;
+ }
+
+ return MqttTopicFilterCompareResult.IsMatch;
+ }
+ else
+ {
+ // Check for e.g. "foo/bar" matching "foo/+/#".
+ if (filterOffset > 0 && filterOffset + 2 == filterLength && topicOffset == topicLength && filterPointer[filterOffset - 1] == SingleLevelWildcard &&
+ filterPointer[filterOffset] == LevelSeparator && isMultiLevelFilter)
+ {
+ return MqttTopicFilterCompareResult.IsMatch;
+ }
+
+ return MqttTopicFilterCompareResult.NoMatch;
+ }
+ }
+ }
+
+ return MqttTopicFilterCompareResult.NoMatch;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.Benchmarks/TopicGenerator.cs b/Source/MQTTnet.Benchmarks/TopicGenerator.cs
new file mode 100644
index 00000000..cb17fee4
--- /dev/null
+++ b/Source/MQTTnet.Benchmarks/TopicGenerator.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MQTTnet.Benchmarks
+{
+ public class TopicGenerator
+ {
+ public static void Generate(
+ int numPublishers, int numTopicsPerPublisher,
+ out Dictionary> topicsByPublisher,
+ out Dictionary> singleWildcardTopicsByPublisher,
+ out Dictionary> multiWildcardTopicsByPublisher
+ )
+ {
+ topicsByPublisher = new Dictionary>();
+ singleWildcardTopicsByPublisher = new Dictionary>();
+ multiWildcardTopicsByPublisher = new Dictionary>();
+
+ // Find some reasonable distribution across three topic levels
+ var topicsPerLevel = (int)Math.Pow(numTopicsPerPublisher, (1.0 / 3.0));
+ if (topicsPerLevel <= 0)
+ {
+ topicsPerLevel = 1;
+ }
+
+ int numLevel1Topics = topicsPerLevel;
+ int numLevel2Topics = topicsPerLevel;
+
+ var maxNumLevel3Topics = 1 + (int)((double)numTopicsPerPublisher / numLevel1Topics / numLevel2Topics);
+ if (maxNumLevel3Topics <= 0)
+ {
+ maxNumLevel3Topics = 1;
+ }
+
+ for (var p = 0; p < numPublishers; ++p)
+ {
+ int publisherTopicCount = 0;
+ var publisherName = "pub" + p;
+ for (var l1 = 0; l1 < numLevel1Topics; ++l1)
+ {
+ for (var l2 = 0; l2 < numLevel2Topics; ++l2)
+ {
+ for (var l3 = 0; l3 < maxNumLevel3Topics; ++l3)
+ {
+ if (publisherTopicCount >= numTopicsPerPublisher)
+ break;
+
+ var topic = string.Format("{0}/building{1}/level{2}/sensor{3}", publisherName, l1 + 1, l2 + 1, l3 + 1);
+ AddPublisherTopic(publisherName, topic, topicsByPublisher);
+
+ if (l2 == 0)
+ {
+ var singleWildcardTopic = string.Format("{0}/building{1}/+/sensor{2}", publisherName, l1 + 1, l3 + 1);
+ AddPublisherTopic(publisherName, singleWildcardTopic, singleWildcardTopicsByPublisher);
+ }
+ if ((l1 == 0) && (l3 == 0))
+ {
+ var multiWildcardTopic = string.Format("{0}/+/level{1}/+", publisherName, l2 + 1);
+ AddPublisherTopic(publisherName, multiWildcardTopic, multiWildcardTopicsByPublisher);
+ }
+
+ ++publisherTopicCount;
+ }
+ }
+ }
+ }
+ }
+
+ static void AddPublisherTopic(string publisherName, string topic, Dictionary> topicsByPublisher)
+ {
+ List topicList;
+ if (!topicsByPublisher.TryGetValue(publisherName, out topicList))
+ {
+ topicList = new List();
+ topicsByPublisher.Add(publisherName, topicList);
+ }
+ topicList.Add(topic);
+ }
+ }
+}
diff --git a/Source/MQTTnet.Benchmarks/UnsubscribeBenchmark.cs b/Source/MQTTnet.Benchmarks/UnsubscribeBenchmark.cs
new file mode 100644
index 00000000..129d8d95
--- /dev/null
+++ b/Source/MQTTnet.Benchmarks/UnsubscribeBenchmark.cs
@@ -0,0 +1,61 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+using MQTTnet.Client;
+using MQTTnet.Server;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MQTTnet.Benchmarks
+{
+ [MemoryDiagnoser]
+ public class UnsubscribeBenchmark
+ {
+ MqttServer _mqttServer;
+ MQTTnet.Client.MqttClient _mqttClient;
+
+ const int NumPublishers = 1;
+ const int NumTopicsPerPublisher = 10000;
+
+ List _topics;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out var topicsByPublisher, out var singleWildcardTopicsByPublisher, out var multiWildcardTopicsByPublisher);
+ _topics = topicsByPublisher.Values.First();
+
+ var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
+
+ var factory = new MqttFactory();
+ _mqttServer = factory.CreateMqttServer(serverOptions);
+ _mqttClient = factory.CreateMqttClient();
+
+ _mqttServer.StartAsync().GetAwaiter().GetResult();
+
+ var clientOptions = new MqttClientOptionsBuilder()
+ .WithTcpServer("localhost").Build();
+
+ _mqttClient.ConnectAsync(clientOptions).GetAwaiter().GetResult();
+
+ foreach (var topic in _topics)
+ {
+ var subscribeOptions = new MqttClientSubscribeOptionsBuilder()
+ .WithTopicFilter(topic, Protocol.MqttQualityOfServiceLevel.AtMostOnce)
+ .Build();
+ _mqttClient.SubscribeAsync(subscribeOptions).GetAwaiter().GetResult();
+ }
+ }
+
+ [Benchmark]
+ public void Unsubscribe_10000_Topics()
+ {
+ foreach (var topic in _topics)
+ {
+ var unsubscribeOptions = new MqttClientUnsubscribeOptionsBuilder()
+ .WithTopicFilter(topic)
+ .Build();
+ _mqttClient.UnsubscribeAsync(unsubscribeOptions).GetAwaiter().GetResult();
+ }
+ }
+ }
+}
diff --git a/Tests/MQTTnet.Benchmarks/packages.config b/Source/MQTTnet.Benchmarks/packages.config
similarity index 100%
rename from Tests/MQTTnet.Benchmarks/packages.config
rename to Source/MQTTnet.Benchmarks/packages.config
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageProcessedEventArgs.cs b/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageProcessedEventArgs.cs
index 6eb97d23..2efaa493 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageProcessedEventArgs.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageProcessedEventArgs.cs
@@ -1,8 +1,12 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
namespace MQTTnet.Extensions.ManagedClient
{
- public class ApplicationMessageProcessedEventArgs : EventArgs
+ public sealed class ApplicationMessageProcessedEventArgs : EventArgs
{
public ApplicationMessageProcessedEventArgs(ManagedMqttApplicationMessage applicationMessage, Exception exception)
{
@@ -11,9 +15,10 @@ namespace MQTTnet.Extensions.ManagedClient
}
public ManagedMqttApplicationMessage ApplicationMessage { get; }
+
+ ///
+ /// Then this is _null_ the message was processed successfully without any error.
+ ///
public Exception Exception { get; }
-
- public bool HasFailed => Exception != null;
- public bool HasSucceeded => Exception == null;
}
}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageProcessedHandlerDelegate.cs b/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageProcessedHandlerDelegate.cs
deleted file mode 100644
index e976722e..00000000
--- a/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageProcessedHandlerDelegate.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System;
-using System.Threading.Tasks;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public class ApplicationMessageProcessedHandlerDelegate : IApplicationMessageProcessedHandler
- {
- private readonly Func _handler;
-
- public ApplicationMessageProcessedHandlerDelegate(Action handler)
- {
- if (handler == null) throw new ArgumentNullException(nameof(handler));
-
- _handler = context =>
- {
- handler(context);
- return Task.FromResult(0);
- };
- }
-
- public ApplicationMessageProcessedHandlerDelegate(Func handler)
- {
- _handler = handler ?? throw new ArgumentNullException(nameof(handler));
- }
-
- public Task HandleApplicationMessageProcessedAsync(ApplicationMessageProcessedEventArgs eventArgs)
- {
- return _handler(eventArgs);
- }
- }
-}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageSkippedEventArgs.cs b/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageSkippedEventArgs.cs
index 0f1613d4..7515334c 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageSkippedEventArgs.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageSkippedEventArgs.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
namespace MQTTnet.Extensions.ManagedClient
{
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageSkippedHandlerDelegate.cs b/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageSkippedHandlerDelegate.cs
index 978f30be..c20e8efd 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageSkippedHandlerDelegate.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ApplicationMessageSkippedHandlerDelegate.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ConnectingFailedEventArgs.cs b/Source/MQTTnet.Extensions.ManagedClient/ConnectingFailedEventArgs.cs
new file mode 100644
index 00000000..5fd7bdd7
--- /dev/null
+++ b/Source/MQTTnet.Extensions.ManagedClient/ConnectingFailedEventArgs.cs
@@ -0,0 +1,21 @@
+using System;
+using MQTTnet.Client;
+
+namespace MQTTnet.Extensions.ManagedClient
+{
+ public sealed class ConnectingFailedEventArgs : EventArgs
+ {
+ public ConnectingFailedEventArgs(MqttClientConnectResult connectResult, Exception exception)
+ {
+ ConnectResult = connectResult;
+ Exception = exception;
+ }
+
+ ///
+ /// This is null when the connection was failing and the server was not reachable.
+ ///
+ public MqttClientConnectResult ConnectResult { get; }
+
+ public Exception Exception { get; }
+ }
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ConnectingFailedHandlerDelegate.cs b/Source/MQTTnet.Extensions.ManagedClient/ConnectingFailedHandlerDelegate.cs
deleted file mode 100644
index 85a796b9..00000000
--- a/Source/MQTTnet.Extensions.ManagedClient/ConnectingFailedHandlerDelegate.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System;
-using System.Threading.Tasks;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public class ConnectingFailedHandlerDelegate : IConnectingFailedHandler
- {
- private readonly Func _handler;
-
- public ConnectingFailedHandlerDelegate(Action handler)
- {
- if (handler == null) throw new ArgumentNullException(nameof(handler));
-
- _handler = eventArgs =>
- {
- handler(eventArgs);
- return Task.FromResult(0);
- };
- }
-
- public ConnectingFailedHandlerDelegate(Func handler)
- {
- _handler = handler ?? throw new ArgumentNullException(nameof(handler));
- }
-
- public Task HandleConnectingFailedAsync(ManagedProcessFailedEventArgs eventArgs)
- {
- return _handler(eventArgs);
- }
- }
-}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/IApplicationMessageProcessedHandler.cs b/Source/MQTTnet.Extensions.ManagedClient/IApplicationMessageProcessedHandler.cs
deleted file mode 100644
index 08ed197a..00000000
--- a/Source/MQTTnet.Extensions.ManagedClient/IApplicationMessageProcessedHandler.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Threading.Tasks;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public interface IApplicationMessageProcessedHandler
- {
- Task HandleApplicationMessageProcessedAsync(ApplicationMessageProcessedEventArgs eventArgs);
- }
-}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/IApplicationMessageSkippedHandler.cs b/Source/MQTTnet.Extensions.ManagedClient/IApplicationMessageSkippedHandler.cs
index e664cd2f..fb51f61c 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/IApplicationMessageSkippedHandler.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/IApplicationMessageSkippedHandler.cs
@@ -1,4 +1,8 @@
-using System.Threading.Tasks;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
{
diff --git a/Source/MQTTnet.Extensions.ManagedClient/IConnectingFailedHandler.cs b/Source/MQTTnet.Extensions.ManagedClient/IConnectingFailedHandler.cs
deleted file mode 100644
index aabdbfb0..00000000
--- a/Source/MQTTnet.Extensions.ManagedClient/IConnectingFailedHandler.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Threading.Tasks;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public interface IConnectingFailedHandler
- {
- Task HandleConnectingFailedAsync(ManagedProcessFailedEventArgs eventArgs);
- }
-}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs
deleted file mode 100644
index cc2490cb..00000000
--- a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClient.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using MQTTnet.Client.Connecting;
-using MQTTnet.Client.Disconnecting;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MQTTnet.Client;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public interface IManagedMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
- {
- ///
- /// Gets the internally used MQTT client.
- /// This property should be used with caution because manipulating the internal client might break the managed client.
- ///
- IMqttClient InternalClient { get; }
-
- bool IsStarted { get; }
-
- bool IsConnected { get; }
-
- int PendingApplicationMessagesCount { get; }
-
- IManagedMqttClientOptions Options { get; }
-
- IMqttClientConnectedHandler ConnectedHandler { get; set; }
-
- IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
-
- IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
-
- IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; }
-
- IConnectingFailedHandler ConnectingFailedHandler { get; set; }
-
- ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; }
-
- Task StartAsync(IManagedMqttClientOptions options);
-
- Task StopAsync();
-
- Task PingAsync(CancellationToken cancellationToken);
-
- Task SubscribeAsync(IEnumerable topicFilters);
-
- Task UnsubscribeAsync(IEnumerable topics);
-
- Task PublishAsync(ManagedMqttApplicationMessage applicationMessages);
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientOptions.cs b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientOptions.cs
deleted file mode 100644
index 74028dae..00000000
--- a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientOptions.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-using MQTTnet.Client.Options;
-using MQTTnet.Server;
-
-namespace MQTTnet.Extensions.ManagedClient
-{
- public interface IManagedMqttClientOptions
- {
- IMqttClientOptions ClientOptions { get; }
-
- TimeSpan AutoReconnectDelay { get; }
-
- TimeSpan ConnectionCheckInterval { get; }
-
- IManagedMqttClientStorage Storage { get; }
-
- int MaxPendingMessages { get; }
-
- MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; }
-
- ///
- /// Defines the maximum amount of topic filters which will be sent in a SUBSCRIBE/UNSUBSCRIBE packet.
- /// Amazon AWS limits this number to 8. The default is int.MaxValue.
- ///
- int MaxTopicFiltersInSubscribeUnsubscribePackets { get; }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientStorage.cs b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientStorage.cs
index dc44001a..09bd4105 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientStorage.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/IManagedMqttClientStorage.cs
@@ -1,4 +1,8 @@
-using System.Collections.Generic;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ISynchronizingSubscriptionsFailedHandler.cs b/Source/MQTTnet.Extensions.ManagedClient/ISynchronizingSubscriptionsFailedHandler.cs
index f76f18fb..5287e6a2 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ISynchronizingSubscriptionsFailedHandler.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ISynchronizingSubscriptionsFailedHandler.cs
@@ -1,4 +1,8 @@
-using System.Threading.Tasks;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
{
diff --git a/Source/MQTTnet.Extensions.ManagedClient/MQTTnet.Extensions.ManagedClient.csproj b/Source/MQTTnet.Extensions.ManagedClient/MQTTnet.Extensions.ManagedClient.csproj
index b53859da..f47aea02 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/MQTTnet.Extensions.ManagedClient.csproj
+++ b/Source/MQTTnet.Extensions.ManagedClient/MQTTnet.Extensions.ManagedClient.csproj
@@ -1,40 +1,72 @@
-
- netstandard1.3;netstandard2.0;netstandard2.1;net5.0
- $(TargetFrameworks);net452;net461
- $(TargetFrameworks);uap10.0
-
-
-
-
- False
- false
- false
- true
- true
- snupkg
-
+
+ netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0
+ $(TargetFrameworks);net452;net461
+ $(TargetFrameworks);uap10.0
+
+ MQTTnet.Extensions.ManagedClient
+ MQTTnet.Extensions.ManagedClient
+ True
+ The contributors of MQTTnet
+ MQTTnet
+ This is an extension library which provides a managed MQTT client with additional features using MQTTnet.
+ The contributors of MQTTnet
+ MQTTnet.Extensions.ManagedClient
+ false
+ false
+ true
+ true
+ snupkg
+ Christian Kratky 2016-2022
+ https://github.com/dotnet/MQTTnet
+ https://github.com/dotnet/MQTTnet.git
+ git
+ 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 Blazor
+ en-US
+ false
+ false
+ nuget.png
+ true
+ true
+ LICENSE
+ For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).
+ true
+
-
- false
- UAP,Version=v10.0
- UAP
- 10.0.18362.0
- 10.0.10240.0
- .NETCore
- v5.0
- $(DefineConstants);WINDOWS_UWP
- en
- $(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets
-
+
+ false
+ UAP,Version=v10.0
+ UAP
+ 10.0.18362.0
+ 10.0.10240.0
+ .NETCore
+ v5.0
+ $(DefineConstants);WINDOWS_UWP
+ en
+ $(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets
+
-
-
-
+
+
+ True
+ \
+
+
-
-
-
+
+
+ True
+ \
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessage.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessage.cs
index bba73b0c..7ef38ebd 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessage.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessage.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
namespace MQTTnet.Extensions.ManagedClient
{
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessageBuilder.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessageBuilder.cs
index bf67f70e..96d0a42b 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessageBuilder.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttApplicationMessageBuilder.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
namespace MQTTnet.Extensions.ManagedClient
{
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs
index 6e1c1f54..4d3fed22 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs
@@ -1,162 +1,155 @@
-using MQTTnet.Client;
-using MQTTnet.Client.Connecting;
-using MQTTnet.Client.Disconnecting;
-using MQTTnet.Client.Publishing;
-using MQTTnet.Client.Receiving;
-using MQTTnet.Diagnostics;
-using MQTTnet.Exceptions;
-using MQTTnet.Internal;
-using MQTTnet.Protocol;
-using MQTTnet.Server;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using MQTTnet.Diagnostics.Logger;
+using MQTTnet.Client;
+using MQTTnet.Diagnostics;
+using MQTTnet.Exceptions;
+using MQTTnet.Internal;
+using MQTTnet.Packets;
+using MQTTnet.Protocol;
+using MQTTnet.Server;
+using MqttClient = MQTTnet.Client.MqttClient;
namespace MQTTnet.Extensions.ManagedClient
{
- public class ManagedMqttClient : Disposable, IManagedMqttClient
+ public sealed class ManagedMqttClient : Disposable
{
+ readonly AsyncEvent _applicationMessageProcessedEvent = new AsyncEvent();
+ readonly AsyncEvent _connectingFailedEvent = new AsyncEvent();
+ readonly AsyncEvent _connectionStateChangedEvent = new AsyncEvent();
+
+ readonly MqttNetSourceLogger _logger;
readonly BlockingQueue _messageQueue = new BlockingQueue();
+ readonly AsyncLock _messageQueueLock = new AsyncLock();
+
///
- /// The subscriptions are managed in 2 separate buckets:
- /// and are processed during normal operation
- /// and are moved to the when they get processed. They can be accessed by
- /// any thread and are therefore mutex'ed. get sent to the broker
- /// at reconnect and are solely owned by .
+ /// The subscriptions are managed in 2 separate buckets:
+ ///
+ /// and
+ ///
+ /// are processed during normal operation
+ /// and are moved to the
+ ///
+ /// when they get processed. They can be accessed by
+ /// any thread and are therefore mutex'ed.
+ ///
+ /// get sent to the broker
+ /// at reconnect and are solely owned by
+ ///
+ /// .
///
readonly Dictionary _reconnectSubscriptions = new Dictionary();
readonly Dictionary _subscriptions = new Dictionary();
- readonly HashSet _unsubscriptions = new HashSet();
readonly SemaphoreSlim _subscriptionsQueuedSignal = new SemaphoreSlim(0);
-
- readonly MqttNetSourceLogger _logger;
-
- readonly AsyncLock _messageQueueLock = new AsyncLock();
+ readonly HashSet _unsubscriptions = new HashSet();
CancellationTokenSource _connectionCancellationToken;
- CancellationTokenSource _publishingCancellationToken;
Task _maintainConnectionTask;
+ CancellationTokenSource _publishingCancellationToken;
ManagedMqttClientStorageManager _storageManager;
- public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
+ public ManagedMqttClient(MqttClient mqttClient, IMqttNetLogger logger)
{
InternalClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
- if (logger == null) throw new ArgumentNullException(nameof(logger));
+ if (logger == null)
+ {
+ throw new ArgumentNullException(nameof(logger));
+ }
+
_logger = logger.WithSource(nameof(ManagedMqttClient));
}
+ public event Func ApplicationMessageProcessedAsync
+ {
+ add => _applicationMessageProcessedEvent.AddHandler(value);
+ remove => _applicationMessageProcessedEvent.RemoveHandler(value);
+ }
+
+ public event Func ApplicationMessageReceivedAsync
+ {
+ add => InternalClient.ApplicationMessageReceivedAsync += value;
+ remove => InternalClient.ApplicationMessageReceivedAsync -= value;
+ }
+
+ public event Func ConnectedAsync
+ {
+ add => InternalClient.ConnectedAsync += value;
+ remove => InternalClient.ConnectedAsync -= value;
+ }
+
+ public event Func ConnectingFailedAsync
+ {
+ add => _connectingFailedEvent.AddHandler(value);
+ remove => _connectingFailedEvent.RemoveHandler(value);
+ }
+
+ public event Func ConnectionStateChangedAsync
+ {
+ add => _connectionStateChangedEvent.AddHandler(value);
+ remove => _connectionStateChangedEvent.RemoveHandler(value);
+ }
+
+ public event Func DisconnectedAsync
+ {
+ add => InternalClient.DisconnectedAsync += value;
+ remove => InternalClient.DisconnectedAsync -= value;
+ }
+
+ public IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; }
+
+ public MqttClient InternalClient { get; }
+
public bool IsConnected => InternalClient.IsConnected;
public bool IsStarted => _connectionCancellationToken != null;
- public IMqttClient InternalClient { get; }
+ public ManagedMqttClientOptions Options { get; private set; }
public int PendingApplicationMessagesCount => _messageQueue.Count;
- public IManagedMqttClientOptions Options { get; private set; }
-
- public IMqttClientConnectedHandler ConnectedHandler
- {
- get => InternalClient.ConnectedHandler;
- set => InternalClient.ConnectedHandler = value;
- }
-
- public IMqttClientDisconnectedHandler DisconnectedHandler
- {
- get => InternalClient.DisconnectedHandler;
- set => InternalClient.DisconnectedHandler = value;
- }
-
- public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler
- {
- get => InternalClient.ApplicationMessageReceivedHandler;
- set => InternalClient.ApplicationMessageReceivedHandler = value;
- }
-
- public IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
-
- public IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; }
-
- public IConnectingFailedHandler ConnectingFailedHandler { get; set; }
-
public ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; }
- public async Task StartAsync(IManagedMqttClientOptions options)
+ public async Task EnqueueAsync(MqttApplicationMessage applicationMessage)
{
ThrowIfDisposed();
- if (options == null) throw new ArgumentNullException(nameof(options));
- if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options));
-
- if (!_maintainConnectionTask?.IsCompleted ?? false) throw new InvalidOperationException("The managed client is already started.");
-
- Options = options;
-
- if (options.Storage != null)
+ if (applicationMessage == null)
{
- _storageManager = new ManagedMqttClientStorageManager(options.Storage);
- var messages = await _storageManager.LoadQueuedMessagesAsync().ConfigureAwait(false);
-
- foreach (var message in messages)
- {
- _messageQueue.Enqueue(message);
- }
+ throw new ArgumentNullException(nameof(applicationMessage));
}
- var cancellationTokenSource = new CancellationTokenSource();
- var cancellationToken = cancellationTokenSource.Token;
- _connectionCancellationToken = cancellationTokenSource;
-
- _maintainConnectionTask = Task.Run(() => MaintainConnectionAsync(cancellationToken), cancellationToken);
- _maintainConnectionTask.RunInBackground(_logger);
-
- _logger.Info("Started");
+ var managedMqttApplicationMessage = new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage);
+ await EnqueueAsync(managedMqttApplicationMessage.Build()).ConfigureAwait(false);
}
- public async Task StopAsync()
+ public async Task EnqueueAsync(ManagedMqttApplicationMessage applicationMessage)
{
ThrowIfDisposed();
- StopPublishing();
- StopMaintainingConnection();
-
- _messageQueue.Clear();
-
- if (_maintainConnectionTask != null)
+ if (applicationMessage == null)
{
- await Task.WhenAny(_maintainConnectionTask);
- _maintainConnectionTask = null;
+ throw new ArgumentNullException(nameof(applicationMessage));
}
- }
- public Task PingAsync(CancellationToken cancellationToken)
- {
- return InternalClient.PingAsync(cancellationToken);
- }
-
- public async Task PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken)
- {
- ThrowIfDisposed();
-
- if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
-
- await PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build()).ConfigureAwait(false);
- return new MqttClientPublishResult();
- }
-
- public async Task PublishAsync(ManagedMqttApplicationMessage applicationMessage)
- {
- ThrowIfDisposed();
-
- if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
- if (Options == null) throw new InvalidOperationException("call StartAsync before publishing messages");
+ if (Options == null)
+ {
+ throw new InvalidOperationException("call StartAsync before publishing messages");
+ }
MqttTopicValidator.ThrowIfInvalid(applicationMessage.ApplicationMessage.Topic);
@@ -210,11 +203,77 @@ namespace MQTTnet.Extensions.ManagedClient
}
}
- public Task SubscribeAsync(IEnumerable topicFilters)
+ public Task PingAsync(CancellationToken cancellationToken)
+ {
+ return InternalClient.PingAsync(cancellationToken);
+ }
+
+ public async Task StartAsync(ManagedMqttClientOptions options)
{
ThrowIfDisposed();
- if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ if (options.ClientOptions == null)
+ {
+ throw new ArgumentException("The client options are not set.", nameof(options));
+ }
+
+ if (!_maintainConnectionTask?.IsCompleted ?? false)
+ {
+ throw new InvalidOperationException("The managed client is already started.");
+ }
+
+ Options = options;
+
+ if (options.Storage != null)
+ {
+ _storageManager = new ManagedMqttClientStorageManager(options.Storage);
+ var messages = await _storageManager.LoadQueuedMessagesAsync().ConfigureAwait(false);
+
+ foreach (var message in messages)
+ {
+ _messageQueue.Enqueue(message);
+ }
+ }
+
+ var cancellationTokenSource = new CancellationTokenSource();
+ var cancellationToken = cancellationTokenSource.Token;
+ _connectionCancellationToken = cancellationTokenSource;
+
+ _maintainConnectionTask = Task.Run(() => MaintainConnectionAsync(cancellationToken), cancellationToken);
+ _maintainConnectionTask.RunInBackground(_logger);
+
+ _logger.Info("Started");
+ }
+
+ public async Task StopAsync()
+ {
+ ThrowIfDisposed();
+
+ StopPublishing();
+ StopMaintainingConnection();
+
+ _messageQueue.Clear();
+
+ if (_maintainConnectionTask != null)
+ {
+ await Task.WhenAny(_maintainConnectionTask);
+ _maintainConnectionTask = null;
+ }
+ }
+
+ public Task SubscribeAsync(ICollection topicFilters)
+ {
+ ThrowIfDisposed();
+
+ if (topicFilters == null)
+ {
+ throw new ArgumentNullException(nameof(topicFilters));
+ }
foreach (var topicFilter in topicFilters)
{
@@ -235,11 +294,14 @@ namespace MQTTnet.Extensions.ManagedClient
return Task.FromResult(0);
}
- public Task UnsubscribeAsync(IEnumerable topics)
+ public Task UnsubscribeAsync(ICollection topics)
{
ThrowIfDisposed();
- if (topics == null) throw new ArgumentNullException(nameof(topics));
+ if (topics == null)
+ {
+ throw new ArgumentNullException(nameof(topics));
+ }
lock (_subscriptions)
{
@@ -277,6 +339,23 @@ namespace MQTTnet.Extensions.ManagedClient
base.Dispose(disposing);
}
+ static TimeSpan GetRemainingTime(DateTime endTime)
+ {
+ var remainingTime = endTime - DateTime.UtcNow;
+ return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
+ }
+
+ async Task HandleSubscriptionExceptionAsync(Exception exception)
+ {
+ _logger.Warning(exception, "Synchronizing subscriptions failed.");
+
+ var synchronizingSubscriptionsFailedHandler = SynchronizingSubscriptionsFailedHandler;
+ if (SynchronizingSubscriptionsFailedHandler != null)
+ {
+ await synchronizingSubscriptionsFailedHandler.HandleSynchronizingSubscriptionsFailedAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
+ }
+ }
+
async Task MaintainConnectionAsync(CancellationToken cancellationToken)
{
try
@@ -299,9 +378,9 @@ namespace MQTTnet.Extensions.ManagedClient
{
try
{
- using (var disconnectTimeout = new CancellationTokenSource(Options.ClientOptions.CommunicationTimeout))
+ using (var disconnectTimeout = new CancellationTokenSource(Options.ClientOptions.Timeout))
{
- await InternalClient.DisconnectAsync(disconnectTimeout.Token).ConfigureAwait(false);
+ await InternalClient.DisconnectAsync(new MqttClientDisconnectOptions(), disconnectTimeout.Token).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
@@ -326,49 +405,6 @@ namespace MQTTnet.Extensions.ManagedClient
}
}
- async Task TryMaintainConnectionAsync(CancellationToken cancellationToken)
- {
- try
- {
- var connectionState = await ReconnectIfRequiredAsync(cancellationToken).ConfigureAwait(false);
- if (connectionState == ReconnectionResult.NotConnected)
- {
- StopPublishing();
- await Task.Delay(Options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false);
- return;
- }
-
- if (connectionState == ReconnectionResult.Reconnected)
- {
- await PublishReconnectSubscriptionsAsync().ConfigureAwait(false);
- StartPublishing();
- return;
- }
-
- if (connectionState == ReconnectionResult.Recovered)
- {
- StartPublishing();
- return;
- }
-
- if (connectionState == ReconnectionResult.StillConnected)
- {
- await PublishSubscriptionsAsync(Options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
- }
- }
- catch (OperationCanceledException)
- {
- }
- catch (MqttCommunicationException exception)
- {
- _logger.Warning(exception, "Communication error while maintaining connection.");
- }
- catch (Exception exception)
- {
- _logger.Error(exception, "Error exception while maintaining connection.");
- }
- }
-
async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken)
{
try
@@ -407,6 +443,259 @@ namespace MQTTnet.Extensions.ManagedClient
}
}
+ async Task PublishReconnectSubscriptionsAsync()
+ {
+ _logger.Info("Publishing subscriptions at reconnect");
+
+ try
+ {
+ if (_reconnectSubscriptions.Any())
+ {
+ var subscriptions = _reconnectSubscriptions.Select(
+ i => new MqttTopicFilter
+ {
+ Topic = i.Key,
+ QualityOfServiceLevel = i.Value
+ });
+
+ var topicFilters = new List();
+
+ foreach (var sub in subscriptions)
+ {
+ topicFilters.Add(sub);
+
+ if (topicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
+ {
+ await SendSubscribeUnsubscribe(topicFilters, null).ConfigureAwait(false);
+ topicFilters.Clear();
+ }
+ }
+
+ await SendSubscribeUnsubscribe(topicFilters, null).ConfigureAwait(false);
+ }
+ }
+ catch (Exception exception)
+ {
+ await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
+ }
+ }
+
+ async Task PublishSubscriptionsAsync(TimeSpan timeout, CancellationToken cancellationToken)
+ {
+ var endTime = DateTime.UtcNow + timeout;
+
+ while (await _subscriptionsQueuedSignal.WaitAsync(GetRemainingTime(endTime), cancellationToken).ConfigureAwait(false))
+ {
+ List subscriptions;
+ HashSet unsubscriptions;
+
+ lock (_subscriptions)
+ {
+ subscriptions = _subscriptions.Select(
+ i => new MqttTopicFilter
+ {
+ Topic = i.Key,
+ QualityOfServiceLevel = i.Value
+ })
+ .ToList();
+
+ _subscriptions.Clear();
+
+ unsubscriptions = new HashSet(_unsubscriptions);
+ _unsubscriptions.Clear();
+ }
+
+ if (!subscriptions.Any() && !unsubscriptions.Any())
+ {
+ continue;
+ }
+
+ _logger.Verbose("Publishing {0} added and {1} removed subscriptions", subscriptions.Count, unsubscriptions.Count);
+
+ foreach (var unsubscription in unsubscriptions)
+ {
+ _reconnectSubscriptions.Remove(unsubscription);
+ }
+
+ foreach (var subscription in subscriptions)
+ {
+ _reconnectSubscriptions[subscription.Topic] = subscription.QualityOfServiceLevel;
+ }
+
+ var addedTopicFilters = new List();
+ foreach (var subscription in subscriptions)
+ {
+ addedTopicFilters.Add(subscription);
+
+ if (addedTopicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
+ {
+ await SendSubscribeUnsubscribe(addedTopicFilters, null).ConfigureAwait(false);
+ addedTopicFilters.Clear();
+ }
+ }
+
+ await SendSubscribeUnsubscribe(addedTopicFilters, null).ConfigureAwait(false);
+
+ var removedTopicFilters = new List();
+ foreach (var unSub in unsubscriptions)
+ {
+ removedTopicFilters.Add(unSub);
+
+ if (removedTopicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
+ {
+ await SendSubscribeUnsubscribe(null, removedTopicFilters).ConfigureAwait(false);
+ removedTopicFilters.Clear();
+ }
+ }
+
+ await SendSubscribeUnsubscribe(null, removedTopicFilters).ConfigureAwait(false);
+ }
+ }
+
+ async Task ReconnectIfRequiredAsync(CancellationToken cancellationToken)
+ {
+ if (InternalClient.IsConnected)
+ {
+ return ReconnectionResult.StillConnected;
+ }
+
+ MqttClientConnectResult connectResult = null;
+ try
+ {
+ using (var connectTimeout = new CancellationTokenSource(Options.ClientOptions.Timeout))
+ {
+ connectResult = await InternalClient.ConnectAsync(Options.ClientOptions, connectTimeout.Token).ConfigureAwait(false);
+ }
+
+ if (connectResult.ResultCode != MqttClientConnectResultCode.Success)
+ {
+ throw new MqttCommunicationException($"Client connected but server denied connection with reason '{connectResult.ResultCode}'.");
+ }
+
+ return connectResult.IsSessionPresent ? ReconnectionResult.Recovered : ReconnectionResult.Reconnected;
+ }
+ catch (Exception exception)
+ {
+ await _connectingFailedEvent.InvokeAsync(new ConnectingFailedEventArgs(connectResult, exception));
+ return ReconnectionResult.NotConnected;
+ }
+ }
+
+ async Task SendSubscribeUnsubscribe(List addedSubscriptions, List removedSubscriptions)
+ {
+ try
+ {
+ if (removedSubscriptions != null && removedSubscriptions.Any())
+ {
+ var unsubscribeOptionsBuilder = new MqttClientUnsubscribeOptionsBuilder();
+
+ foreach (var removedSubscription in removedSubscriptions)
+ {
+ unsubscribeOptionsBuilder.WithTopicFilter(removedSubscription);
+ }
+
+ await InternalClient.UnsubscribeAsync(unsubscribeOptionsBuilder.Build()).ConfigureAwait(false);
+ }
+
+ if (addedSubscriptions != null && addedSubscriptions.Any())
+ {
+ var subscribeOptionsBuilder = new MqttClientSubscribeOptionsBuilder();
+
+ foreach (var addedSubscription in addedSubscriptions)
+ {
+ subscribeOptionsBuilder.WithTopicFilter(addedSubscription);
+ }
+
+ await InternalClient.SubscribeAsync(subscribeOptionsBuilder.Build()).ConfigureAwait(false);
+ }
+ }
+ catch (Exception exception)
+ {
+ await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
+ }
+ }
+
+ void StartPublishing()
+ {
+ StopPublishing();
+
+ var cancellationTokenSource = new CancellationTokenSource();
+ var cancellationToken = cancellationTokenSource.Token;
+ _publishingCancellationToken = cancellationTokenSource;
+
+ Task.Run(() => PublishQueuedMessagesAsync(cancellationToken), cancellationToken).RunInBackground(_logger);
+ }
+
+ void StopMaintainingConnection()
+ {
+ try
+ {
+ _connectionCancellationToken?.Cancel(false);
+ }
+ finally
+ {
+ _connectionCancellationToken?.Dispose();
+ _connectionCancellationToken = null;
+ }
+ }
+
+ void StopPublishing()
+ {
+ try
+ {
+ _publishingCancellationToken?.Cancel(false);
+ }
+ finally
+ {
+ _publishingCancellationToken?.Dispose();
+ _publishingCancellationToken = null;
+ }
+ }
+
+ async Task TryMaintainConnectionAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ var oldConnectionState = InternalClient.IsConnected;
+ var connectionState = await ReconnectIfRequiredAsync(cancellationToken).ConfigureAwait(false);
+
+ if (connectionState == ReconnectionResult.NotConnected)
+ {
+ StopPublishing();
+ await Task.Delay(Options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false);
+ }
+ else if (connectionState == ReconnectionResult.Reconnected)
+ {
+ await PublishReconnectSubscriptionsAsync().ConfigureAwait(false);
+ StartPublishing();
+ }
+ else if (connectionState == ReconnectionResult.Recovered)
+ {
+ StartPublishing();
+ }
+ else if (connectionState == ReconnectionResult.StillConnected)
+ {
+ await PublishSubscriptionsAsync(Options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
+ }
+
+ if (oldConnectionState != InternalClient.IsConnected)
+ {
+ await _connectionStateChangedEvent.InvokeAsync(EventArgs.Empty).ConfigureAwait(false);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (MqttCommunicationException exception)
+ {
+ _logger.Warning(exception, "Communication error while maintaining connection.");
+ }
+ catch (Exception exception)
+ {
+ _logger.Error(exception, "Error exception while maintaining connection.");
+ }
+ }
+
async Task TryPublishQueuedMessageAsync(ManagedMqttApplicationMessage message)
{
Exception transmitException = null;
@@ -463,220 +752,12 @@ namespace MQTTnet.Extensions.ManagedClient
}
finally
{
- var eventHandler = ApplicationMessageProcessedHandler;
- if (eventHandler != null)
+ if (_applicationMessageProcessedEvent.HasHandlers)
{
- var eventArguments = new ApplicationMessageProcessedEventArgs(message, transmitException);
- await eventHandler.HandleApplicationMessageProcessedAsync(eventArguments).ConfigureAwait(false);
+ var eventArgs = new ApplicationMessageProcessedEventArgs(message, transmitException);
+ await _applicationMessageProcessedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
}
}
}
-
- async Task PublishSubscriptionsAsync(TimeSpan timeout, CancellationToken cancellationToken)
- {
- var endTime = DateTime.UtcNow + timeout;
-
- while (await _subscriptionsQueuedSignal.WaitAsync(GetRemainingTime(endTime), cancellationToken).ConfigureAwait(false))
- {
- List subscriptions;
- HashSet unsubscriptions;
-
- lock (_subscriptions)
- {
- subscriptions = _subscriptions.Select(i => new MqttTopicFilter
- {
- Topic = i.Key,
- QualityOfServiceLevel = i.Value
- }).ToList();
-
- _subscriptions.Clear();
-
- unsubscriptions = new HashSet(_unsubscriptions);
- _unsubscriptions.Clear();
- }
-
- if (!subscriptions.Any() && !unsubscriptions.Any())
- {
- continue;
- }
-
- _logger.Verbose("Publishing {0} added and {1} removed subscriptions", subscriptions.Count, unsubscriptions.Count);
-
- foreach (var unsubscription in unsubscriptions)
- {
- _reconnectSubscriptions.Remove(unsubscription);
- }
-
- foreach (var subscription in subscriptions)
- {
- _reconnectSubscriptions[subscription.Topic] = subscription.QualityOfServiceLevel;
- }
-
- var addedTopicFilters = new List();
- foreach (var subscription in subscriptions)
- {
- addedTopicFilters.Add(subscription);
-
- if (addedTopicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
- {
- await SendSubscribeUnsubscribe(addedTopicFilters, null).ConfigureAwait(false);
- addedTopicFilters.Clear();
- }
- }
-
- await SendSubscribeUnsubscribe(addedTopicFilters, null).ConfigureAwait(false);
-
- var removedTopicFilters = new List();
- foreach (var unSub in unsubscriptions)
- {
- removedTopicFilters.Add(unSub);
-
- if (removedTopicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
- {
- await SendSubscribeUnsubscribe(null, removedTopicFilters).ConfigureAwait(false);
- removedTopicFilters.Clear();
- }
- }
-
- await SendSubscribeUnsubscribe(null, removedTopicFilters).ConfigureAwait(false);
- }
- }
-
- async Task SendSubscribeUnsubscribe(List addedSubscriptions, List removedSubscriptions)
- {
- try
- {
- if (removedSubscriptions != null && removedSubscriptions.Any())
- {
- await InternalClient.UnsubscribeAsync(removedSubscriptions.ToArray()).ConfigureAwait(false);
- }
-
- if (addedSubscriptions != null && addedSubscriptions.Any())
- {
- await InternalClient.SubscribeAsync(addedSubscriptions.ToArray()).ConfigureAwait(false);
- }
- }
- catch (Exception exception)
- {
- await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
- }
- }
-
- async Task PublishReconnectSubscriptionsAsync()
- {
- _logger.Info("Publishing subscriptions at reconnect");
-
- try
- {
- if (_reconnectSubscriptions.Any())
- {
- var subscriptions = _reconnectSubscriptions.Select(i => new MqttTopicFilter
- {
- Topic = i.Key,
- QualityOfServiceLevel = i.Value
- });
-
- var topicFilters = new List();
-
- foreach (var sub in subscriptions)
- {
- topicFilters.Add(sub);
-
- if (topicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
- {
- await SendSubscribeUnsubscribe(topicFilters, null).ConfigureAwait(false);
- topicFilters.Clear();
- }
- }
-
- await SendSubscribeUnsubscribe(topicFilters, null).ConfigureAwait(false);
- }
- }
- catch (Exception exception)
- {
- await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
- }
- }
-
- async Task HandleSubscriptionExceptionAsync(Exception exception)
- {
- _logger.Warning(exception, "Synchronizing subscriptions failed.");
-
- var synchronizingSubscriptionsFailedHandler = SynchronizingSubscriptionsFailedHandler;
- if (SynchronizingSubscriptionsFailedHandler != null)
- {
- await synchronizingSubscriptionsFailedHandler.HandleSynchronizingSubscriptionsFailedAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
- }
- }
-
- async Task ReconnectIfRequiredAsync(CancellationToken cancellationToken)
- {
- if (InternalClient.IsConnected)
- {
- return ReconnectionResult.StillConnected;
- }
-
- try
- {
- var result = await InternalClient.ConnectAsync(Options.ClientOptions, cancellationToken).ConfigureAwait(false);
- return result.IsSessionPresent ? ReconnectionResult.Recovered : ReconnectionResult.Reconnected;
- }
- catch (Exception exception)
- {
- var connectingFailedHandler = ConnectingFailedHandler;
- if (connectingFailedHandler != null)
- {
- await connectingFailedHandler.HandleConnectingFailedAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
- }
-
- return ReconnectionResult.NotConnected;
- }
- }
-
- void StartPublishing()
- {
- if (_publishingCancellationToken != null)
- {
- StopPublishing();
- }
-
- var cancellationTokenSource = new CancellationTokenSource();
- var cancellationToken = cancellationTokenSource.Token;
- _publishingCancellationToken = cancellationTokenSource;
-
- Task.Run(() => PublishQueuedMessagesAsync(cancellationToken), cancellationToken).RunInBackground(_logger);
- }
-
- void StopPublishing()
- {
- try
- {
- _publishingCancellationToken?.Cancel(false);
- }
- finally
- {
- _publishingCancellationToken?.Dispose();
- _publishingCancellationToken = null;
- }
- }
-
- void StopMaintainingConnection()
- {
- try
- {
- _connectionCancellationToken?.Cancel(false);
- }
- finally
- {
- _connectionCancellationToken?.Dispose();
- _connectionCancellationToken = null;
- }
- }
-
- static TimeSpan GetRemainingTime(DateTime endTime)
- {
- var remainingTime = endTime - DateTime.UtcNow;
- return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
- }
}
}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientExtensions.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientExtensions.cs
index d92e5c25..d14538cb 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientExtensions.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientExtensions.cs
@@ -1,232 +1,78 @@
-using MQTTnet.Client.Connecting;
-using MQTTnet.Client.Disconnecting;
-using MQTTnet.Client.Publishing;
-using MQTTnet.Client.Receiving;
-using MQTTnet.Protocol;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
using System;
using System.Collections.Generic;
-using System.Threading;
using System.Threading.Tasks;
+using MQTTnet.Packets;
+using MQTTnet.Protocol;
namespace MQTTnet.Extensions.ManagedClient
{
public static class ManagedMqttClientExtensions
{
- public static IManagedMqttClient UseConnectedHandler(this IManagedMqttClient client, Func handler)
+ public static Task EnqueueAsync(
+ this ManagedMqttClient managedMqttClient,
+ string topic,
+ string payload = null,
+ MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
+ bool retain = false)
{
- if (client == null) throw new ArgumentNullException(nameof(client));
-
- if (handler == null)
+ if (managedMqttClient == null)
{
- return client.UseConnectedHandler((IMqttClientConnectedHandler)null);
+ throw new ArgumentNullException(nameof(managedMqttClient));
}
- return client.UseConnectedHandler(new MqttClientConnectedHandlerDelegate(handler));
- }
-
- public static IManagedMqttClient UseConnectedHandler(this IManagedMqttClient client, Action handler)
- {
- if (client == null) throw new ArgumentNullException(nameof(client));
-
- if (handler == null)
+ if (topic == null)
{
- return client.UseConnectedHandler((IMqttClientConnectedHandler)null);
+ throw new ArgumentNullException(nameof(topic));
}
- return client.UseConnectedHandler(new MqttClientConnectedHandlerDelegate(handler));
- }
-
- public static IManagedMqttClient UseConnectedHandler(this IManagedMqttClient client, IMqttClientConnectedHandler handler)
- {
- if (client == null) throw new ArgumentNullException(nameof(client));
-
- client.ConnectedHandler = handler;
- return client;
- }
-
- public static IManagedMqttClient UseDisconnectedHandler(this IManagedMqttClient client, Func handler)
- {
- if (client == null) throw new ArgumentNullException(nameof(client));
-
- if (handler == null)
- {
- return client.UseDisconnectedHandler((IMqttClientDisconnectedHandler)null);
- }
-
- return client.UseDisconnectedHandler(new MqttClientDisconnectedHandlerDelegate(handler));
- }
-
- public static IManagedMqttClient UseDisconnectedHandler(this IManagedMqttClient client, Action handler)
- {
- if (client == null) throw new ArgumentNullException(nameof(client));
-
- if (handler == null)
- {
- return client.UseDisconnectedHandler((IMqttClientDisconnectedHandler)null);
- }
-
- return client.UseDisconnectedHandler(new MqttClientDisconnectedHandlerDelegate(handler));
- }
-
- public static IManagedMqttClient UseDisconnectedHandler(this IManagedMqttClient client, IMqttClientDisconnectedHandler handler)
- {
- if (client == null) throw new ArgumentNullException(nameof(client));
-
- client.DisconnectedHandler = handler;
- return client;
- }
-
- public static TReceiver UseApplicationMessageReceivedHandler(this TReceiver receiver, Func handler)
- where TReceiver : IApplicationMessageReceiver
- {
- if (receiver == null) throw new ArgumentNullException(nameof(receiver));
-
- if (handler == null)
- {
- return receiver.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
- }
-
- return receiver.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
- }
-
- public static TReceiver UseApplicationMessageReceivedHandler(this TReceiver receiver, Action handler)
- where TReceiver : IApplicationMessageReceiver
- {
- if (receiver == null) throw new ArgumentNullException(nameof(receiver));
-
- if (handler == null)
- {
- return receiver.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
- }
-
- return receiver.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
- }
-
- public static TReceiver UseApplicationMessageReceivedHandler(this TReceiver receiver, IMqttApplicationMessageReceivedHandler handler)
- where TReceiver : IApplicationMessageReceiver
- {
- if (receiver == null) throw new ArgumentNullException(nameof(receiver));
-
- receiver.ApplicationMessageReceivedHandler = handler;
- return receiver;
- }
-
- public static Task SubscribeAsync(this IManagedMqttClient client, params MqttTopicFilter[] topicFilters)
- {
- if (client == null) throw new ArgumentNullException(nameof(client));
-
- return client.SubscribeAsync(topicFilters);
- }
-
- public static Task SubscribeAsync(this IManagedMqttClient 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 MqttTopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
- }
-
- public static Task SubscribeAsync(this IManagedMqttClient client, string topic)
- {
- if (client == null) throw new ArgumentNullException(nameof(client));
- if (topic == null) throw new ArgumentNullException(nameof(topic));
-
- return client.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(topic).Build());
- }
-
- public static Task UnsubscribeAsync(this IManagedMqttClient client, params string[] topicFilters)
- {
- if (client == null) throw new ArgumentNullException(nameof(client));
-
- return client.UnsubscribeAsync(topicFilters);
- }
-
- public static async Task PublishAsync(this IApplicationMessagePublisher publisher, IEnumerable applicationMessages)
- {
- if (publisher == null) throw new ArgumentNullException(nameof(publisher));
- if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
-
- foreach (var applicationMessage in applicationMessages)
- {
- await publisher.PublishAsync(applicationMessage).ConfigureAwait(false);
- }
- }
-
- public static Task PublishAsync(this IApplicationMessagePublisher publisher, MqttApplicationMessage applicationMessage)
- {
- if (publisher == null) throw new ArgumentNullException(nameof(publisher));
- if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
-
- return publisher.PublishAsync(applicationMessage, CancellationToken.None);
- }
-
- public static async Task PublishAsync(this IApplicationMessagePublisher publisher, params MqttApplicationMessage[] applicationMessages)
- {
- if (publisher == null) throw new ArgumentNullException(nameof(publisher));
- if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
-
- foreach (var applicationMessage in applicationMessages)
- {
- await publisher.PublishAsync(applicationMessage, CancellationToken.None).ConfigureAwait(false);
- }
- }
-
- 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(builder => builder
- .WithTopic(topic));
- }
-
- 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(builder => builder
- .WithTopic(topic)
- .WithPayload(payload));
- }
-
- 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(builder => builder
- .WithTopic(topic)
- .WithPayload(payload)
- .WithQualityOfServiceLevel(qualityOfServiceLevel));
- }
-
- public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
- {
- if (publisher == null) throw new ArgumentNullException(nameof(publisher));
- if (topic == null) throw new ArgumentNullException(nameof(topic));
-
- return publisher.PublishAsync(builder => builder
- .WithTopic(topic)
+ var applicationMessage = new MqttApplicationMessageBuilder().WithTopic(topic)
.WithPayload(payload)
+ .WithRetainFlag(retain)
.WithQualityOfServiceLevel(qualityOfServiceLevel)
- .WithRetainFlag(retain));
+ .Build();
+
+ return managedMqttClient.EnqueueAsync(applicationMessage);
}
- public static Task PublishAsync(this IApplicationMessagePublisher publisher, Func builder, CancellationToken cancellationToken)
+ public static Task SubscribeAsync(
+ this ManagedMqttClient managedMqttClient,
+ string topic,
+ MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce)
{
- if (publisher == null) throw new ArgumentNullException(nameof(publisher));
+ if (managedMqttClient == null)
+ {
+ throw new ArgumentNullException(nameof(managedMqttClient));
+ }
- var message = builder(new MqttApplicationMessageBuilder()).Build();
- return publisher.PublishAsync(message, cancellationToken);
+ if (topic == null)
+ {
+ throw new ArgumentNullException(nameof(topic));
+ }
+
+ return managedMqttClient.SubscribeAsync(
+ new List
+ {
+ new MqttTopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build()
+ });
}
- public static Task PublishAsync(this IApplicationMessagePublisher publisher, Func builder)
+ public static Task UnsubscribeAsync(this ManagedMqttClient managedMqttClient, string topic)
{
- if (publisher == null) throw new ArgumentNullException(nameof(publisher));
+ if (managedMqttClient == null)
+ {
+ throw new ArgumentNullException(nameof(managedMqttClient));
+ }
- var message = builder(new MqttApplicationMessageBuilder()).Build();
- return publisher.PublishAsync(message, CancellationToken.None);
+ if (topic == null)
+ {
+ throw new ArgumentNullException(nameof(topic));
+ }
+
+ return managedMqttClient.UnsubscribeAsync(new List { topic });
}
}
-}
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptions.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptions.cs
index f948055e..dcd0c119 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptions.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptions.cs
@@ -1,12 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
using System;
-using MQTTnet.Client.Options;
+using MQTTnet.Client;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
{
- public sealed class ManagedMqttClientOptions : IManagedMqttClientOptions
+ public sealed class ManagedMqttClientOptions
{
- public IMqttClientOptions ClientOptions { get; set; }
+ public MqttClientOptions ClientOptions { get; set; }
public TimeSpan AutoReconnectDelay { get; set; } = TimeSpan.FromSeconds(5);
@@ -18,6 +22,10 @@ namespace MQTTnet.Extensions.ManagedClient
public MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; set; } = MqttPendingMessagesOverflowStrategy.DropNewMessage;
+ ///
+ /// Defines the maximum amount of topic filters which will be sent in a SUBSCRIBE/UNSUBSCRIBE packet.
+ /// Amazon AWS limits this number to 8. The default is int.MaxValue.
+ ///
public int MaxTopicFiltersInSubscribeUnsubscribePackets { get; set; } = int.MaxValue;
}
}
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptionsBuilder.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptionsBuilder.cs
index dec105ff..72c192b5 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptionsBuilder.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientOptionsBuilder.cs
@@ -1,5 +1,9 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
using System;
-using MQTTnet.Client.Options;
+using MQTTnet.Client;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
@@ -33,7 +37,7 @@ namespace MQTTnet.Extensions.ManagedClient
return this;
}
- public ManagedMqttClientOptionsBuilder WithClientOptions(IMqttClientOptions value)
+ public ManagedMqttClientOptionsBuilder WithClientOptions(MqttClientOptions value)
{
if (_clientOptionsBuilder != null)
{
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientStorageManager.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientStorageManager.cs
index 772f102e..1b812d8d 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientStorageManager.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClientStorageManager.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedProcessFailedEventArgs.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedProcessFailedEventArgs.cs
index bb0cf683..73eee45e 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedProcessFailedEventArgs.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ManagedProcessFailedEventArgs.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
namespace MQTTnet.Extensions.ManagedClient
{
diff --git a/Source/MQTTnet.Extensions.ManagedClient/MqttFactoryExtensions.cs b/Source/MQTTnet.Extensions.ManagedClient/MqttFactoryExtensions.cs
index 10256c58..be9ae2a8 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/MqttFactoryExtensions.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/MqttFactoryExtensions.cs
@@ -1,18 +1,28 @@
-using System;
-using MQTTnet.Diagnostics.Logger;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using MQTTnet.Client;
+using MQTTnet.Diagnostics;
namespace MQTTnet.Extensions.ManagedClient
{
public static class MqttFactoryExtensions
{
- public static IManagedMqttClient CreateManagedMqttClient(this IMqttFactory factory)
+ public static ManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, MqttClient mqttClient = null)
{
if (factory == null) throw new ArgumentNullException(nameof(factory));
- return new ManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger);
+ if (mqttClient == null)
+ {
+ return new ManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger);
+ }
+
+ return new ManagedMqttClient(mqttClient, factory.DefaultLogger);
}
-
- public static IManagedMqttClient CreateManagedMqttClient(this IMqttFactory factory, IMqttNetLogger logger)
+
+ public static ManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttNetLogger logger)
{
if (factory == null) throw new ArgumentNullException(nameof(factory));
if (logger == null) throw new ArgumentNullException(nameof(logger));
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ReconnectionResult.cs b/Source/MQTTnet.Extensions.ManagedClient/ReconnectionResult.cs
index 092662f4..9bee012e 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ReconnectionResult.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ReconnectionResult.cs
@@ -1,4 +1,8 @@
-namespace MQTTnet.Extensions.ManagedClient
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace MQTTnet.Extensions.ManagedClient
{
public enum ReconnectionResult
{
diff --git a/Source/MQTTnet.Extensions.ManagedClient/SynchronizingSubscriptionsFailedHandlerDelegate.cs b/Source/MQTTnet.Extensions.ManagedClient/SynchronizingSubscriptionsFailedHandlerDelegate.cs
index 72d6e4a0..2a4b6b4c 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/SynchronizingSubscriptionsFailedHandlerDelegate.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/SynchronizingSubscriptionsFailedHandlerDelegate.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
diff --git a/Source/MQTTnet.Extensions.Rpc/IMqttRpcClient.cs b/Source/MQTTnet.Extensions.Rpc/IMqttRpcClient.cs
deleted file mode 100644
index 2941fe8e..00000000
--- a/Source/MQTTnet.Extensions.Rpc/IMqttRpcClient.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using MQTTnet.Protocol;
-
-namespace MQTTnet.Extensions.Rpc
-{
- public interface IMqttRpcClient : IDisposable
- {
- Task ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel);
-
- Task ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken);
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj b/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj
index b53859da..6f36766d 100644
--- a/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj
+++ b/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj
@@ -1,40 +1,72 @@
-
- netstandard1.3;netstandard2.0;netstandard2.1;net5.0
- $(TargetFrameworks);net452;net461
- $(TargetFrameworks);uap10.0
-
-
-
-
- False
- false
- false
- true
- true
- snupkg
-
+
+ netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0
+ $(TargetFrameworks);net452;net461
+ $(TargetFrameworks);uap10.0
+
+ MQTTnet.Extensions.Rpc
+ MQTTnet.Extensions.Rpc
+ True
+ The contributors of MQTTnet
+ MQTTnet
+ This is an extension library which allows executing synchronous device calls including a response using MQTTnet.
+ The contributors of MQTTnet
+ MQTTnet.Extensions.Rpc
+ false
+ false
+ true
+ true
+ snupkg
+ Christian Kratky 2016-2022
+ https://github.com/dotnet/MQTTnet
+ https://github.com/dotnet/MQTTnet.git
+ git
+ 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 Blazor
+ en-US
+ false
+ false
+ nuget.png
+ true
+ true
+ LICENSE
+ For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).
+ true
+
-
- false
- UAP,Version=v10.0
- UAP
- 10.0.18362.0
- 10.0.10240.0
- .NETCore
- v5.0
- $(DefineConstants);WINDOWS_UWP
- en
- $(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets
-
+
+ false
+ UAP,Version=v10.0
+ UAP
+ 10.0.18362.0
+ 10.0.10240.0
+ .NETCore
+ v5.0
+ $(DefineConstants);WINDOWS_UWP
+ en
+ $(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets
+
-
-
-
+
+
+ True
+ \
+
+
-
-
-
+
+
+ True
+ \
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj.DotSettings b/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj.DotSettings
new file mode 100644
index 00000000..1b491ba9
--- /dev/null
+++ b/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj.DotSettings
@@ -0,0 +1,3 @@
+
+ True
+ True
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.Rpc/MqttFactoryExtensions.cs b/Source/MQTTnet.Extensions.Rpc/MqttFactoryExtensions.cs
new file mode 100644
index 00000000..42028cc0
--- /dev/null
+++ b/Source/MQTTnet.Extensions.Rpc/MqttFactoryExtensions.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using MQTTnet.Client;
+
+namespace MQTTnet.Extensions.Rpc
+{
+ public static class MqttFactoryExtensions
+ {
+ public static MqttRpcClient CreateMqttRpcClient(this MqttFactory factory, MqttClient mqttClient)
+ {
+ return factory.CreateMqttRpcClient(mqttClient, new MqttRpcClientOptions
+ {
+ TopicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy()
+ });
+ }
+
+ public static MqttRpcClient CreateMqttRpcClient(this MqttFactory factory, MqttClient mqttClient, MqttRpcClientOptions rpcClientOptions)
+ {
+ if (factory == null) throw new ArgumentNullException(nameof(factory));
+
+ if (mqttClient == null)
+ {
+ throw new ArgumentNullException(nameof(mqttClient));
+ }
+
+ if (rpcClientOptions == null)
+ {
+ throw new ArgumentNullException(nameof(rpcClientOptions));
+ }
+
+ return new MqttRpcClient(mqttClient, rpcClientOptions);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.Rpc/MqttRpcClient.cs b/Source/MQTTnet.Extensions.Rpc/MqttRpcClient.cs
index 7a1cfef1..cf0a6f7a 100644
--- a/Source/MQTTnet.Extensions.Rpc/MqttRpcClient.cs
+++ b/Source/MQTTnet.Extensions.Rpc/MqttRpcClient.cs
@@ -1,39 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
using MQTTnet.Client;
using MQTTnet.Exceptions;
-using MQTTnet.Extensions.Rpc.Options;
-using MQTTnet.Extensions.Rpc.Options.TopicGeneration;
using MQTTnet.Protocol;
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
-using MQTTnet.Client.Subscribing;
using MQTTnet.Implementations;
namespace MQTTnet.Extensions.Rpc
{
- public sealed class MqttRpcClient : IMqttRpcClient
+ public sealed class MqttRpcClient : IDisposable
{
readonly ConcurrentDictionary> _waitingCalls = new ConcurrentDictionary>();
- readonly IMqttClient _mqttClient;
- readonly IMqttRpcClientOptions _options;
- readonly RpcAwareApplicationMessageReceivedHandler _applicationMessageReceivedHandler;
-
- [Obsolete("Use MqttRpcClient(IMqttClient mqttClient, IMqttRpcClientOptions options).")]
- public MqttRpcClient(IMqttClient mqttClient) : this(mqttClient, new MqttRpcClientOptions())
- {
- }
-
- public MqttRpcClient(IMqttClient mqttClient, IMqttRpcClientOptions options)
+ readonly MqttClient _mqttClient;
+ readonly MqttRpcClientOptions _options;
+
+ public MqttRpcClient(MqttClient mqttClient, MqttRpcClientOptions options)
{
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
_options = options ?? throw new ArgumentNullException(nameof(options));
- _applicationMessageReceivedHandler = new RpcAwareApplicationMessageReceivedHandler(
- mqttClient.ApplicationMessageReceivedHandler,
- HandleApplicationMessageReceivedAsync);
-
- _mqttClient.ApplicationMessageReceivedHandler = _applicationMessageReceivedHandler;
+ _mqttClient.ApplicationMessageReceivedAsync += HandleApplicationMessageReceivedAsync;
}
public async Task ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
@@ -59,12 +50,7 @@ namespace MQTTnet.Extensions.Rpc
public async Task ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken)
{
if (methodName == null) throw new ArgumentNullException(nameof(methodName));
-
- if (!(_mqttClient.ApplicationMessageReceivedHandler is RpcAwareApplicationMessageReceivedHandler))
- {
- throw new InvalidOperationException("The application message received handler was modified.");
- }
-
+
var topicNames = _options.TopicGenerationStrategy.CreateRpcTopics(new TopicGenerationContext
{
MethodName = methodName,
@@ -121,7 +107,7 @@ namespace MQTTnet.Extensions.Rpc
finally
{
_waitingCalls.TryRemove(responseTopic, out _);
-
+
await _mqttClient.UnsubscribeAsync(responseTopic).ConfigureAwait(false);
}
}
@@ -147,7 +133,7 @@ namespace MQTTnet.Extensions.Rpc
public void Dispose()
{
- _mqttClient.ApplicationMessageReceivedHandler = _applicationMessageReceivedHandler.OriginalHandler;
+ _mqttClient.ApplicationMessageReceivedAsync -= HandleApplicationMessageReceivedAsync;
foreach (var tcs in _waitingCalls)
{
diff --git a/Source/MQTTnet.Extensions.Rpc/MqttRpcClientExtensions.cs b/Source/MQTTnet.Extensions.Rpc/MqttRpcClientExtensions.cs
index 005c774f..86fd1ef7 100644
--- a/Source/MQTTnet.Extensions.Rpc/MqttRpcClientExtensions.cs
+++ b/Source/MQTTnet.Extensions.Rpc/MqttRpcClientExtensions.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Text;
using System.Threading.Tasks;
using MQTTnet.Protocol;
@@ -7,7 +11,7 @@ namespace MQTTnet.Extensions.Rpc
{
public static class MqttRpcClientExtensions
{
- public static Task ExecuteAsync(this IMqttRpcClient client, TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
+ public static Task ExecuteAsync(this MqttRpcClient client, TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
if (client == null) throw new ArgumentNullException(nameof(client));
diff --git a/Source/MQTTnet.Extensions.Rpc/Options/IMqttRpcClientOptions.cs b/Source/MQTTnet.Extensions.Rpc/Options/IMqttRpcClientOptions.cs
deleted file mode 100644
index 6b7afa90..00000000
--- a/Source/MQTTnet.Extensions.Rpc/Options/IMqttRpcClientOptions.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using MQTTnet.Extensions.Rpc.Options.TopicGeneration;
-
-namespace MQTTnet.Extensions.Rpc.Options
-{
- public interface IMqttRpcClientOptions
- {
- IMqttRpcClientTopicGenerationStrategy TopicGenerationStrategy { get; set; }
- }
-}
\ No newline at end of file
diff --git a/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcClientOptions.cs b/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcClientOptions.cs
index 617653ab..bb891705 100644
--- a/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcClientOptions.cs
+++ b/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcClientOptions.cs
@@ -1,8 +1,10 @@
-using MQTTnet.Extensions.Rpc.Options.TopicGeneration;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
-namespace MQTTnet.Extensions.Rpc.Options
+namespace MQTTnet.Extensions.Rpc
{
- public sealed class MqttRpcClientOptions : IMqttRpcClientOptions
+ public sealed class MqttRpcClientOptions
{
public IMqttRpcClientTopicGenerationStrategy TopicGenerationStrategy { get; set; } = new DefaultMqttRpcClientTopicGenerationStrategy();
}
diff --git a/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcClientOptionsBuilder.cs b/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcClientOptionsBuilder.cs
index 9f0e986f..dc53ddc0 100644
--- a/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcClientOptionsBuilder.cs
+++ b/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcClientOptionsBuilder.cs
@@ -1,7 +1,10 @@
-using MQTTnet.Extensions.Rpc.Options.TopicGeneration;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
using System;
-namespace MQTTnet.Extensions.Rpc.Options
+namespace MQTTnet.Extensions.Rpc
{
public sealed class MqttRpcClientOptionsBuilder
{
@@ -14,7 +17,7 @@ namespace MQTTnet.Extensions.Rpc.Options
return this;
}
- public IMqttRpcClientOptions Build()
+ public MqttRpcClientOptions Build()
{
return new MqttRpcClientOptions
{
diff --git a/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcTopicPair.cs b/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcTopicPair.cs
index 56f768ae..4cbb1660 100644
--- a/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcTopicPair.cs
+++ b/Source/MQTTnet.Extensions.Rpc/Options/MqttRpcTopicPair.cs
@@ -1,4 +1,8 @@
-namespace MQTTnet.Extensions.Rpc.Options
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace MQTTnet.Extensions.Rpc
{
public sealed class MqttRpcTopicPair
{
diff --git a/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/DefaultMqttRpcClientTopicGenerationStrategy.cs b/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/DefaultMqttRpcClientTopicGenerationStrategy.cs
index 39bbde7f..11c087d8 100644
--- a/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/DefaultMqttRpcClientTopicGenerationStrategy.cs
+++ b/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/DefaultMqttRpcClientTopicGenerationStrategy.cs
@@ -1,6 +1,10 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
-namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
+using System;
+
+namespace MQTTnet.Extensions.Rpc
{
public sealed class DefaultMqttRpcClientTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
{
diff --git a/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/IMqttRpcClientTopicGenerationStrategy.cs b/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/IMqttRpcClientTopicGenerationStrategy.cs
index 19c78d3e..487daa13 100644
--- a/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/IMqttRpcClientTopicGenerationStrategy.cs
+++ b/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/IMqttRpcClientTopicGenerationStrategy.cs
@@ -1,4 +1,8 @@
-namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace MQTTnet.Extensions.Rpc
{
public interface IMqttRpcClientTopicGenerationStrategy
{
diff --git a/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/TopicGenerationContext.cs b/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/TopicGenerationContext.cs
index 39110bb4..fe2001c8 100644
--- a/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/TopicGenerationContext.cs
+++ b/Source/MQTTnet.Extensions.Rpc/Options/TopicGeneration/TopicGenerationContext.cs
@@ -1,7 +1,11 @@
-using MQTTnet.Client;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MQTTnet.Client;
using MQTTnet.Protocol;
-namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
+namespace MQTTnet.Extensions.Rpc
{
public sealed class TopicGenerationContext
{
@@ -17,8 +21,8 @@ namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
///
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
- public IMqttClient MqttClient { get; set; }
+ public MqttClient MqttClient { get; set; }
- public IMqttRpcClientOptions Options { get; set; }
+ public MqttRpcClientOptions Options { get; set; }
}
}
diff --git a/Source/MQTTnet.Extensions.Rpc/RpcAwareApplicationMessageReceivedHandler.cs b/Source/MQTTnet.Extensions.Rpc/RpcAwareApplicationMessageReceivedHandler.cs
deleted file mode 100644
index 5d43eb68..00000000
--- a/Source/MQTTnet.Extensions.Rpc/RpcAwareApplicationMessageReceivedHandler.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using MQTTnet.Client.Receiving;
-
-namespace MQTTnet.Extensions.Rpc
-{
- public sealed class RpcAwareApplicationMessageReceivedHandler : IMqttApplicationMessageReceivedHandler
- {
- readonly Func _handleReceivedApplicationMessageAsync;
-
- public RpcAwareApplicationMessageReceivedHandler(
- IMqttApplicationMessageReceivedHandler originalHandler,
- Func handleReceivedApplicationMessageAsync)
- {
- OriginalHandler = originalHandler;
- _handleReceivedApplicationMessageAsync = handleReceivedApplicationMessageAsync ?? throw new ArgumentNullException(nameof(handleReceivedApplicationMessageAsync));
- }
-
- public IMqttApplicationMessageReceivedHandler OriginalHandler { get; }
-
- public async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
- {
- // First try to check if there is a pending RPC call!
- await _handleReceivedApplicationMessageAsync(eventArgs).ConfigureAwait(false);
-
- if (OriginalHandler != null)
- {
- await OriginalHandler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false);
- }
- }
- }
-}
diff --git a/Source/MQTTnet.Extensions.WebSocket4Net/MQTTnet.Extensions.WebSocket4Net.csproj b/Source/MQTTnet.Extensions.WebSocket4Net/MQTTnet.Extensions.WebSocket4Net.csproj
index 9a5cf528..e8d3d0b2 100644
--- a/Source/MQTTnet.Extensions.WebSocket4Net/MQTTnet.Extensions.WebSocket4Net.csproj
+++ b/Source/MQTTnet.Extensions.WebSocket4Net/MQTTnet.Extensions.WebSocket4Net.csproj
@@ -1,44 +1,76 @@
-
- netstandard1.3;netstandard2.0;netstandard2.1;net5.0
- $(TargetFrameworks);net452;net461
- $(TargetFrameworks);uap10.0
-
-
-
-
- False
- false
- false
- true
- true
- snupkg
-
+
+ netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0
+ $(TargetFrameworks);net452;net461
+ $(TargetFrameworks);uap10.0
+
+ MQTTnet.Extensions.WebSocket4Net
+ MQTTnet.Extensions.WebSocket4Net
+ True
+ The contributors of MQTTnet
+ MQTTnet
+ This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients.
+ The contributors of MQTTnet
+ MQTTnet.Extensions.WebSocket4Net
+ false
+ false
+ true
+ true
+ snupkg
+ Christian Kratky 2016-2022
+ https://github.com/dotnet/MQTTnet
+ https://github.com/dotnet/MQTTnet.git
+ git
+ 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 Blazor
+ en-US
+ false
+ false
+ nuget.png
+ true
+ true
+ LICENSE
+ For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).
+ true
+
-
- false
- UAP,Version=v10.0
- UAP
- 10.0.18362.0
- 10.0.10240.0
- .NETCore
- v5.0
- $(DefineConstants);WINDOWS_UWP
- en
- $(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets
-
+
+ false
+ UAP,Version=v10.0
+ UAP
+ 10.0.18362.0
+ 10.0.10240.0
+ .NETCore
+ v5.0
+ $(DefineConstants);WINDOWS_UWP
+ en
+ $(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets
+
-
-
-
+
+
+ True
+ \
+
+
-
-
-
+
+
+ True
+ \
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/MQTTnet.Extensions.WebSocket4Net/MqttFactoryExtensions.cs b/Source/MQTTnet.Extensions.WebSocket4Net/MqttFactoryExtensions.cs
index 327074d1..d23286f2 100644
--- a/Source/MQTTnet.Extensions.WebSocket4Net/MqttFactoryExtensions.cs
+++ b/Source/MQTTnet.Extensions.WebSocket4Net/MqttFactoryExtensions.cs
@@ -1,14 +1,18 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
namespace MQTTnet.Extensions.WebSocket4Net
{
public static class MqttFactoryExtensions
{
- public static IMqttFactory UseWebSocket4Net(this IMqttFactory mqttFactory)
+ public static MqttFactory UseWebSocket4Net(this MqttFactory mqttFactory)
{
if (mqttFactory == null) throw new ArgumentNullException(nameof(mqttFactory));
- return mqttFactory.UseClientAdapterFactory(new WebSocket4NetMqttClientAdapterFactory(mqttFactory.DefaultLogger));
+ return mqttFactory.UseClientAdapterFactory(new WebSocket4NetMqttClientAdapterFactory());
}
}
}
diff --git a/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttChannel.cs b/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttChannel.cs
index 930ab7e8..a248be48 100644
--- a/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttChannel.cs
+++ b/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttChannel.cs
@@ -1,3 +1,7 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -8,9 +12,8 @@ using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Channel;
-using MQTTnet.Client.Options;
+using MQTTnet.Client;
using MQTTnet.Exceptions;
-using MQTTnet.Internal;
using SuperSocket.ClientEngine;
using WebSocket4Net;
@@ -20,12 +23,12 @@ namespace MQTTnet.Extensions.WebSocket4Net
{
readonly BlockingCollection _receiveBuffer = new BlockingCollection();
- readonly IMqttClientOptions _clientOptions;
+ readonly MqttClientOptions _clientOptions;
readonly MqttClientWebSocketOptions _webSocketOptions;
WebSocket _webSocket;
- public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions, MqttClientWebSocketOptions webSocketOptions)
+ public WebSocket4NetMqttChannel(MqttClientOptions clientOptions, MqttClientWebSocketOptions webSocketOptions)
{
_clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions));
_webSocketOptions = webSocketOptions ?? throw new ArgumentNullException(nameof(webSocketOptions));
@@ -210,25 +213,36 @@ namespace MQTTnet.Extensions.WebSocket4Net
_webSocket.Open();
#pragma warning restore AsyncFixer02 // Long-running or blocking operations inside an async method
- var exception = await MqttTaskTimeout.WaitAsync(c =>
+ using (var timeoutCts = new CancellationTokenSource(_clientOptions.Timeout))
+ using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken))
{
- c.Register(() => taskCompletionSource.TrySetCanceled());
- return taskCompletionSource.Task;
- }, _clientOptions.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
-
- if (exception != null)
- {
- if (exception is AuthenticationException authenticationException)
+ using (linkedCts.Token.Register(() => taskCompletionSource.TrySetCanceled()))
{
- throw new MqttCommunicationException(authenticationException.InnerException);
- }
+ try
+ {
+ await taskCompletionSource.Task.ConfigureAwait(false);
+ }
+ catch (Exception exception)
+ {
+ var timeoutReached = timeoutCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested;
+ if (timeoutReached)
+ {
+ throw new MqttCommunicationTimedOutException(exception);
+ }
+
+ if (exception is AuthenticationException authenticationException)
+ {
+ throw new MqttCommunicationException(authenticationException.InnerException);
+ }
- if (exception is OperationCanceledException)
- {
- throw new MqttCommunicationTimedOutException();
- }
+ if (exception is OperationCanceledException)
+ {
+ throw new MqttCommunicationTimedOutException();
+ }
- throw new MqttCommunicationException(exception);
+ throw new MqttCommunicationException(exception);
+ }
+ }
}
}
catch (OperationCanceledException)
diff --git a/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttClientAdapterFactory.cs b/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttClientAdapterFactory.cs
index e4e72cce..69ece623 100644
--- a/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttClientAdapterFactory.cs
+++ b/Source/MQTTnet.Extensions.WebSocket4Net/WebSocket4NetMqttClientAdapterFactory.cs
@@ -1,23 +1,19 @@
-using MQTTnet.Adapter;
-using MQTTnet.Client.Options;
-using MQTTnet.Diagnostics;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MQTTnet.Adapter;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using System;
-using MQTTnet.Diagnostics.Logger;
+using MQTTnet.Client;
+using MQTTnet.Diagnostics;
namespace MQTTnet.Extensions.WebSocket4Net
{
public sealed class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
{
- readonly IMqttNetLogger _logger;
-
- public WebSocket4NetMqttClientAdapterFactory(IMqttNetLogger logger)
- {
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
- public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options)
+ public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger)
{
if (options == null) throw new ArgumentNullException(nameof(options));
@@ -27,18 +23,18 @@ namespace MQTTnet.Extensions.WebSocket4Net
{
return new MqttChannelAdapter(
new MqttTcpChannel(options),
- new MqttPacketFormatterAdapter(options.ProtocolVersion),
- options.PacketInspector,
- _logger);
+ new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(4096, 65535)),
+ packetInspector,
+ logger);
}
case MqttClientWebSocketOptions webSocketOptions:
{
return new MqttChannelAdapter(
new WebSocket4NetMqttChannel(options, webSocketOptions),
- new MqttPacketFormatterAdapter(options.ProtocolVersion),
- options.PacketInspector,
- _logger);
+ new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(4068, 65535)),
+ packetInspector,
+ logger);
}
default:
diff --git a/Tests/MQTTnet.TestApp.NetCore/ClientFlowTest.cs b/Source/MQTTnet.TestApp/ClientFlowTest.cs
similarity index 80%
rename from Tests/MQTTnet.TestApp.NetCore/ClientFlowTest.cs
rename to Source/MQTTnet.TestApp/ClientFlowTest.cs
index 8c0c6d99..457fc88e 100644
--- a/Tests/MQTTnet.TestApp.NetCore/ClientFlowTest.cs
+++ b/Source/MQTTnet.TestApp/ClientFlowTest.cs
@@ -1,10 +1,13 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Threading.Tasks;
using MQTTnet.Client;
-using MQTTnet.Client.Options;
-using MQTTnet.Diagnostics.Logger;
+using MQTTnet.Diagnostics;
-namespace MQTTnet.TestApp.NetCore
+namespace MQTTnet.TestApp
{
public static class ClientFlowTest
{
@@ -32,7 +35,7 @@ namespace MQTTnet.TestApp.NetCore
Console.WriteLine("AFTER SUBSCRIBE");
Console.WriteLine("BEFORE PUBLISH");
- await client.PublishAsync("test/topic", "payload");
+ await client.PublishStringAsync("test/topic", "payload");
Console.WriteLine("AFTER PUBLISH");
await Task.Delay(1000);
diff --git a/Tests/MQTTnet.TestApp.NetCore/ClientTest.cs b/Source/MQTTnet.TestApp/ClientTest.cs
similarity index 75%
rename from Tests/MQTTnet.TestApp.NetCore/ClientTest.cs
rename to Source/MQTTnet.TestApp/ClientTest.cs
index 1f177400..33469acb 100644
--- a/Tests/MQTTnet.TestApp.NetCore/ClientTest.cs
+++ b/Source/MQTTnet.TestApp/ClientTest.cs
@@ -1,15 +1,16 @@
-using MQTTnet.Client;
-using MQTTnet.Client.Connecting;
-using MQTTnet.Client.Disconnecting;
-using MQTTnet.Client.Options;
-using MQTTnet.Client.Receiving;
-using MQTTnet.Protocol;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MQTTnet.Client;
using System;
using System.Text;
using System.Threading.Tasks;
-using MQTTnet.Diagnostics.Logger;
+using MQTTnet.Diagnostics;
+using MQTTnet.Implementations;
+using MQTTnet.Protocol;
-namespace MQTTnet.TestApp.NetCore
+namespace MQTTnet.TestApp
{
public static class ClientTest
{
@@ -30,7 +31,7 @@ namespace MQTTnet.TestApp.NetCore
}
};
- client.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(e =>
+ client.ApplicationMessageReceivedAsync += e =>
{
Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###");
Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}");
@@ -38,18 +39,20 @@ namespace MQTTnet.TestApp.NetCore
Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}");
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
Console.WriteLine();
- });
+
+ return PlatformAbstractionLayer.CompletedTask;
+ };
- client.ConnectedHandler = new MqttClientConnectedHandlerDelegate(async e =>
+ client.ConnectedAsync += async e =>
{
Console.WriteLine("### CONNECTED WITH SERVER ###");
- await client.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
+ await client.SubscribeAsync("#");
Console.WriteLine("### SUBSCRIBED ###");
- });
+ };
- client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(async e =>
+ client.DisconnectedAsync += async e =>
{
Console.WriteLine("### DISCONNECTED FROM SERVER ###");
await Task.Delay(TimeSpan.FromSeconds(5));
@@ -62,7 +65,7 @@ namespace MQTTnet.TestApp.NetCore
{
Console.WriteLine("### RECONNECTING FAILED ###");
}
- });
+ };
try
{
@@ -79,12 +82,12 @@ namespace MQTTnet.TestApp.NetCore
{
Console.ReadLine();
- await client.SubscribeAsync(new MqttTopicFilter { Topic = "test", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
+ await client.SubscribeAsync("test");
var applicationMessage = new MqttApplicationMessageBuilder()
.WithTopic("A/B/C")
.WithPayload("Hello World")
- .WithAtLeastOnceQoS()
+ .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
.Build();
await client.PublishAsync(applicationMessage);
diff --git a/Source/MQTTnet.TestApp/MQTTnet.TestApp.csproj b/Source/MQTTnet.TestApp/MQTTnet.TestApp.csproj
new file mode 100644
index 00000000..d43a14f0
--- /dev/null
+++ b/Source/MQTTnet.TestApp/MQTTnet.TestApp.csproj
@@ -0,0 +1,32 @@
+
+
+
+ Exe
+ Full
+ net5.0;net6.0
+ $(TargetFrameworks);net452;net461
+ false
+ false
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
diff --git a/Tests/MQTTnet.TestApp.NetCore/ManagedClientTest.cs b/Source/MQTTnet.TestApp/ManagedClientTest.cs
similarity index 62%
rename from Tests/MQTTnet.TestApp.NetCore/ManagedClientTest.cs
rename to Source/MQTTnet.TestApp/ManagedClientTest.cs
index 663d6ec3..88125ff8 100644
--- a/Tests/MQTTnet.TestApp.NetCore/ManagedClientTest.cs
+++ b/Source/MQTTnet.TestApp/ManagedClientTest.cs
@@ -1,14 +1,18 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Threading.Tasks;
using System.IO;
using Newtonsoft.Json;
using System.Collections.Generic;
-using MQTTnet.Client.Options;
-using MQTTnet.Client.Receiving;
+using MQTTnet.Client;
using MQTTnet.Extensions.ManagedClient;
+using MQTTnet.Implementations;
using MQTTnet.Protocol;
-namespace MQTTnet.TestApp.NetCore
+namespace MQTTnet.TestApp
{
public static class ManagedClientTest
{
@@ -35,20 +39,21 @@ namespace MQTTnet.TestApp.NetCore
try
{
var managedClient = new MqttFactory().CreateManagedMqttClient();
- managedClient.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(e =>
+ managedClient.ApplicationMessageReceivedAsync += e =>
{
Console.WriteLine(">> RECEIVED: " + e.ApplicationMessage.Topic);
- });
+ return PlatformAbstractionLayer.CompletedTask;
+ };
await managedClient.StartAsync(options);
- await managedClient.PublishAsync(builder => builder.WithTopic("Step").WithPayload("1"));
- await managedClient.PublishAsync(builder => builder.WithTopic("Step").WithPayload("2").WithAtLeastOnceQoS());
+ await managedClient.EnqueueAsync(topic: "Step", payload: "1");
+ await managedClient.EnqueueAsync(topic: "Step", payload: "2", MqttQualityOfServiceLevel.AtLeastOnce);
- await managedClient.SubscribeAsync(new MqttTopicFilter { Topic = "xyz", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
- await managedClient.SubscribeAsync(new MqttTopicFilter { Topic = "abc", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
+ await managedClient.SubscribeAsync(topic: "xyz", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtMostOnce);
+ await managedClient.SubscribeAsync(topic: "abc", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtMostOnce);
- await managedClient.PublishAsync(builder => builder.WithTopic("Step").WithPayload("3"));
+ await managedClient.EnqueueAsync(topic: "Step", payload: "3");
Console.WriteLine("Managed client started.");
Console.ReadLine();
@@ -60,16 +65,22 @@ namespace MQTTnet.TestApp.NetCore
}
- public class RandomPassword : IMqttClientCredentials
+ public sealed class RandomPassword : IMqttClientCredentialsProvider
{
- public byte[] Password => Guid.NewGuid().ToByteArray();
+ public string GetUserName(MqttClientOptions clientOptions)
+ {
+ return "the_static_user";
+ }
- public string Username => "the_static_user";
+ public byte[] GetPassword(MqttClientOptions clientOptions)
+ {
+ return Guid.NewGuid().ToByteArray();
+ }
}
public class ClientRetainedMessageHandler : IManagedMqttClientStorage
{
- private const string Filename = @"RetainedMessages.json";
+ const string Filename = @"RetainedMessages.json";
public Task SaveQueuedMessagesAsync(IList messages)
{
diff --git a/Source/MQTTnet.TestApp/MessageThroughputTest.cs b/Source/MQTTnet.TestApp/MessageThroughputTest.cs
new file mode 100644
index 00000000..311eaea6
--- /dev/null
+++ b/Source/MQTTnet.TestApp/MessageThroughputTest.cs
@@ -0,0 +1,344 @@
+using MQTTnet.Client;
+using MQTTnet.Server;
+using System.Collections.Generic;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Linq;
+
+namespace MQTTnet.TestApp
+{
+ ///
+ /// Connect a number of publisher clients and one subscriber client, then publish messages and measure
+ /// the number of messages per second that can be exchanged between publishers and subscriber.
+ /// Measurements are performed for subscriptions containing no wildcard, a single wildcard or multiple wildcards.
+ ///
+ public class MessageThroughputTest
+ {
+ // Change these constants to suit
+ const int NumPublishers = 5000;
+ const int NumTopicsPerPublisher = 10;
+
+ // Note: Other code changes may be required when changing this constant:
+ const int NumSubscribers = 1; // Fixed
+
+ // Number of publish calls before a response for all published messages is awaited.
+ // This must be limited to a reasonable value that the server or TCP pipeline can handle.
+ const int NumPublishCallsPerBatch = 250;
+
+ // Message counters are set/reset in the PublishAllAsync loop
+ int _messagesReceivedCount;
+ int _messagesExpectedCount;
+
+ CancellationTokenSource _cancellationTokenSource;
+
+ MqttServer _mqttServer;
+ Dictionary _mqttPublisherClientsByPublisherName;
+ List _mqttSubscriberClients;
+
+ Dictionary> _topicsByPublisher;
+ Dictionary> _singleWildcardTopicsByPublisher;
+ Dictionary> _multiWildcardTopicsByPublisher;
+
+ public async Task Run()
+ {
+ try
+ {
+ Console.WriteLine();
+ Console.WriteLine("Begin message throughput test");
+ Console.WriteLine();
+
+ Console.WriteLine("Number of publishers: " + NumPublishers);
+ Console.WriteLine("Number of published topics (total): " + NumPublishers * NumTopicsPerPublisher);
+ Console.WriteLine("Number of subscribers: " + NumSubscribers);
+
+ await Setup();
+
+ await Subscribe_to_No_Wildcard_Topics();
+ await Subscribe_to_Single_Wildcard_Topics();
+ await Subscribe_to_Multi_Wildcard_Topics();
+
+ Console.WriteLine();
+ Console.WriteLine("End message throughput test");
+ }
+ catch (Exception ex)
+ {
+ ConsoleWriteLineError(ex.Message);
+ }
+ finally
+ {
+ await Cleanup();
+ }
+ }
+
+ public async Task Setup()
+ {
+ new TopicGenerator().Generate(NumPublishers, NumTopicsPerPublisher, out _topicsByPublisher, out _singleWildcardTopicsByPublisher, out _multiWildcardTopicsByPublisher);
+
+ var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
+ var factory = new MqttFactory();
+ _mqttServer = factory.CreateMqttServer(serverOptions);
+ await _mqttServer.StartAsync();
+
+ Console.WriteLine();
+ Console.WriteLine("Begin connect " + NumPublishers + " publisher(s)...");
+
+ var stopWatch = new System.Diagnostics.Stopwatch();
+ stopWatch.Start();
+ _mqttPublisherClientsByPublisherName = new Dictionary();
+ foreach (var pt in _topicsByPublisher)
+ {
+ var publisherName = pt.Key;
+ var mqttClient = factory.CreateMqttClient();
+ var publisherOptions = new MqttClientOptionsBuilder()
+ .WithTcpServer("localhost")
+ .WithClientId(publisherName)
+ .Build();
+ await mqttClient.ConnectAsync(publisherOptions);
+ _mqttPublisherClientsByPublisherName.Add(publisherName, mqttClient);
+ }
+ stopWatch.Stop();
+
+ Console.Write(string.Format("{0} publisher(s) connected in {1:0.000} seconds, ", NumPublishers, stopWatch.ElapsedMilliseconds / 1000.0));
+ Console.WriteLine(string.Format("connections per second: {0:0.000}", NumPublishers / (stopWatch.ElapsedMilliseconds / 1000.0)));
+
+ _mqttSubscriberClients = new List();
+ for (var i = 0; i < NumSubscribers; ++i)
+ {
+ var mqttClient = factory.CreateMqttClient();
+ var subsriberOptions = new MqttClientOptionsBuilder()
+ .WithTcpServer("localhost")
+ .WithClientId("sub" + i)
+ .Build();
+ await mqttClient.ConnectAsync(subsriberOptions);
+ mqttClient.ApplicationMessageReceivedAsync += HandleApplicationMessageReceivedAsync;
+ _mqttSubscriberClients.Add(mqttClient);
+ }
+ }
+
+ Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
+ {
+ // count messages and signal cancellation when expected message count is reached
+ ++_messagesReceivedCount;
+ if (_messagesReceivedCount == _messagesExpectedCount)
+ {
+ _cancellationTokenSource.Cancel();
+ }
+ return Implementations.PlatformAbstractionLayer.CompletedTask;
+ }
+
+ public async Task Cleanup()
+ {
+ foreach (var mqttClient in _mqttSubscriberClients)
+ {
+ await mqttClient.DisconnectAsync();
+ mqttClient.ApplicationMessageReceivedAsync -= HandleApplicationMessageReceivedAsync;
+ mqttClient.Dispose();
+ }
+
+ foreach (var pub in _mqttPublisherClientsByPublisherName)
+ {
+ var mqttClient = pub.Value;
+ await mqttClient.DisconnectAsync();
+ mqttClient.Dispose();
+ }
+
+ await _mqttServer.StopAsync();
+ _mqttServer.Dispose();
+ }
+
+
+ ///
+ /// Measure no-wildcard topic subscription message exchange performance
+ ///
+ public Task Subscribe_to_No_Wildcard_Topics()
+ {
+ return ProcessMessages(_topicsByPublisher, "no wildcards");
+ }
+
+ ///
+ /// Measure single-wildcard topic subscription message exchange performance
+ ///
+ public Task Subscribe_to_Single_Wildcard_Topics()
+ {
+ return ProcessMessages(_singleWildcardTopicsByPublisher, "single wildcard");
+ }
+
+ ///
+ /// Measure multi-wildcard topic subscription message exchange performance
+ ///
+ public Task Subscribe_to_Multi_Wildcard_Topics()
+ {
+ return ProcessMessages(_multiWildcardTopicsByPublisher, "multi wildcard");
+ }
+
+
+ ///
+ /// Subcribe to all topics, then run message exchange
+ ///
+ public async Task ProcessMessages(Dictionary> topicsByPublisher, string topicTypeDescription)
+ {
+ var numTopics = CountTopics(topicsByPublisher);
+
+ Console.WriteLine();
+ Console.Write(string.Format("Subscribing to {0} topics ", numTopics));
+ ConsoleWriteInfo(string.Format("({0})", topicTypeDescription));
+ Console.WriteLine(string.Format(" for {0} subscriber(s)...", NumSubscribers));
+
+ var stopWatch = new System.Diagnostics.Stopwatch();
+ stopWatch.Start();
+
+ // each subscriber subscribes to all topics, one call per topic
+ foreach (var subscriber in _mqttSubscriberClients)
+ {
+ foreach (var tp in topicsByPublisher)
+ {
+ var topics = tp.Value;
+ foreach (var topic in topics)
+ {
+ var topicFilter = new Packets.MqttTopicFilter() { Topic = topic };
+ await subscriber.SubscribeAsync(topicFilter).ConfigureAwait(false);
+ }
+ }
+ }
+
+ stopWatch.Stop();
+
+ Console.Write(string.Format("{0} subscriber(s) subscribed in {1:0.000} seconds, ", NumSubscribers, stopWatch.ElapsedMilliseconds / 1000.0));
+ Console.WriteLine(string.Format("subscribe calls per second: {0:0.000}", numTopics / (stopWatch.ElapsedMilliseconds / 1000.0)));
+
+ await PublishAllAsync();
+
+ Console.WriteLine(string.Format("Unsubscribing {0} topics ({1}) for {2} subscriber(s)...", numTopics, topicTypeDescription, NumSubscribers));
+
+ stopWatch.Restart();
+
+ // unsubscribe to all topics, one call per topic
+ foreach (var subscriber in _mqttSubscriberClients)
+ {
+ foreach (var tp in topicsByPublisher)
+ {
+ var topics = tp.Value;
+ foreach (var topic in topics)
+ {
+ var topicFilter = new Packets.MqttTopicFilter() { Topic = topic };
+
+ MqttClientUnsubscribeOptions options =
+ new MqttClientUnsubscribeOptionsBuilder()
+ .WithTopicFilter(topicFilter)
+ .Build();
+ await subscriber.UnsubscribeAsync(options).ConfigureAwait(false);
+ }
+ }
+ }
+
+ stopWatch.Stop();
+
+ Console.Write(string.Format("{0} subscriber(s) unsubscribed in {1:0.000} seconds, ", NumSubscribers, stopWatch.ElapsedMilliseconds / 1000.0));
+ Console.WriteLine(string.Format("unsubscribe calls per second: {0:0.000}", numTopics / (stopWatch.ElapsedMilliseconds / 1000.0)));
+ }
+
+
+ ///
+ /// Publish messages in batches of NumPublishCallsPerBatch, wait for messages sent to subscriber
+ ///
+ async Task PublishAllAsync()
+ {
+ Console.WriteLine("Begin message exchange...");
+
+ int publisherIndexCounter = 0; // index to loop around all publishers to publish
+ int topicIndexCounter = 0; // index to loop around all topics to publish
+ int totalNumMessagesReceived = 0;
+
+ var publisherNames = _topicsByPublisher.Keys.ToList();
+
+ // There should be one message received per publish
+ _messagesExpectedCount = NumPublishCallsPerBatch;
+
+ var stopWatch = new System.Diagnostics.Stopwatch();
+ stopWatch.Start();
+
+ // Loop for a while and exchange messages
+
+ while (stopWatch.ElapsedMilliseconds < 10000)
+ {
+ _messagesReceivedCount = 0;
+
+ _cancellationTokenSource = new CancellationTokenSource();
+
+ for (var publishCallCount = 0; publishCallCount < NumPublishCallsPerBatch; ++publishCallCount)
+ {
+ // pick a publisher
+ var publisherName = publisherNames[publisherIndexCounter % publisherNames.Count];
+ var publisherTopics = _topicsByPublisher[publisherName];
+ // pick a publisher topic
+ var topic = publisherTopics[topicIndexCounter % publisherTopics.Count];
+ var message = new MqttApplicationMessageBuilder()
+ .WithTopic(topic)
+ .Build();
+ await _mqttPublisherClientsByPublisherName[publisherName].PublishAsync(message).ConfigureAwait(false);
+ ++topicIndexCounter;
+ ++publisherIndexCounter;
+ }
+
+ // Wait for at least one message per publish to be received by subscriber (in the subscriber's application message handler),
+ // then loop around to send another batch
+ try
+ {
+ await Task.Delay(30000, _cancellationTokenSource.Token).ConfigureAwait(false);
+ }
+ catch
+ {
+
+ }
+
+ _cancellationTokenSource.Dispose();
+
+ if (_messagesReceivedCount < _messagesExpectedCount)
+ {
+ ConsoleWriteLineError(string.Format("Messages Received Count mismatch, expected {0}, received {1}", _messagesExpectedCount, _messagesReceivedCount));
+ return;
+ }
+
+ totalNumMessagesReceived += _messagesReceivedCount;
+ }
+
+ stopWatch.Stop();
+
+ System.Console.Write(string.Format("{0} messages published and received in {1:0.000} seconds, ", totalNumMessagesReceived, stopWatch.ElapsedMilliseconds / 1000.0));
+ ConsoleWriteLineSuccess(string.Format("messages per second: {0}", (int)(totalNumMessagesReceived / (stopWatch.ElapsedMilliseconds / 1000.0))));
+ }
+
+ int CountTopics(Dictionary> topicsByPublisher)
+ {
+ var count = 0;
+ foreach (var tp in topicsByPublisher)
+ {
+ count += tp.Value.Count;
+ }
+ return count;
+ }
+
+ void ConsoleWriteLineError(string message)
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine(message);
+ Console.ResetColor();
+ }
+
+ void ConsoleWriteLineSuccess(string message)
+ {
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine(message);
+ Console.ResetColor();
+ }
+
+ void ConsoleWriteInfo(string message)
+ {
+ Console.ForegroundColor = ConsoleColor.White;
+ Console.Write(message);
+ Console.ResetColor();
+ }
+
+ }
+}
diff --git a/Tests/MQTTnet.TestApp.NetCore/MqttNetConsoleLogger.cs b/Source/MQTTnet.TestApp/MqttNetConsoleLogger.cs
similarity index 87%
rename from Tests/MQTTnet.TestApp.NetCore/MqttNetConsoleLogger.cs
rename to Source/MQTTnet.TestApp/MqttNetConsoleLogger.cs
index 1c1d6ed9..040e2c78 100644
--- a/Tests/MQTTnet.TestApp.NetCore/MqttNetConsoleLogger.cs
+++ b/Source/MQTTnet.TestApp/MqttNetConsoleLogger.cs
@@ -1,8 +1,12 @@
-using System;
-using System.Text;
-using MQTTnet.Diagnostics.Logger;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
-namespace MQTTnet.TestApp.NetCore
+using System;
+using System.Text;
+using MQTTnet.Diagnostics;
+
+namespace MQTTnet.TestApp
{
public static class MqttNetConsoleLogger
{
diff --git a/Tests/MQTTnet.TestApp.NetCore/PerformanceTest.cs b/Source/MQTTnet.TestApp/PerformanceTest.cs
similarity index 93%
rename from Tests/MQTTnet.TestApp.NetCore/PerformanceTest.cs
rename to Source/MQTTnet.TestApp/PerformanceTest.cs
index b41d1995..e4189b27 100644
--- a/Tests/MQTTnet.TestApp.NetCore/PerformanceTest.cs
+++ b/Source/MQTTnet.TestApp/PerformanceTest.cs
@@ -1,15 +1,19 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Client;
-using MQTTnet.Client.Options;
using MQTTnet.Protocol;
using MQTTnet.Server;
+using MqttClient = MQTTnet.Client.MqttClient;
-namespace MQTTnet.TestApp.NetCore
+namespace MQTTnet.TestApp
{
public static class PerformanceTest
{
@@ -58,22 +62,25 @@ namespace MQTTnet.TestApp.NetCore
{
try
{
- var mqttServer = new MqttFactory().CreateMqttServer();
- await mqttServer.StartAsync(new MqttServerOptions()).ConfigureAwait(false);
+ var mqttFactory = new MqttFactory();
+ var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
+ var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions);
+ await mqttServer.StartAsync().ConfigureAwait(false);
var options = new MqttClientOptions
{
ChannelOptions = new MqttClientTcpOptions
{
Server = "127.0.0.1"
- },
- CleanSession = true
+ }
};
- var client = new MqttFactory().CreateMqttClient();
+ var client = mqttFactory.CreateMqttClient();
await client.ConnectAsync(options).ConfigureAwait(false);
- var message = CreateMessage();
+ var message = new MqttApplicationMessageBuilder().WithTopic("t")
+ .Build();
+
var stopwatch = new Stopwatch();
for (var i = 0; i < 10; i++)
@@ -96,12 +103,12 @@ namespace MQTTnet.TestApp.NetCore
}
}
- private static Task RunClientsAsync(int msgChunkSize, TimeSpan interval, bool concurrent)
+ static Task RunClientsAsync(int msgChunkSize, TimeSpan interval, bool concurrent)
{
return Task.WhenAll(Enumerable.Range(0, 3).Select(i => Task.Run(() => RunClientAsync(msgChunkSize, interval, concurrent))));
}
- private static async Task RunClientAsync(int msgChunkSize, TimeSpan interval, bool concurrent)
+ static async Task RunClientAsync(int msgChunkSize, TimeSpan interval, bool concurrent)
{
try
{
@@ -109,8 +116,7 @@ namespace MQTTnet.TestApp.NetCore
{
ChannelOptions = new MqttClientTcpOptions { Server = "localhost" },
ClientId = "Client1",
- CleanSession = true,
- CommunicationTimeout = TimeSpan.FromMinutes(10)
+ CleanSession = true
};
var client = new MqttFactory().CreateMqttClient();
@@ -156,9 +162,12 @@ namespace MQTTnet.TestApp.NetCore
}
else
{
- await client.PublishAsync(msgs);
- msgCount += msgs.Count;
- //send multiple
+ foreach (var msg in msgs)
+ {
+ await client.PublishAsync(msg);
+ msgCount += msgs.Count;
+ //send multiple
+ }
}
var now = DateTime.Now;
@@ -178,7 +187,7 @@ namespace MQTTnet.TestApp.NetCore
}
}
- private static MqttApplicationMessage CreateMessage()
+ static MqttApplicationMessage CreateMessage()
{
//const string Payload = "###############################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################"
//const string Payload = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
@@ -192,7 +201,7 @@ namespace MQTTnet.TestApp.NetCore
};
}
- private static Task PublishSingleMessage(IMqttClient client, MqttApplicationMessage applicationMessage, ref int count)
+ static Task PublishSingleMessage(MqttClient client, MqttApplicationMessage applicationMessage, ref int count)
{
Interlocked.Increment(ref count);
return Task.Run(() => client.PublishAsync(applicationMessage));
@@ -202,8 +211,8 @@ namespace MQTTnet.TestApp.NetCore
{
try
{
- var mqttServer = new MqttFactory().CreateMqttServer();
- await mqttServer.StartAsync(new MqttServerOptions());
+ var mqttServer = new MqttFactory().CreateMqttServer(new MqttServerOptions());
+ await mqttServer.StartAsync();
var options = new MqttClientOptions
{
@@ -253,8 +262,8 @@ namespace MQTTnet.TestApp.NetCore
{
try
{
- var mqttServer = new MqttFactory().CreateMqttServer();
- await mqttServer.StartAsync(new MqttServerOptions());
+ var mqttServer = new MqttFactory().CreateMqttServer(new MqttServerOptions());
+ await mqttServer.StartAsync();
var options = new MqttClientOptions
{
diff --git a/Source/MQTTnet.TestApp/Program.cs b/Source/MQTTnet.TestApp/Program.cs
new file mode 100644
index 00000000..f1a32581
--- /dev/null
+++ b/Source/MQTTnet.TestApp/Program.cs
@@ -0,0 +1,95 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MQTTnet.Diagnostics;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MQTTnet.TestApp
+{
+ public static class Program
+ {
+ public static void Main()
+ {
+ Console.WriteLine($"MQTTnet - TestApp.{TargetFrameworkProvider.TargetFramework}");
+ Console.WriteLine("1 = Start client");
+ Console.WriteLine("2 = Start server");
+ Console.WriteLine("3 = Start performance test");
+ Console.WriteLine("4 = Start managed client");
+ Console.WriteLine("5 = Start public broker test");
+ Console.WriteLine("6 = Start server & client");
+ Console.WriteLine("7 = Client flow test");
+ Console.WriteLine("8 = Start performance test (client only)");
+ Console.WriteLine("9 = Start server (no trace)");
+ Console.WriteLine("a = Start QoS 2 benchmark");
+ Console.WriteLine("b = Start QoS 1 benchmark");
+ Console.WriteLine("c = Start QoS 0 benchmark");
+ Console.WriteLine("d = Start server with logging");
+ Console.WriteLine("e = Start Message Throughput Test");
+
+ var pressedKey = Console.ReadKey(true);
+ if (pressedKey.KeyChar == '1')
+ {
+ Task.Run(ClientTest.RunAsync);
+ }
+ else if (pressedKey.KeyChar == '2')
+ {
+ Task.Run(ServerTest.RunAsync);
+ }
+ else if (pressedKey.KeyChar == '3')
+ {
+ Task.Run(PerformanceTest.RunClientAndServer);
+ }
+ else if (pressedKey.KeyChar == '4')
+ {
+ Task.Run(ManagedClientTest.RunAsync);
+ }
+ else if (pressedKey.KeyChar == '5')
+ {
+ Task.Run(PublicBrokerTest.RunAsync);
+ }
+ else if (pressedKey.KeyChar == '6')
+ {
+ Task.Run(ServerAndClientTest.RunAsync);
+ }
+ else if (pressedKey.KeyChar == '7')
+ {
+ Task.Run(ClientFlowTest.RunAsync);
+ }
+ else if (pressedKey.KeyChar == '8')
+ {
+ PerformanceTest.RunClientOnly();
+ return;
+ }
+ else if (pressedKey.KeyChar == '9')
+ {
+ ServerTest.RunEmptyServer();
+ return;
+ }
+ else if (pressedKey.KeyChar == 'a')
+ {
+ Task.Run(PerformanceTest.RunQoS2Test);
+ }
+ else if (pressedKey.KeyChar == 'b')
+ {
+ Task.Run(PerformanceTest.RunQoS1Test);
+ }
+ else if (pressedKey.KeyChar == 'c')
+ {
+ Task.Run(PerformanceTest.RunQoS0Test);
+ }
+ else if (pressedKey.KeyChar == 'd')
+ {
+ Task.Run(ServerTest.RunEmptyServerWithLogging);
+ }
+ else if (pressedKey.KeyChar == 'e')
+ {
+ Task.Run(new MessageThroughputTest().Run);
+ }
+
+ Thread.Sleep(Timeout.Infinite);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tests/MQTTnet.TestApp.NetCore/PublicBrokerTest.cs b/Source/MQTTnet.TestApp/PublicBrokerTest.cs
similarity index 89%
rename from Tests/MQTTnet.TestApp.NetCore/PublicBrokerTest.cs
rename to Source/MQTTnet.TestApp/PublicBrokerTest.cs
index bb55f30d..65aaa31c 100644
--- a/Tests/MQTTnet.TestApp.NetCore/PublicBrokerTest.cs
+++ b/Source/MQTTnet.TestApp/PublicBrokerTest.cs
@@ -1,16 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
using MQTTnet.Client;
using System;
-using System.IO;
-using System.Net;
using System.Security.Authentication;
using System.Threading;
using System.Threading.Tasks;
-using MQTTnet.Client.Options;
-using MQTTnet.Client.Receiving;
using MQTTnet.Formatter;
+using MQTTnet.Implementations;
using MQTTnet.Protocol;
-namespace MQTTnet.TestApp.NetCore
+namespace MQTTnet.TestApp
{
public static class PublicBrokerTest
{
@@ -27,7 +28,7 @@ namespace MQTTnet.TestApp.NetCore
UseTls = true,
SslProtocol = SslProtocols.Tls13,
// Don't use this in production code. This handler simply allows any invalid certificate to work.
- CertificateValidationHandler = (w) => true
+ CertificateValidationHandler = w => true
};
#endif
@@ -37,7 +38,7 @@ namespace MQTTnet.TestApp.NetCore
UseTls = true,
SslProtocol = SslProtocols.Tls12,
// Don't use this in production code. This handler simply allows any invalid certificate to work.
- CertificateValidationHandler = (w) => true
+ CertificateValidationHandler = w => true
};
// mqtt.eclipseprojects.io
@@ -129,7 +130,7 @@ namespace MQTTnet.TestApp.NetCore
Console.ReadLine();
}
- private static async Task ExecuteTestAsync(string name, IMqttClientOptions options)
+ static async Task ExecuteTestAsync(string name, MqttClientOptions options)
{
try
{
@@ -140,11 +141,15 @@ namespace MQTTnet.TestApp.NetCore
var topic = Guid.NewGuid().ToString();
MqttApplicationMessage receivedMessage = null;
- client.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(e => receivedMessage = e.ApplicationMessage);
+ client.ApplicationMessageReceivedAsync += e =>
+ {
+ receivedMessage = e.ApplicationMessage;
+ return PlatformAbstractionLayer.CompletedTask;
+ };
await client.ConnectAsync(options);
await client.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce);
- await client.PublishAsync(topic, "Hello_World", MqttQualityOfServiceLevel.AtLeastOnce);
+ await client.PublishStringAsync(topic, "Hello_World", MqttQualityOfServiceLevel.AtLeastOnce);
SpinWait.SpinUntil(() => receivedMessage != null, 5000);
@@ -164,7 +169,7 @@ namespace MQTTnet.TestApp.NetCore
}
}
- private static void Write(string message, ConsoleColor color)
+ static void Write(string message, ConsoleColor color)
{
Console.ForegroundColor = color;
Console.Write(message);
diff --git a/Tests/MQTTnet.TestApp.NetCore/ServerAndClientTest.cs b/Source/MQTTnet.TestApp/ServerAndClientTest.cs
similarity index 60%
rename from Tests/MQTTnet.TestApp.NetCore/ServerAndClientTest.cs
rename to Source/MQTTnet.TestApp/ServerAndClientTest.cs
index 28986397..062160c6 100644
--- a/Tests/MQTTnet.TestApp.NetCore/ServerAndClientTest.cs
+++ b/Source/MQTTnet.TestApp/ServerAndClientTest.cs
@@ -1,11 +1,14 @@
-using System.Threading;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Client;
-using MQTTnet.Client.Options;
-using MQTTnet.Diagnostics.Logger;
+using MQTTnet.Diagnostics;
using MQTTnet.Server;
-namespace MQTTnet.TestApp.NetCore
+namespace MQTTnet.TestApp
{
public static class ServerAndClientTest
{
@@ -15,11 +18,10 @@ namespace MQTTnet.TestApp.NetCore
MqttNetConsoleLogger.ForwardToConsole(logger);
var factory = new MqttFactory(logger);
- var server = factory.CreateMqttServer();
+ var server = factory.CreateMqttServer( new MqttServerOptionsBuilder().Build());
var client = factory.CreateMqttClient();
- var serverOptions = new MqttServerOptionsBuilder().Build();
- await server.StartAsync(serverOptions);
+ await server.StartAsync();
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
await client.ConnectAsync(clientOptions);
diff --git a/Source/MQTTnet.TestApp/ServerTest.cs b/Source/MQTTnet.TestApp/ServerTest.cs
new file mode 100644
index 00000000..341b3dda
--- /dev/null
+++ b/Source/MQTTnet.TestApp/ServerTest.cs
@@ -0,0 +1,198 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using MQTTnet.Diagnostics;
+using MQTTnet.Extensions.ManagedClient;
+using MQTTnet.Implementations;
+using MQTTnet.Protocol;
+using MQTTnet.Server;
+using Newtonsoft.Json;
+
+namespace MQTTnet.TestApp
+{
+ public static class ServerTest
+ {
+ public static void RunEmptyServer()
+ {
+ var mqttServer = new MqttFactory().CreateMqttServer(new MqttServerOptions());
+ mqttServer.StartAsync().GetAwaiter().GetResult();
+
+ Console.WriteLine("Press any key to exit.");
+ Console.ReadLine();
+ }
+
+ public static void RunEmptyServerWithLogging()
+ {
+ var logger = new MqttNetEventLogger();
+ MqttNetConsoleLogger.ForwardToConsole(logger);
+
+ var mqttFactory = new MqttFactory(logger);
+ var mqttServer = mqttFactory.CreateMqttServer(new MqttServerOptions());
+ mqttServer.StartAsync().GetAwaiter().GetResult();
+
+ Console.WriteLine("Press any key to exit.");
+ Console.ReadLine();
+ }
+
+ public static async Task RunAsync()
+ {
+ try
+ {
+ var options = new MqttServerOptions();
+
+ // Extend the timestamp for all messages from clients.
+ // Protect several topics from being subscribed from every client.
+
+ //var certificate = new X509Certificate(@"C:\certs\test\test.cer", "");
+ //options.TlsEndpointOptions.Certificate = certificate.Export(X509ContentType.Cert);
+ //options.ConnectionBacklog = 5;
+ //options.DefaultEndpointOptions.IsEnabled = true;
+ //options.TlsEndpointOptions.IsEnabled = false;
+
+ var mqttServer = new MqttFactory().CreateMqttServer(options);
+
+ const string Filename = "C:\\MQTT\\RetainedMessages.json";
+
+ mqttServer.RetainedMessageChangedAsync += e =>
+ {
+ var directory = Path.GetDirectoryName(Filename);
+ if (!Directory.Exists(directory))
+ {
+ Directory.CreateDirectory(directory);
+ }
+
+ File.WriteAllText(Filename, JsonConvert.SerializeObject(e.StoredRetainedMessages));
+ return Task.FromResult(0);
+ };
+
+ mqttServer.RetainedMessagesClearedAsync += e =>
+ {
+ File.Delete(Filename);
+ return Task.FromResult(0);
+ };
+
+ mqttServer.LoadingRetainedMessageAsync += e =>
+ {
+ List retainedMessages;
+ if (File.Exists(Filename))
+ {
+ var json = File.ReadAllText(Filename);
+ retainedMessages = JsonConvert.DeserializeObject>(json);
+ }
+ else
+ {
+ retainedMessages = new List();
+ }
+
+ e.LoadedRetainedMessages = retainedMessages;
+
+ return Task.FromResult(0);
+ };
+
+ mqttServer.InterceptingPublishAsync += e =>
+ {
+ if (MqttTopicFilterComparer.Compare(e.ApplicationMessage.Topic, "/myTopic/WithTimestamp/#") == MqttTopicFilterCompareResult.IsMatch)
+ {
+ // Replace the payload with the timestamp. But also extending a JSON
+ // based payload with the timestamp is a suitable use case.
+ e.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(DateTime.Now.ToString("O"));
+ }
+
+ if (e.ApplicationMessage.Topic == "not_allowed_topic")
+ {
+ e.ProcessPublish = false;
+ e.CloseConnection = true;
+ }
+
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ mqttServer.ValidatingConnectionAsync += e =>
+ {
+ if (e.ClientId == "SpecialClient")
+ {
+ if (e.Username != "USER" || e.Password != "PASS")
+ {
+ e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
+ }
+ }
+
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ mqttServer.InterceptingSubscriptionAsync += e =>
+ {
+ if (e.TopicFilter.Topic.StartsWith("admin/foo/bar") && e.ClientId != "theAdmin")
+ {
+ e.Response.ReasonCode = MqttSubscribeReasonCode.ImplementationSpecificError;
+ }
+
+ if (e.TopicFilter.Topic.StartsWith("the/secret/stuff") && e.ClientId != "Imperator")
+ {
+ e.Response.ReasonCode = MqttSubscribeReasonCode.ImplementationSpecificError;
+ e.CloseConnection = true;
+ }
+
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ mqttServer.InterceptingPublishAsync += e =>
+ {
+ MqttNetConsoleLogger.PrintToConsole(
+ $"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[0])}'",
+ ConsoleColor.Magenta);
+
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ //options.ApplicationMessageInterceptor = c =>
+ //{
+ // if (c.ApplicationMessage.Payload == null || c.ApplicationMessage.Payload.Length == 0)
+ // {
+ // return;
+ // }
+
+ // try
+ // {
+ // var content = JObject.Parse(Encoding.UTF8.GetString(c.ApplicationMessage.Payload));
+ // var timestampProperty = content.Property("timestamp");
+ // if (timestampProperty != null && timestampProperty.Value.Type == JTokenType.Null)
+ // {
+ // timestampProperty.Value = DateTime.Now.ToString("O");
+ // c.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(content.ToString());
+ // }
+ // }
+ // catch (Exception)
+ // {
+ // }
+ //};
+
+ mqttServer.ClientConnectedAsync += e =>
+ {
+ Console.Write("Client disconnected event fired.");
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ await mqttServer.StartAsync();
+
+ Console.WriteLine("Press any key to exit.");
+ Console.ReadLine();
+
+ await mqttServer.StopAsync();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ }
+
+ Console.ReadLine();
+ }
+ }
+}
diff --git a/Tests/MQTTnet.TestApp.NetCore/Start.bat b/Source/MQTTnet.TestApp/Start.bat
similarity index 100%
rename from Tests/MQTTnet.TestApp.NetCore/Start.bat
rename to Source/MQTTnet.TestApp/Start.bat
diff --git a/Source/MQTTnet.TestApp/TopicGenerator.cs b/Source/MQTTnet.TestApp/TopicGenerator.cs
new file mode 100644
index 00000000..495c18e2
--- /dev/null
+++ b/Source/MQTTnet.TestApp/TopicGenerator.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MQTTnet.TestApp
+{
+ public class TopicGenerator
+ {
+ public void Generate(
+ int numPublishers, int numTopicsPerPublisher,
+ out Dictionary> topicsByPublisher,
+ out Dictionary> singleWildcardTopicsByPublisher,
+ out Dictionary> multiWildcardTopicsByPublisher
+ )
+ {
+ topicsByPublisher = new Dictionary>();
+ singleWildcardTopicsByPublisher = new Dictionary>();
+ multiWildcardTopicsByPublisher = new Dictionary>();
+
+ // Find some reasonable distribution across three topic levels
+ var topicsPerLevel = (int)Math.Pow(numTopicsPerPublisher, (1.0 / 3.0));
+
+ int numLevel1Topics = topicsPerLevel;
+ if (numLevel1Topics <= 0)
+ {
+ numLevel1Topics = 1;
+ }
+ int numLevel2Topics = topicsPerLevel;
+ if (numLevel2Topics <= 0)
+ {
+ numLevel2Topics = 1;
+ }
+ var maxNumLevel3Topics = 1 + (int)((double)numTopicsPerPublisher / numLevel1Topics / numLevel2Topics);
+ if (maxNumLevel3Topics <= 0)
+ {
+ maxNumLevel3Topics = 1;
+ }
+ for (var p = 0; p < numPublishers; ++p)
+ {
+ int publisherTopicCount = 0;
+
+ var publisherName = "pub" + p;
+ for (var l1 = 0; l1 < numLevel1Topics; ++l1)
+ {
+ for (var l2 = 0; l2 < numLevel2Topics; ++l2)
+ {
+ for (var l3 = 0; l3 < maxNumLevel3Topics; ++l3)
+ {
+ if (publisherTopicCount >= numTopicsPerPublisher)
+ break;
+
+ var topic = string.Format("{0}/building{1}/level{2}/sensor{3}", publisherName, l1 + 1, l2 + 1, l3 + 1);
+ AddPublisherTopic(publisherName, topic, topicsByPublisher);
+
+ if (l2 == 0)
+ {
+ var singleWildcardTopic = string.Format("{0}/building{1}/+/sensor{2}", publisherName, l1 + 1, l3 + 1);
+ AddPublisherTopic(publisherName, singleWildcardTopic, singleWildcardTopicsByPublisher);
+ if (l1 == 0)
+ {
+ var multiWildcardTopic = string.Format("{0}/+/level{1}/+", publisherName, l3 + 1);
+ AddPublisherTopic(publisherName, multiWildcardTopic, multiWildcardTopicsByPublisher);
+ }
+ }
+
+ ++publisherTopicCount;
+ }
+ }
+ }
+ }
+ }
+
+ void AddPublisherTopic(string publisherName, string topic, Dictionary> topicsByPublisher)
+ {
+ List topicList;
+ if (!topicsByPublisher.TryGetValue(publisherName, out topicList))
+ {
+ topicList = new List();
+ topicsByPublisher.Add(publisherName, topicList);
+ }
+ topicList.Add(topic);
+ }
+ }
+}
diff --git a/Tests/MQTTnet.Core.Tests/BaseTestClass.cs b/Source/MQTTnet.Tests/BaseTestClass.cs
similarity index 73%
rename from Tests/MQTTnet.Core.Tests/BaseTestClass.cs
rename to Source/MQTTnet.Tests/BaseTestClass.cs
index 80cfe240..8e5248e7 100644
--- a/Tests/MQTTnet.Core.Tests/BaseTestClass.cs
+++ b/Source/MQTTnet.Tests/BaseTestClass.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Formatter;
diff --git a/Source/MQTTnet.Tests/Client/LowLevelMqttClient_Tests.cs b/Source/MQTTnet.Tests/Client/LowLevelMqttClient_Tests.cs
new file mode 100644
index 00000000..6534bf3f
--- /dev/null
+++ b/Source/MQTTnet.Tests/Client/LowLevelMqttClient_Tests.cs
@@ -0,0 +1,216 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MQTTnet.Client;
+using MQTTnet.Exceptions;
+using MQTTnet.LowLevelClient;
+using MQTTnet.Packets;
+using MQTTnet.Protocol;
+
+namespace MQTTnet.Tests.Client
+{
+ [TestClass]
+ public sealed class LowLevelMqttClient_Tests : BaseTestClass
+ {
+ [TestMethod]
+ public async Task Authenticate()
+ {
+ using (var testEnvironment = CreateTestEnvironment())
+ {
+ await testEnvironment.StartServer();
+
+ var factory = new MqttFactory();
+ var lowLevelClient = factory.CreateLowLevelMqttClient();
+
+ await lowLevelClient.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build(), CancellationToken.None);
+
+ var receivedPacket = await Authenticate(lowLevelClient).ConfigureAwait(false);
+
+ await lowLevelClient.DisconnectAsync(CancellationToken.None).ConfigureAwait(false);
+
+ Assert.IsNotNull(receivedPacket);
+ Assert.AreEqual(MqttConnectReturnCode.ConnectionAccepted, receivedPacket.ReturnCode);
+ }
+ }
+
+ [TestMethod]
+ public async Task Connect_And_Disconnect()
+ {
+ using (var testEnvironment = CreateTestEnvironment())
+ {
+ await testEnvironment.StartServer();
+
+ var lowLevelClient = testEnvironment.Factory.CreateLowLevelMqttClient();
+
+ await lowLevelClient.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build(), CancellationToken.None);
+
+ await lowLevelClient.DisconnectAsync(CancellationToken.None);
+ }
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(MqttCommunicationException))]
+ public async Task Connect_To_Not_Existing_Broker()
+ {
+ var client = new MqttFactory().CreateLowLevelMqttClient();
+ var options = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
+
+ await client.ConnectAsync(options, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(MqttCommunicationException))]
+ public async Task Connect_To_Wrong_Host()
+ {
+ var client = new MqttFactory().CreateLowLevelMqttClient();
+ var options = new MqttClientOptionsBuilder().WithTcpServer("123.456.789.10").Build();
+
+ await client.ConnectAsync(options, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ [TestMethod]
+ public async Task Loose_Connection()
+ {
+ using (var testEnvironment = CreateTestEnvironment())
+ {
+ testEnvironment.ServerPort = 8364;
+ var server = await testEnvironment.StartServer();
+
+ var client = await testEnvironment.ConnectLowLevelClient(o => o.WithTimeout(TimeSpan.Zero));
+
+ await Authenticate(client).ConfigureAwait(false);
+
+ await server.StopAsync();
+
+ await Task.Delay(2000);
+
+ try
+ {
+ await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None).ConfigureAwait(false);
+ await Task.Delay(2000);
+ await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None).ConfigureAwait(false);
+ }
+ catch (MqttCommunicationException exception)
+ {
+ Assert.IsTrue(exception.InnerException is SocketException);
+ return;
+ }
+ catch
+ {
+ Assert.Fail("Wrong exception type thrown.");
+ }
+
+ Assert.Fail("This MUST fail");
+ }
+ }
+
+ [TestMethod]
+ public async Task Maintain_IsConnected_Property()
+ {
+ using (var testEnvironment = CreateTestEnvironment())
+ {
+ var server = await testEnvironment.StartServer();
+
+ using (var lowLevelClient = testEnvironment.CreateLowLevelClient())
+ {
+ Assert.IsFalse(lowLevelClient.IsConnected);
+
+ var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithTimeout(TimeSpan.FromSeconds(1)).Build();
+
+ await lowLevelClient.ConnectAsync(clientOptions, CancellationToken.None);
+
+ Assert.IsTrue(lowLevelClient.IsConnected);
+
+ await server.StopAsync();
+ server.Dispose();
+
+ await LongTestDelay();
+
+ Assert.IsTrue(lowLevelClient.IsConnected);
+
+ try
+ {
+ using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
+ {
+ await lowLevelClient.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
+ await LongTestDelay();
+
+ await lowLevelClient.ReceiveAsync(timeout.Token);
+ }
+ }
+ catch
+ {
+ }
+
+ Assert.IsFalse(lowLevelClient.IsConnected);
+ }
+ }
+ }
+
+ [TestMethod]
+ public async Task Subscribe()
+ {
+ using (var testEnvironment = CreateTestEnvironment())
+ {
+ await testEnvironment.StartServer();
+
+ var factory = new MqttFactory();
+ var lowLevelClient = factory.CreateLowLevelMqttClient();
+
+ await lowLevelClient.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build(), CancellationToken.None);
+
+ await Authenticate(lowLevelClient).ConfigureAwait(false);
+
+ var receivedPacket = await Subscribe(lowLevelClient, "a").ConfigureAwait(false);
+
+ await lowLevelClient.DisconnectAsync(CancellationToken.None).ConfigureAwait(false);
+
+ Assert.IsNotNull(receivedPacket);
+ Assert.AreEqual(MqttSubscribeReasonCode.GrantedQoS0, receivedPacket.ReasonCodes[0]);
+ }
+ }
+
+ async Task Authenticate(LowLevelMqttClient client)
+ {
+ await client.SendAsync(
+ new MqttConnectPacket
+ {
+ CleanSession = true,
+ ClientId = TestContext.TestName,
+ Username = "user",
+ Password = Encoding.UTF8.GetBytes("pass")
+ },
+ CancellationToken.None)
+ .ConfigureAwait(false);
+
+ return await client.ReceiveAsync(CancellationToken.None).ConfigureAwait(false) as MqttConnAckPacket;
+ }
+
+ async Task Subscribe(LowLevelMqttClient client, string topic)
+ {
+ await client.SendAsync(
+ new MqttSubscribePacket
+ {
+ PacketIdentifier = 1,
+ TopicFilters =
+ {
+ new MqttTopicFilter
+ {
+ Topic = topic
+ }
+ }
+ },
+ CancellationToken.None)
+ .ConfigureAwait(false);
+
+ return await client.ReceiveAsync(CancellationToken.None).ConfigureAwait(false) as MqttSubAckPacket;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tests/MQTTnet.Core.Tests/Client/ManagedMqttClient_Tests.cs b/Source/MQTTnet.Tests/Client/ManagedMqttClient_Tests.cs
similarity index 62%
rename from Tests/MQTTnet.Core.Tests/Client/ManagedMqttClient_Tests.cs
rename to Source/MQTTnet.Tests/Client/ManagedMqttClient_Tests.cs
index 7f29468b..e64931ac 100644
--- a/Tests/MQTTnet.Core.Tests/Client/ManagedMqttClient_Tests.cs
+++ b/Source/MQTTnet.Tests/Client/ManagedMqttClient_Tests.cs
@@ -1,25 +1,63 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Client;
-using MQTTnet.Client.Connecting;
-using MQTTnet.Client.Options;
-using MQTTnet.Client.Receiving;
using MQTTnet.Diagnostics;
-using MQTTnet.Diagnostics.Logger;
using MQTTnet.Extensions.ManagedClient;
+using MQTTnet.Implementations;
+using MQTTnet.Internal;
+using MQTTnet.Protocol;
using MQTTnet.Server;
using MQTTnet.Tests.Mockups;
+using MqttClient = MQTTnet.Client.MqttClient;
namespace MQTTnet.Tests.Client
{
[TestClass]
- public class ManagedMqttClient_Tests
+ public sealed class ManagedMqttClient_Tests : BaseTestClass
{
- public TestContext TestContext { get; set; }
+ [TestMethod]
+ public async Task Connect_To_Invalid_Server()
+ {
+ using (var testEnvironment = CreateTestEnvironment())
+ {
+ testEnvironment.IgnoreClientLogErrors = true;
+
+ var mqttClient = testEnvironment.CreateClient();
+
+ var managedClient = testEnvironment.Factory.CreateManagedMqttClient(mqttClient);
+
+ ConnectingFailedEventArgs connectingFailedEventArgs = null;
+
+ managedClient.ConnectingFailedAsync += args =>
+ {
+ connectingFailedEventArgs = args;
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ await managedClient.StartAsync(
+ new ManagedMqttClientOptions
+ {
+ ClientOptions = testEnvironment.Factory.CreateClientOptionsBuilder().WithTimeout(TimeSpan.FromSeconds(2)).WithTcpServer("wrong_server", 1234).Build()
+ });
+
+ await managedClient.EnqueueAsync("test_topic_2");
+
+ SpinWait.SpinUntil(() => connectingFailedEventArgs != null, 10000);
+
+ // The wrong server must be reported in general.
+ Assert.IsNotNull(connectingFailedEventArgs);
+ Assert.IsNotNull(connectingFailedEventArgs.Exception);
+ Assert.IsNull(connectingFailedEventArgs.ConnectResult);
+ }
+ }
[TestMethod]
public async Task Drop_New_Messages_On_Full_Queue()
@@ -28,23 +66,21 @@ namespace MQTTnet.Tests.Client
var managedClient = factory.CreateManagedMqttClient();
try
{
- var clientOptions = new ManagedMqttClientOptionsBuilder()
- .WithMaxPendingMessages(5)
+ var clientOptions = new ManagedMqttClientOptionsBuilder().WithMaxPendingMessages(5)
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropNewMessage);
clientOptions.WithClientOptions(o => o.WithTcpServer("localhost"));
await managedClient.StartAsync(clientOptions.Build());
- await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "1" });
- await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "2" });
- await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "3" });
- await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "4" });
- await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "5" });
-
- await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "6" });
- await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "7" });
- await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "8" });
+ await managedClient.EnqueueAsync("1");
+ await managedClient.EnqueueAsync("2");
+ await managedClient.EnqueueAsync("3");
+ await managedClient.EnqueueAsync("4");
+ await managedClient.EnqueueAsync("5");
+ await managedClient.EnqueueAsync("6");
+ await managedClient.EnqueueAsync("7");
+ await managedClient.EnqueueAsync("8");
Assert.AreEqual(5, managedClient.PendingApplicationMessagesCount);
}
@@ -57,30 +93,41 @@ namespace MQTTnet.Tests.Client
[TestMethod]
public async Task ManagedClients_Will_Message_Send()
{
- using (var testEnvironment = new TestEnvironment(TestContext))
+ using (var testEnvironment = CreateTestEnvironment())
{
- var receivedMessagesCount = 0;
-
await testEnvironment.StartServer();
- var willMessage = new MqttApplicationMessageBuilder().WithTopic("My/last/will").WithAtMostOnceQoS().Build();
- var clientOptions = new MqttClientOptionsBuilder()
- .WithTcpServer("localhost", testEnvironment.ServerPort)
- .WithWillMessage(willMessage);
+ var receivedMessagesCount = 0;
+
+ var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
+ .WithWillTopic("My/last/will")
+ .WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce)
+ .Build();
+
var dyingClient = testEnvironment.CreateClient();
var dyingManagedClient = new ManagedMqttClient(dyingClient, testEnvironment.ClientLogger);
- await dyingManagedClient.StartAsync(new ManagedMqttClientOptionsBuilder()
- .WithClientOptions(clientOptions)
- .Build());
- var recievingClient = await testEnvironment.ConnectClient();
- await recievingClient.SubscribeAsync("My/last/will");
+ await dyingManagedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).Build());
- recievingClient.UseApplicationMessageReceivedHandler(context => Interlocked.Increment(ref receivedMessagesCount));
+ // Wait until the managed client is fully set up and running.
+ await Task.Delay(1000);
+ var receivingClient = await testEnvironment.ConnectClient();
+
+ receivingClient.ApplicationMessageReceivedAsync += e =>
+ {
+ Interlocked.Increment(ref receivedMessagesCount);
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ await receivingClient.SubscribeAsync("My/last/will");
+
+ // Disposing the client will not sent a DISCONNECT packet so that connection is terminated
+ // which will lead to the will publish.
dyingManagedClient.Dispose();
- await Task.Delay(1000);
+ // Wait for arrival of the will message at the receiver.
+ await Task.Delay(5000);
Assert.AreEqual(1, receivedMessagesCount);
}
@@ -89,19 +136,16 @@ namespace MQTTnet.Tests.Client
[TestMethod]
public async Task Start_Stop()
{
- using (var testEnvironment = new TestEnvironment(TestContext))
+ using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServer();
var managedClient = new ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetEventLogger());
- var clientOptions = new MqttClientOptionsBuilder()
- .WithTcpServer("localhost", testEnvironment.ServerPort);
+ var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort);
var connected = GetConnectedTask(managedClient);
- await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder()
- .WithClientOptions(clientOptions)
- .Build());
+ await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).Build());
await connected;
@@ -109,14 +153,14 @@ namespace MQTTnet.Tests.Client
await Task.Delay(500);
- Assert.AreEqual(0, (await server.GetClientStatusAsync()).Count);
+ Assert.AreEqual(0, (await server.GetClientsAsync()).Count);
}
}
[TestMethod]
public async Task Storage_Queue_Drains()
{
- using (var testEnvironment = new TestEnvironment(TestContext))
+ using (var testEnvironment = CreateTestEnvironment())
{
testEnvironment.IgnoreClientLogErrors = true;
testEnvironment.IgnoreServerLogErrors = true;
@@ -124,23 +168,19 @@ namespace MQTTnet.Tests.Client
await testEnvironment.StartServer();
var managedClient = new ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetEventLogger());
- var clientOptions = new MqttClientOptionsBuilder()
- .WithTcpServer("localhost", testEnvironment.ServerPort);
+ var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort);
var storage = new ManagedMqttClientTestStorage();
var connected = GetConnectedTask(managedClient);
- await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder()
- .WithClientOptions(clientOptions)
- .WithStorage(storage)
- .WithAutoReconnectDelay(System.TimeSpan.FromSeconds(5))
- .Build());
+ await managedClient.StartAsync(
+ new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithStorage(storage).WithAutoReconnectDelay(TimeSpan.FromSeconds(5)).Build());
await connected;
await testEnvironment.Server.StopAsync();
- await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "1" });
+ await managedClient.EnqueueAsync("1");
//Message should have been added to the storage queue in PublishAsync,
//and we are awaiting PublishAsync, so the message should already be
@@ -149,8 +189,7 @@ namespace MQTTnet.Tests.Client
connected = GetConnectedTask(managedClient);
- await testEnvironment.Server.StartAsync(new MqttServerOptionsBuilder()
- .WithDefaultEndpointPort(testEnvironment.ServerPort).Build());
+ await testEnvironment.Server.StartAsync();
await connected;
@@ -166,7 +205,7 @@ namespace MQTTnet.Tests.Client
[TestMethod]
public async Task Subscriptions_And_Unsubscriptions_Are_Made_And_Reestablished_At_Reconnect()
{
- using (var testEnvironment = new TestEnvironment(TestContext))
+ using (var testEnvironment = CreateTestEnvironment())
{
var unmanagedClient = testEnvironment.CreateClient();
var managedClient = await CreateManagedClientAsync(testEnvironment, unmanagedClient);
@@ -190,9 +229,9 @@ namespace MQTTnet.Tests.Client
async Task PublishMessages()
{
- await sendingClient.PublishAsync("keptSubscribed", new byte[] { 1 });
- await sendingClient.PublishAsync("subscribedThenUnsubscribed", new byte[] { 1 });
- await sendingClient.PublishAsync("unsubscribedThenSubscribed", new byte[] { 1 });
+ await sendingClient.PublishBinaryAsync("keptSubscribed", new byte[] { 1 });
+ await sendingClient.PublishBinaryAsync("subscribedThenUnsubscribed", new byte[] { 1 });
+ await sendingClient.PublishBinaryAsync("unsubscribedThenSubscribed", new byte[] { 1 });
}
await PublishMessages();
@@ -225,13 +264,102 @@ namespace MQTTnet.Tests.Client
}
}
+ [TestMethod]
+ public async Task Subscriptions_Are_Cleared_At_Logout()
+ {
+ using (var testEnvironment = CreateTestEnvironment())
+ {
+ await testEnvironment.StartServer().ConfigureAwait(false);
+
+ var sendingClient = await testEnvironment.ConnectClient().ConfigureAwait(false);
+ await sendingClient.PublishAsync(
+ new MqttApplicationMessage
+ {
+ Topic = "topic",
+ Payload = new byte[] { 1 },
+ Retain = true
+ });
+
+ // Wait a bit for the retained message to be available
+ await Task.Delay(1000);
+
+ await sendingClient.DisconnectAsync();
+
+ // Now use the managed client and check if subscriptions get cleared properly.
+
+ var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort);
+
+ var receivedManagedMessages = new List();
+ var managedClient = new ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetEventLogger());
+ managedClient.ApplicationMessageReceivedAsync += e =>
+ {
+ receivedManagedMessages.Add(e.ApplicationMessage);
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ await managedClient.SubscribeAsync("topic");
+
+ await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
+
+ await Task.Delay(1000);
+
+ Assert.AreEqual(1, receivedManagedMessages.Count);
+
+ await managedClient.StopAsync();
+
+ await Task.Delay(500);
+
+ await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
+
+ await Task.Delay(1000);
+
+ // After reconnect and then some delay, the retained message must not be received,
+ // showing that the subscriptions were cleared
+ Assert.AreEqual(1, receivedManagedMessages.Count);
+
+ // Make sure that it gets received after subscribing again.
+ await managedClient.SubscribeAsync("topic");
+ await Task.Delay(500);
+ Assert.AreEqual(2, receivedManagedMessages.Count);
+ }
+ }
+
+ // This case also serves as a regression test for the previous behavior
+ // that subscriptions were only published at the ConnectionCheckInterval
+ [TestMethod]
+ public async Task Subscriptions_Are_Published_Immediately()
+ {
+ using (var testEnvironment = CreateTestEnvironment())
+ {
+ // Use a long connection check interval to verify that the subscriptions
+ // do not depend on the connection check interval anymore
+ var connectionCheckInterval = TimeSpan.FromSeconds(10);
+ var receivingClient = await CreateManagedClientAsync(testEnvironment, null, connectionCheckInterval);
+ var sendingClient = await testEnvironment.ConnectClient();
+
+ await sendingClient.PublishAsync(new MqttApplicationMessage { Topic = "topic", Payload = new byte[] { 1 }, Retain = true });
+
+ var subscribeTime = DateTime.UtcNow;
+
+ var messagesTask = SetupReceivingOfMessages(receivingClient, 1);
+
+ await receivingClient.SubscribeAsync("topic");
+
+ var messages = await messagesTask;
+
+ var elapsed = DateTime.UtcNow - subscribeTime;
+ Assert.IsTrue(elapsed < TimeSpan.FromSeconds(1), $"Subscriptions must be activated immediately, this one took {elapsed}");
+ Assert.AreEqual(messages.Single().Topic, "topic");
+ }
+ }
+
// This case also serves as a regression test for the previous behavior which re-published
// each and every existing subscriptions with every new subscription that was made
// (causing performance problems and having the visible symptom of retained messages being received again)
[TestMethod]
public async Task Subscriptions_Subscribe_Only_New_Subscriptions()
{
- using (var testEnvironment = new TestEnvironment(TestContext))
+ using (var testEnvironment = CreateTestEnvironment())
{
var managedClient = await CreateManagedClientAsync(testEnvironment);
@@ -259,173 +387,17 @@ namespace MQTTnet.Tests.Client
}
}
- // This case also serves as a regression test for the previous behavior
- // that subscriptions were only published at the ConnectionCheckInterval
- [TestMethod]
- public async Task Subscriptions_Are_Published_Immediately()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- // Use a long connection check interval to verify that the subscriptions
- // do not depend on the connection check interval anymore
- var connectionCheckInterval = TimeSpan.FromSeconds(10);
- var managedClient = await CreateManagedClientAsync(testEnvironment, null, connectionCheckInterval);
- var sendingClient = await testEnvironment.ConnectClient();
-
- await sendingClient.PublishAsync(new MqttApplicationMessage { Topic = "topic", Payload = new byte[] { 1 }, Retain = true });
-
- var subscribeTime = DateTime.UtcNow;
-
- var messagesTask = SetupReceivingOfMessages(managedClient, 1);
-
- await managedClient.SubscribeAsync("topic");
-
- var messages = await messagesTask;
-
- var elapsed = DateTime.UtcNow - subscribeTime;
- Assert.IsTrue(elapsed < TimeSpan.FromSeconds(1), $"Subscriptions must be activated immediately, this one took {elapsed}");
- Assert.AreEqual(messages.Single().Topic, "topic");
- }
- }
-
- [TestMethod]
- public async Task Subscriptions_Are_Cleared_At_Logout()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- await testEnvironment.StartServer().ConfigureAwait(false);
-
- var sendingClient = await testEnvironment.ConnectClient().ConfigureAwait(false);
- await sendingClient.PublishAsync(new MqttApplicationMessage
- {
- Topic = "topic",
- Payload = new byte[] { 1 },
- Retain = true
- });
-
- // Wait a bit for the retained message to be available
- await Task.Delay(1000);
-
- await sendingClient.DisconnectAsync();
-
- // Now use the managed client and check if subscriptions get cleared properly.
-
- var clientOptions = new MqttClientOptionsBuilder()
- .WithTcpServer("127.0.0.1", testEnvironment.ServerPort);
-
- var receivedManagedMessages = new List();
- var managedClient = new ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetEventLogger());
- managedClient.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(c =>
- {
- receivedManagedMessages.Add(c.ApplicationMessage);
- });
-
- await managedClient.SubscribeAsync("topic");
-
- await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder()
- .WithClientOptions(clientOptions)
- .WithAutoReconnectDelay(TimeSpan.FromSeconds(1))
- .Build());
-
- await Task.Delay(1000);
-
- Assert.AreEqual(1, receivedManagedMessages.Count);
-
- await managedClient.StopAsync();
-
- await Task.Delay(500);
-
- await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder()
- .WithClientOptions(clientOptions)
- .WithAutoReconnectDelay(TimeSpan.FromSeconds(1))
- .Build());
-
- await Task.Delay(1000);
-
- // After reconnect and then some delay, the retained message must not be received,
- // showing that the subscriptions were cleared
- Assert.AreEqual(1, receivedManagedMessages.Count);
-
- // Make sure that it gets received after subscribing again.
- await managedClient.SubscribeAsync("topic");
- await Task.Delay(500);
- Assert.AreEqual(2, receivedManagedMessages.Count);
- }
- }
-
- [TestMethod]
- public async Task Manage_Session_MaxParallel_Subscribe()
- {
- using (var testEnvironment = new TestEnvironment())
- {
- testEnvironment.IgnoreClientLogErrors = true;
-
- var serverOptions = new MqttServerOptionsBuilder();
- await testEnvironment.StartServer(serverOptions);
-
- var options = new MqttClientOptionsBuilder().WithClientId("1").WithKeepAlivePeriod(TimeSpan.FromSeconds(5));
-
- var hasReceive = false;
-
- void OnReceive()
- {
- if (!hasReceive)
- {
- hasReceive = true;
- }
- }
-
- var clients = await Task.WhenAll(Enumerable.Range(0, 25)
- .Select(i => TryConnect_Subscribe(testEnvironment, options, OnReceive)));
-
- var connectedClients = clients.Where(c => c?.IsConnected ?? false).ToList();
-
- Assert.AreEqual(1, connectedClients.Count);
-
- await Task.Delay(10000); // over 1.5T
-
- var option2 = new MqttClientOptionsBuilder().WithClientId("2").WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
- var sendClient = await testEnvironment.ConnectClient(option2);
- await sendClient.PublishAsync("aaa", "1");
-
- await Task.Delay(3000);
-
- Assert.AreEqual(true, hasReceive);
- }
- }
-
- private async Task TryConnect_Subscribe(TestEnvironment testEnvironment, MqttClientOptionsBuilder options, Action onReceive)
- {
-
- try
- {
- var sendClient = await testEnvironment.ConnectClient(options);
- sendClient.ApplicationMessageReceivedHandler = new MQTTnet.Client.Receiving.MqttApplicationMessageReceivedHandlerDelegate(e =>
- {
- onReceive();
- });
- await sendClient.SubscribeAsync("aaa");
- return sendClient;
- }
- catch (System.Exception)
- {
- return null;
- }
- }
-
async Task CreateManagedClientAsync(
TestEnvironment testEnvironment,
- IMqttClient underlyingClient = null,
- TimeSpan? connectionCheckInterval = null)
+ MqttClient underlyingClient = null,
+ TimeSpan? connectionCheckInterval = null,
+ string host = "localhost")
{
await testEnvironment.StartServer();
- var clientOptions = new MqttClientOptionsBuilder()
- .WithTcpServer("localhost", testEnvironment.ServerPort);
+ var clientOptions = new MqttClientOptionsBuilder().WithTcpServer(host, testEnvironment.ServerPort);
- var managedOptions = new ManagedMqttClientOptionsBuilder()
- .WithClientOptions(clientOptions)
- .Build();
+ var managedOptions = new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).Build();
// Use a short connection check interval so that subscription operations are performed quickly
// in order to verify against a previous implementation that performed subscriptions only
@@ -444,37 +416,49 @@ namespace MQTTnet.Tests.Client
}
///
- /// Returns a task that will finish when the has connected
+ /// Returns a task that will finish when the
+ ///
+ /// has connected
///
Task GetConnectedTask(ManagedMqttClient managedClient)
{
- TaskCompletionSource connected = new TaskCompletionSource();
- managedClient.ConnectedHandler = new MqttClientConnectedHandlerDelegate(e =>
+ var connected = new TaskCompletionSource();
+
+ managedClient.ConnectedAsync += e =>
{
- managedClient.ConnectedHandler = null;
- connected.SetResult(true);
- });
+ connected.TrySetResult(true);
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
return connected.Task;
}
///
- /// Returns a task that will return the messages received on
- /// when have been received
+ /// Returns a task that will return the messages received on
+ ///
+ /// when
+ ///
+ /// have been received
///
Task> SetupReceivingOfMessages(ManagedMqttClient managedClient, int expectedNumberOfMessages)
{
var receivedMessages = new List();
- var result = new TaskCompletionSource>(TaskCreationOptions.RunContinuationsAsynchronously);
+ var result = new AsyncTaskCompletionSource>();
- managedClient.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(r =>
+ managedClient.ApplicationMessageReceivedAsync += e =>
{
- receivedMessages.Add(r.ApplicationMessage);
+ receivedMessages.Add(e.ApplicationMessage);
if (receivedMessages.Count == expectedNumberOfMessages)
{
result.TrySetResult(receivedMessages);
}
- });
+
+ return PlatformAbstractionLayer.CompletedTask;
+ };
return result.Task;
}
@@ -484,6 +468,11 @@ namespace MQTTnet.Tests.Client
{
IList _messages;
+ public int GetMessageCount()
+ {
+ return _messages.Count;
+ }
+
public Task> LoadQueuedMessagesAsync()
{
if (_messages == null)
@@ -499,10 +488,5 @@ namespace MQTTnet.Tests.Client
_messages = messages;
return Task.FromResult(0);
}
-
- public int GetMessageCount()
- {
- return _messages.Count;
- }
}
-}
+}
\ No newline at end of file
diff --git a/Tests/MQTTnet.Core.Tests/Client/MqttClientOptionsBuilder_Tests.cs b/Source/MQTTnet.Tests/Client/MqttClientOptionsBuilder_Tests.cs
similarity index 59%
rename from Tests/MQTTnet.Core.Tests/Client/MqttClientOptionsBuilder_Tests.cs
rename to Source/MQTTnet.Tests/Client/MqttClientOptionsBuilder_Tests.cs
index 56748bb2..59db1094 100644
--- a/Tests/MQTTnet.Core.Tests/Client/MqttClientOptionsBuilder_Tests.cs
+++ b/Source/MQTTnet.Tests/Client/MqttClientOptionsBuilder_Tests.cs
@@ -1,7 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using MQTTnet.Client.Options;
+using MQTTnet.Client;
using MQTTnet.Extensions;
namespace MQTTnet.Tests.Client
@@ -15,8 +19,9 @@ namespace MQTTnet.Tests.Client
var options = new MqttClientOptionsBuilder()
.WithConnectionUri("mqtt://user:password@127.0.0.1")
.Build();
- Assert.AreEqual("user", options.Credentials.Username);
- Assert.IsTrue(Encoding.UTF8.GetBytes("password").SequenceEqual(options.Credentials.Password));
+
+ Assert.AreEqual("user", options.Credentials.GetUserName(null));
+ Assert.IsTrue(Encoding.UTF8.GetBytes("password").SequenceEqual(options.Credentials.GetPassword(null)));
}
}
}
diff --git a/Tests/MQTTnet.Core.Tests/Client/MqttClient_Tests.cs b/Source/MQTTnet.Tests/Client/MqttClient_Tests.cs
similarity index 74%
rename from Tests/MQTTnet.Core.Tests/Client/MqttClient_Tests.cs
rename to Source/MQTTnet.Tests/Client/MqttClient_Tests.cs
index b6f32753..e6dbcdc4 100644
--- a/Tests/MQTTnet.Core.Tests/Client/MqttClient_Tests.cs
+++ b/Source/MQTTnet.Tests/Client/MqttClient_Tests.cs
@@ -1,5 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Text;
@@ -7,24 +12,148 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Client;
-using MQTTnet.Client.Connecting;
-using MQTTnet.Client.Disconnecting;
-using MQTTnet.Client.Options;
-using MQTTnet.Client.Receiving;
-using MQTTnet.Client.Subscribing;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
+using MQTTnet.Implementations;
using MQTTnet.Packets;
using MQTTnet.Protocol;
-using MQTTnet.Server;
using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests.Client
{
[TestClass]
- public class Client_Tests
+ public sealed class Client_Tests : BaseTestClass
{
- public TestContext TestContext { get; set; }
+ [DataTestMethod]
+ [DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
+ [DataRow(MqttQualityOfServiceLevel.AtMostOnce)]
+ [DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
+ public async Task Concurrent_Processing(MqttQualityOfServiceLevel qos)
+ {
+ long concurrency = 0;
+ var success = false;
+
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ await testEnvironment.StartServer();
+ var publisher = await testEnvironment.ConnectClient();
+ var subscriber = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId(qos.ToString()));
+ await subscriber.SubscribeAsync("#", qos);
+
+ subscriber.ApplicationMessageReceivedAsync += e =>
+ {
+ e.AutoAcknowledge = false;
+
+ async Task InvokeInternal()
+ {
+ if (Interlocked.Increment(ref concurrency) > 1)
+ {
+ success = true;
+ }
+
+ await Task.Delay(100);
+ Interlocked.Decrement(ref concurrency);
+ }
+
+ _ = InvokeInternal();
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ var publishes = Task.WhenAll(publisher.PublishStringAsync("a", null, qos), publisher.PublishStringAsync("b", null, qos));
+
+ await Task.Delay(200);
+
+ await publishes;
+ Assert.IsTrue(success);
+ }
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(MqttCommunicationException))]
+ public async Task Connect_To_Invalid_Server_Port_Not_Opened()
+ {
+ var client = new MqttFactory().CreateMqttClient();
+ await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", 12345).WithTimeout(TimeSpan.FromSeconds(5)).Build());
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(OperationCanceledException))]
+ public async Task Connect_To_Invalid_Server_Wrong_IP()
+ {
+ var client = new MqttFactory().CreateMqttClient();
+ using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2)))
+ {
+ await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build(), timeout.Token);
+ }
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(MqttCommunicationException))]
+ public async Task Connect_To_Invalid_Server_Wrong_Protocol()
+ {
+ var client = new MqttFactory().CreateMqttClient();
+ await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("http://127.0.0.1", 12345).WithTimeout(TimeSpan.FromSeconds(2)).Build());
+ }
+
+ [TestMethod]
+ public async Task ConnectTimeout_Throws_Exception()
+ {
+ var factory = new MqttFactory();
+ using (var client = factory.CreateMqttClient())
+ {
+ var disconnectHandlerCalled = false;
+ try
+ {
+ client.DisconnectedAsync += args =>
+ {
+ disconnectHandlerCalled = true;
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build());
+
+ Assert.Fail("Must fail!");
+ }
+ catch (Exception exception)
+ {
+ Assert.IsNotNull(exception);
+ Assert.IsInstanceOfType(exception, typeof(MqttCommunicationException));
+ //Assert.IsInstanceOfType(exception.InnerException, typeof(SocketException));
+ }
+
+ await Task.Delay(100); // disconnected handler is called async
+ Assert.IsTrue(disconnectHandlerCalled);
+ }
+ }
+
+ [TestMethod]
+ public async Task Disconnect_Event_Contains_Exception()
+ {
+ var factory = new MqttFactory();
+ using (var client = factory.CreateMqttClient())
+ {
+ Exception ex = null;
+ client.DisconnectedAsync += e =>
+ {
+ ex = e.Exception;
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ try
+ {
+ await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("wrong-server").Build());
+ }
+ catch
+ {
+ }
+
+ await Task.Delay(500);
+
+ Assert.IsNotNull(ex);
+ Assert.IsInstanceOfType(ex, typeof(MqttCommunicationException));
+ Assert.IsInstanceOfType(ex.InnerException, typeof(SocketException));
+ }
+ }
[TestMethod]
public async Task Ensure_Queue_Drain()
@@ -35,305 +164,105 @@ namespace MQTTnet.Tests.Client
var client = await testEnvironment.ConnectLowLevelClient();
var i = 0;
- server.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(c =>
+ server.InterceptingPublishAsync += c =>
{
i++;
- });
+ return PlatformAbstractionLayer.CompletedTask;
+ };
- await client.SendAsync(new MqttConnectPacket
- {
- ClientId = "Ensure_Queue_Drain_Test"
- }, CancellationToken.None);
+ await client.SendAsync(
+ new MqttConnectPacket
+ {
+ ClientId = "Ensure_Queue_Drain_Test"
+ },
+ CancellationToken.None);
+
+ await client.SendAsync(
+ new MqttPublishPacket
+ {
+ Topic = "Test"
+ },
+ CancellationToken.None);
- await client.SendAsync(new MqttPublishPacket
- {
- Topic = "Test"
- }, CancellationToken.None);
-
await Task.Delay(500);
-
+
// This will simulate a device which closes the connection directly
// after sending the data so do delay is added between send and dispose!
client.Dispose();
-
+
await Task.Delay(1000);
Assert.AreEqual(1, i);
}
}
-
+
[TestMethod]
- public async Task Set_ClientWasConnected_On_ServerDisconnect()
+ public async Task Fire_Disconnected_Event_On_Server_Shutdown()
{
using (var testEnvironment = new TestEnvironment(TestContext))
{
var server = await testEnvironment.StartServer();
var client = await testEnvironment.ConnectClient();
- Assert.IsTrue(client.IsConnected);
- client.UseDisconnectedHandler(e => Assert.IsTrue(e.ClientWasConnected));
+ var handlerFired = false;
+ client.DisconnectedAsync += e =>
+ {
+ handlerFired = true;
+ return PlatformAbstractionLayer.CompletedTask;
+ };
await server.StopAsync();
+
await Task.Delay(4000);
+
+ Assert.IsTrue(handlerFired);
}
}
[TestMethod]
- public async Task Set_ClientWasConnected_On_ClientDisconnect()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- var server = await testEnvironment.StartServer();
- var client = await testEnvironment.ConnectClient();
-
- Assert.IsTrue(client.IsConnected);
- client.UseDisconnectedHandler(e => Assert.IsTrue(e.ClientWasConnected));
-
- await client.DisconnectAsync();
- await Task.Delay(200);
- }
- }
-
- [TestMethod]
- [ExpectedException(typeof(MqttCommunicationTimedOutException))]
- public async Task Connect_To_Invalid_Server_Wrong_IP()
- {
- var client = new MqttFactory().CreateMqttClient();
- await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").WithCommunicationTimeout(TimeSpan.FromSeconds(2)).Build());
- }
-
- [TestMethod]
- [ExpectedException(typeof(MqttCommunicationException))]
- public async Task Connect_To_Invalid_Server_Port_Not_Opened()
- {
- var client = new MqttFactory().CreateMqttClient();
- await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", 12345).WithCommunicationTimeout(TimeSpan.FromSeconds(5)).Build());
- }
-
- [TestMethod]
- [ExpectedException(typeof(MqttCommunicationException))]
- public async Task Connect_To_Invalid_Server_Wrong_Protocol()
- {
- var client = new MqttFactory().CreateMqttClient();
- await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("http://127.0.0.1", 12345).WithCommunicationTimeout(TimeSpan.FromSeconds(2)).Build());
- }
-
- [TestMethod]
- public async Task Send_Manual_Ping()
+ public async Task Frequent_Connects()
{
using (var testEnvironment = new TestEnvironment(TestContext))
{
await testEnvironment.StartServer();
- var client = await testEnvironment.ConnectClient();
- await client.PingAsync(CancellationToken.None);
- }
- }
-
- [TestMethod]
- public async Task Send_Reply_In_Message_Handler_For_Same_Client()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- await testEnvironment.StartServer();
- var client = await testEnvironment.ConnectClient();
-
- await client.SubscribeAsync("#");
-
- var replyReceived = false;
-
- client.UseApplicationMessageReceivedHandler(c =>
+ var clients = new List();
+ for (var i = 0; i < 100; i++)
{
- if (c.ApplicationMessage.Topic == "request")
- {
-#pragma warning disable 4014
- Task.Run(() => client.PublishAsync("reply", null, MqttQualityOfServiceLevel.AtLeastOnce));
-#pragma warning restore 4014
- }
- else
- {
- replyReceived = true;
- }
- });
-
- await client.PublishAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
-
- SpinWait.SpinUntil(() => replyReceived, TimeSpan.FromSeconds(10));
-
- Assert.IsTrue(replyReceived);
- }
- }
-
- [TestMethod]
- public async Task Send_Reply_In_Message_Handler()
- {
- using (var testEnvironment = new TestEnvironment())
- {
- await testEnvironment.StartServer();
- var client1 = await testEnvironment.ConnectClient();
- var client2 = await testEnvironment.ConnectClient();
-
- await client1.SubscribeAsync("#");
- await client2.SubscribeAsync("#");
-
- var replyReceived = false;
-
- client1.UseApplicationMessageReceivedHandler(c =>
- {
- if (c.ApplicationMessage.Topic == "reply")
- {
- replyReceived = true;
- }
- });
-
- client2.UseApplicationMessageReceivedHandler(async c =>
- {
- if (c.ApplicationMessage.Topic == "request")
- {
- // Use AtMostOnce here because with QoS 1 or even QoS 2 the process waits for
- // the ACK etc. The problem is that the SpinUntil below only waits until the
- // flag is set. It does not wait until the client has sent the ACK
- await client2.PublishAsync("reply", null, MqttQualityOfServiceLevel.AtMostOnce);
- }
- });
-
- await client1.PublishAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
-
- await Task.Delay(500);
-
- SpinWait.SpinUntil(() => replyReceived, TimeSpan.FromSeconds(10));
-
- await Task.Delay(500);
-
- Assert.IsTrue(replyReceived);
- }
- }
-
- [TestMethod]
- public async Task Reconnect()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- var server = await testEnvironment.StartServer();
- var client = await testEnvironment.ConnectClient();
-
- await Task.Delay(500);
- Assert.IsTrue(client.IsConnected);
-
- await server.StopAsync();
- await Task.Delay(500);
- Assert.IsFalse(client.IsConnected);
-
- await server.StartAsync(new MqttServerOptionsBuilder().WithDefaultEndpointPort(testEnvironment.ServerPort).Build());
- await Task.Delay(500);
-
- await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
- Assert.IsTrue(client.IsConnected);
- }
- }
-
- [TestMethod]
- public async Task Reconnect_While_Server_Offline()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- testEnvironment.IgnoreClientLogErrors = true;
-
- var server = await testEnvironment.StartServer();
- var client = await testEnvironment.ConnectClient();
-
- await Task.Delay(500);
- Assert.IsTrue(client.IsConnected);
-
- await server.StopAsync();
- await Task.Delay(500);
- Assert.IsFalse(client.IsConnected);
-
- for (var i = 0; i < 5; i++)
- {
- try
- {
- await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
- Assert.Fail("Must fail!");
- }
- catch
- {
- }
+ clients.Add(await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("a")));
}
- await server.StartAsync(new MqttServerOptionsBuilder().WithDefaultEndpointPort(testEnvironment.ServerPort).Build());
await Task.Delay(500);
- await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
- Assert.IsTrue(client.IsConnected);
- }
- }
+ var clientStatus = await testEnvironment.Server.GetClientsAsync();
+ var sessionStatus = await testEnvironment.Server.GetSessionsAsync();
- [TestMethod]
- public async Task Reconnect_From_Disconnected_Event()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- testEnvironment.IgnoreClientLogErrors = true;
-
- var client = testEnvironment.CreateClient();
-
- var tries = 0;
- var maxTries = 3;
-
- client.UseDisconnectedHandler(async e =>
- {
- if (tries >= maxTries)
- {
- return;
- }
-
- Interlocked.Increment(ref tries);
-
- await Task.Delay(100);
- await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
- });
-
- try
- {
- await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
- Assert.Fail("Must fail!");
- }
- catch
+ for (var i = 0; i < 98; i++)
{
+ Assert.IsFalse(clients[i].IsConnected, $"clients[{i}] is not connected");
}
- SpinWait.SpinUntil(() => tries >= maxTries, 10000);
+ Assert.IsTrue(clients[99].IsConnected);
- Assert.AreEqual(maxTries, tries);
- }
- }
+ Assert.AreEqual(1, clientStatus.Count);
+ Assert.AreEqual(1, sessionStatus.Count);
- [TestMethod]
- public async Task PacketIdentifier_In_Publish_Result()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- await testEnvironment.StartServer();
- var client = await testEnvironment.ConnectClient();
+ var receiveClient = clients[99];
+ object receivedPayload = null;
+ receiveClient.ApplicationMessageReceivedAsync += e =>
+ {
+ receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
+ return PlatformAbstractionLayer.CompletedTask;
+ };
- var result = await client.PublishAsync("a", "a", MqttQualityOfServiceLevel.AtMostOnce);
- Assert.AreEqual(null, result.PacketIdentifier);
+ await receiveClient.SubscribeAsync("x");
- result = await client.PublishAsync("b", "b", MqttQualityOfServiceLevel.AtMostOnce);
- Assert.AreEqual(null, result.PacketIdentifier);
+ var sendClient = await testEnvironment.ConnectClient();
+ await sendClient.PublishStringAsync("x", "1");
- result = await client.PublishAsync("a", "a", MqttQualityOfServiceLevel.AtLeastOnce);
- Assert.AreEqual((ushort)1, result.PacketIdentifier);
+ await Task.Delay(250);
- result = await client.PublishAsync("b", "b", MqttQualityOfServiceLevel.AtLeastOnce);
- Assert.AreEqual((ushort)2, result.PacketIdentifier);
-
- result = await client.PublishAsync("a", "a", MqttQualityOfServiceLevel.ExactlyOnce);
- Assert.AreEqual((ushort)3, result.PacketIdentifier);
-
- result = await client.PublishAsync("b", "b", MqttQualityOfServiceLevel.ExactlyOnce);
- Assert.AreEqual((ushort)4, result.PacketIdentifier);
+ Assert.AreEqual("1", receivedPayload);
}
}
@@ -359,79 +288,94 @@ namespace MQTTnet.Tests.Client
}
[TestMethod]
- public async Task ConnectTimeout_Throws_Exception()
- {
- var factory = new MqttFactory();
- using (var client = factory.CreateMqttClient())
- {
- bool disconnectHandlerCalled = false;
- try
- {
- client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(args =>
- {
- disconnectHandlerCalled = true;
- });
-
- await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build());
-
- Assert.Fail("Must fail!");
- }
- catch (Exception exception)
- {
- Assert.IsNotNull(exception);
- Assert.IsInstanceOfType(exception, typeof(MqttCommunicationException));
- //Assert.IsInstanceOfType(exception.InnerException, typeof(SocketException));
- }
-
- await Task.Delay(100); // disconnected handler is called async
- Assert.IsTrue(disconnectHandlerCalled);
- }
- }
-
- [TestMethod]
- public async Task Fire_Disconnected_Event_On_Server_Shutdown()
+ public async Task No_Payload()
{
using (var testEnvironment = new TestEnvironment(TestContext))
{
- var server = await testEnvironment.StartServer();
- var client = await testEnvironment.ConnectClient();
+ await testEnvironment.StartServer();
- var handlerFired = false;
- client.UseDisconnectedHandler(e => handlerFired = true);
+ var sender = await testEnvironment.ConnectClient();
+ var receiver = await testEnvironment.ConnectClient();
- await server.StopAsync();
+ var message = new MqttApplicationMessageBuilder().WithTopic("A");
- await Task.Delay(4000);
+ await receiver.SubscribeAsync(
+ new MqttClientSubscribeOptions
+ {
+ TopicFilters = new List { new MqttTopicFilter { Topic = "#" } }
+ },
+ CancellationToken.None);
- Assert.IsTrue(handlerFired);
+ MqttApplicationMessage receivedMessage = null;
+ receiver.ApplicationMessageReceivedAsync += e =>
+ {
+ receivedMessage = e.ApplicationMessage;
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ await sender.PublishAsync(message.Build(), CancellationToken.None);
+
+ await Task.Delay(1000);
+
+ Assert.IsNotNull(receivedMessage);
+ Assert.AreEqual("A", receivedMessage.Topic);
+ Assert.AreEqual(null, receivedMessage.Payload);
}
}
[TestMethod]
- public async Task Disconnect_Event_Contains_Exception()
+ public async Task NoConnectedHandler_Connect_DoesNotThrowException()
{
- var factory = new MqttFactory();
- using (var client = factory.CreateMqttClient())
+ using (var testEnvironment = new TestEnvironment(TestContext))
{
- Exception ex = null;
- client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(e =>
- {
- ex = e.Exception;
- });
+ await testEnvironment.StartServer();
- try
- {
- await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("wrong-server").Build());
- }
- catch
- {
- }
+ var client = await testEnvironment.ConnectClient();
- await Task.Delay(500);
+ Assert.IsTrue(client.IsConnected);
+ }
+ }
- Assert.IsNotNull(ex);
- Assert.IsInstanceOfType(ex, typeof(MqttCommunicationException));
- Assert.IsInstanceOfType(ex.InnerException, typeof(SocketException));
+ [TestMethod]
+ public async Task NoDisconnectedHandler_Disconnect_DoesNotThrowException()
+ {
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ await testEnvironment.StartServer();
+ var client = await testEnvironment.ConnectClient();
+ Assert.IsTrue(client.IsConnected);
+
+ await client.DisconnectAsync();
+
+ Assert.IsFalse(client.IsConnected);
+ }
+ }
+
+ [TestMethod]
+ public async Task PacketIdentifier_In_Publish_Result()
+ {
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ await testEnvironment.StartServer();
+ var client = await testEnvironment.ConnectClient();
+
+ var result = await client.PublishStringAsync("a", "a");
+ Assert.AreEqual(null, result.PacketIdentifier);
+
+ result = await client.PublishStringAsync("b", "b");
+ Assert.AreEqual(null, result.PacketIdentifier);
+
+ result = await client.PublishStringAsync("a", "a", MqttQualityOfServiceLevel.AtLeastOnce);
+ Assert.AreEqual((ushort)1, result.PacketIdentifier);
+
+ result = await client.PublishStringAsync("b", "b", MqttQualityOfServiceLevel.AtLeastOnce);
+ Assert.AreEqual((ushort)2, result.PacketIdentifier);
+
+ result = await client.PublishStringAsync("a", "a", MqttQualityOfServiceLevel.ExactlyOnce);
+ Assert.AreEqual((ushort)3, result.PacketIdentifier);
+
+ result = await client.PublishStringAsync("b", "b", MqttQualityOfServiceLevel.ExactlyOnce);
+ Assert.AreEqual((ushort)4, result.PacketIdentifier);
}
}
@@ -463,12 +407,12 @@ namespace MQTTnet.Tests.Client
}
}
- client1.UseApplicationMessageReceivedHandler(Handler1);
+ client1.ApplicationMessageReceivedAsync += Handler1;
var client2 = await testEnvironment.ConnectClient();
for (var i = MessagesCount; i > 0; i--)
{
- await client2.PublishAsync("x", i.ToString());
+ await client2.PublishStringAsync("x", i.ToString());
}
await Task.Delay(5000);
@@ -503,21 +447,21 @@ namespace MQTTnet.Tests.Client
eventArgs.AutoAcknowledge = false;
Task.Delay(value).ContinueWith(x => eventArgs.AcknowledgeAsync(CancellationToken.None));
- System.Diagnostics.Debug.WriteLine($"received {value}");
+ Debug.WriteLine($"received {value}");
lock (receivedValues)
{
receivedValues.Add(value);
}
- return Task.CompletedTask;
+ return PlatformAbstractionLayer.CompletedTask;
}
- client1.UseApplicationMessageReceivedHandler(Handler1);
+ client1.ApplicationMessageReceivedAsync += Handler1;
var client2 = await testEnvironment.ConnectClient();
for (var i = MessagesCount; i > 0; i--)
{
- await client2.PublishAsync("x", i.ToString(), MqttQualityOfServiceLevel.ExactlyOnce);
+ await client2.PublishStringAsync("x", i.ToString(), MqttQualityOfServiceLevel.ExactlyOnce);
}
await Task.Delay(5000);
@@ -529,124 +473,6 @@ namespace MQTTnet.Tests.Client
}
}
- [TestMethod]
- public async Task Send_Reply_For_Any_Received_Message()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- await testEnvironment.StartServer();
-
- var client1 = await testEnvironment.ConnectClient();
- await client1.SubscribeAsync("request/+");
-
- async Task Handler1(MqttApplicationMessageReceivedEventArgs eventArgs)
- {
- await client1.PublishAsync($"reply/{eventArgs.ApplicationMessage.Topic}");
- }
-
- client1.UseApplicationMessageReceivedHandler(Handler1);
-
- var client2 = await testEnvironment.ConnectClient();
- await client2.SubscribeAsync("reply/#");
-
- var replies = new List();
-
- void Handler2(MqttApplicationMessageReceivedEventArgs eventArgs)
- {
- lock (replies)
- {
- replies.Add(eventArgs.ApplicationMessage.Topic);
- }
- }
-
- client2.UseApplicationMessageReceivedHandler((Action)Handler2);
-
- await Task.Delay(500);
-
- await client2.PublishAsync("request/a");
- await client2.PublishAsync("request/b");
- await client2.PublishAsync("request/c");
-
- await Task.Delay(500);
-
- Assert.AreEqual("reply/request/a,reply/request/b,reply/request/c", string.Join(",", replies));
- }
- }
-
- [TestMethod]
- public async Task Publish_With_Correct_Retain_Flag()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- await testEnvironment.StartServer();
-
- var receivedMessages = new List();
-
- var client1 = await testEnvironment.ConnectClient();
- client1.UseApplicationMessageReceivedHandler(c =>
- {
- lock (receivedMessages)
- {
- receivedMessages.Add(c.ApplicationMessage);
- }
- });
-
- await client1.SubscribeAsync("a");
-
- var client2 = await testEnvironment.ConnectClient();
- var message = new MqttApplicationMessageBuilder().WithTopic("a").WithRetainFlag().Build();
- await client2.PublishAsync(message);
-
- await Task.Delay(500);
-
- Assert.AreEqual(1, receivedMessages.Count);
- Assert.IsFalse(receivedMessages.First().Retain); // Must be false even if set above!
- }
- }
-
- [TestMethod]
- public async Task Publish_QoS_1_In_ApplicationMessageReceiveHandler()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- await testEnvironment.StartServer();
-
- const string client1Topic = "client1/topic";
- const string client2Topic = "client2/topic";
- const string expectedClient2Message = "hello client2";
-
- var client1 = await testEnvironment.ConnectClient();
- client1.UseApplicationMessageReceivedHandler(async c =>
- {
- await client1.PublishAsync(client2Topic, expectedClient2Message, MqttQualityOfServiceLevel.AtLeastOnce);
- });
-
- await client1.SubscribeAsync(client1Topic, MqttQualityOfServiceLevel.AtLeastOnce);
-
- var client2 = await testEnvironment.ConnectClient();
-
- var client2TopicResults = new List();
-
- client2.UseApplicationMessageReceivedHandler(c =>
- {
- client2TopicResults.Add(Encoding.UTF8.GetString(c.ApplicationMessage.Payload));
- });
-
- await client2.SubscribeAsync(client2Topic);
-
- var client3 = await testEnvironment.ConnectClient();
- var message = new MqttApplicationMessageBuilder().WithTopic(client1Topic).Build();
- await client3.PublishAsync(message);
- await client3.PublishAsync(message);
-
- await Task.Delay(500);
-
- Assert.AreEqual(2, client2TopicResults.Count);
- Assert.AreEqual(expectedClient2Message, client2TopicResults[0]);
- Assert.AreEqual(expectedClient2Message, client2TopicResults[1]);
- }
- }
-
[TestMethod]
public async Task Publish_QoS_0_Over_Period_Exceeding_KeepAlive()
{
@@ -679,6 +505,364 @@ namespace MQTTnet.Tests.Client
}
}
+ [TestMethod]
+ public async Task Publish_QoS_1_In_ApplicationMessageReceiveHandler()
+ {
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ await testEnvironment.StartServer();
+
+ const string client1Topic = "client1/topic";
+ const string client2Topic = "client2/topic";
+ const string expectedClient2Message = "hello client2";
+
+ var client1 = await testEnvironment.ConnectClient();
+ client1.ApplicationMessageReceivedAsync += async e =>
+ {
+ await client1.PublishStringAsync(client2Topic, expectedClient2Message, MqttQualityOfServiceLevel.AtLeastOnce);
+ };
+
+ await client1.SubscribeAsync(client1Topic, MqttQualityOfServiceLevel.AtLeastOnce);
+
+ var client2 = await testEnvironment.ConnectClient();
+
+ var client2TopicResults = new List();
+
+ client2.ApplicationMessageReceivedAsync += e =>
+ {
+ client2TopicResults.Add(Encoding.UTF8.GetString(e.ApplicationMessage.Payload));
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ await client2.SubscribeAsync(client2Topic);
+
+ var client3 = await testEnvironment.ConnectClient();
+ var message = new MqttApplicationMessageBuilder().WithTopic(client1Topic).Build();
+ await client3.PublishAsync(message);
+ await client3.PublishAsync(message);
+
+ await Task.Delay(500);
+
+ Assert.AreEqual(2, client2TopicResults.Count);
+ Assert.AreEqual(expectedClient2Message, client2TopicResults[0]);
+ Assert.AreEqual(expectedClient2Message, client2TopicResults[1]);
+ }
+ }
+
+ [TestMethod]
+ public async Task Publish_With_Correct_Retain_Flag()
+ {
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ await testEnvironment.StartServer();
+
+ var receivedMessages = new List();
+
+ var client1 = await testEnvironment.ConnectClient();
+ client1.ApplicationMessageReceivedAsync += e =>
+ {
+ lock (receivedMessages)
+ {
+ receivedMessages.Add(e.ApplicationMessage);
+ }
+
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ await client1.SubscribeAsync("a");
+
+ var client2 = await testEnvironment.ConnectClient();
+ var message = new MqttApplicationMessageBuilder().WithTopic("a").WithRetainFlag().Build();
+ await client2.PublishAsync(message);
+
+ await Task.Delay(500);
+
+ Assert.AreEqual(1, receivedMessages.Count);
+ Assert.IsFalse(receivedMessages.First().Retain); // Must be false even if set above!
+ }
+ }
+
+ [TestMethod]
+ public async Task Reconnect()
+ {
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ var server = await testEnvironment.StartServer();
+ var client = await testEnvironment.ConnectClient();
+
+ await Task.Delay(500);
+ Assert.IsTrue(client.IsConnected);
+
+ await server.StopAsync();
+ await Task.Delay(500);
+ Assert.IsFalse(client.IsConnected);
+
+ await server.StartAsync();
+ await Task.Delay(500);
+
+ await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
+ Assert.IsTrue(client.IsConnected);
+ }
+ }
+
+ [TestMethod]
+ public async Task Reconnect_From_Disconnected_Event()
+ {
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ testEnvironment.IgnoreClientLogErrors = true;
+
+ var client = testEnvironment.CreateClient();
+
+ var tries = 0;
+ var maxTries = 3;
+
+ client.DisconnectedAsync += async e =>
+ {
+ if (tries >= maxTries)
+ {
+ return;
+ }
+
+ Interlocked.Increment(ref tries);
+
+ await Task.Delay(100);
+ await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
+ };
+
+ try
+ {
+ await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
+ Assert.Fail("Must fail!");
+ }
+ catch
+ {
+ }
+
+ SpinWait.SpinUntil(() => tries >= maxTries, 10000);
+
+ Assert.AreEqual(maxTries, tries);
+ }
+ }
+
+ [TestMethod]
+ public async Task Reconnect_While_Server_Offline()
+ {
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ testEnvironment.IgnoreClientLogErrors = true;
+
+ var server = await testEnvironment.StartServer();
+ var client = await testEnvironment.ConnectClient();
+
+ await Task.Delay(500);
+ Assert.IsTrue(client.IsConnected);
+
+ await server.StopAsync();
+ await Task.Delay(500);
+ Assert.IsFalse(client.IsConnected);
+
+ for (var i = 0; i < 5; i++)
+ {
+ try
+ {
+ await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
+ Assert.Fail("Must fail!");
+ }
+ catch
+ {
+ }
+ }
+
+ await server.StartAsync();
+ await Task.Delay(500);
+
+ await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
+ Assert.IsTrue(client.IsConnected);
+ }
+ }
+
+ [TestMethod]
+ public async Task Send_Manual_Ping()
+ {
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ await testEnvironment.StartServer();
+ var client = await testEnvironment.ConnectClient();
+
+ await client.PingAsync(CancellationToken.None);
+ }
+ }
+
+ [TestMethod]
+ public async Task Send_Reply_For_Any_Received_Message()
+ {
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ await testEnvironment.StartServer();
+
+ var client1 = await testEnvironment.ConnectClient();
+ await client1.SubscribeAsync("request/+");
+
+ async Task Handler1(MqttApplicationMessageReceivedEventArgs eventArgs)
+ {
+ await client1.PublishStringAsync($"reply/{eventArgs.ApplicationMessage.Topic}");
+ }
+
+ client1.ApplicationMessageReceivedAsync += Handler1;
+
+ var client2 = await testEnvironment.ConnectClient();
+ await client2.SubscribeAsync("reply/#");
+
+ var replies = new List();
+
+ Task Handler2(MqttApplicationMessageReceivedEventArgs eventArgs)
+ {
+ lock (replies)
+ {
+ replies.Add(eventArgs.ApplicationMessage.Topic);
+ }
+
+ return PlatformAbstractionLayer.CompletedTask;
+ }
+
+ client2.ApplicationMessageReceivedAsync += Handler2;
+
+ await Task.Delay(500);
+
+ await client2.PublishStringAsync("request/a");
+ await client2.PublishStringAsync("request/b");
+ await client2.PublishStringAsync("request/c");
+
+ await Task.Delay(500);
+
+ Assert.AreEqual("reply/request/a,reply/request/b,reply/request/c", string.Join(",", replies));
+ }
+ }
+
+ [TestMethod]
+ public async Task Send_Reply_In_Message_Handler()
+ {
+ using (var testEnvironment = new TestEnvironment())
+ {
+ await testEnvironment.StartServer();
+ var client1 = await testEnvironment.ConnectClient();
+ var client2 = await testEnvironment.ConnectClient();
+
+ await client1.SubscribeAsync("#");
+ await client2.SubscribeAsync("#");
+
+ var replyReceived = false;
+
+ client1.ApplicationMessageReceivedAsync += e =>
+ {
+ if (e.ApplicationMessage.Topic == "reply")
+ {
+ replyReceived = true;
+ }
+
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ client2.ApplicationMessageReceivedAsync += async e =>
+ {
+ if (e.ApplicationMessage.Topic == "request")
+ {
+ // Use AtMostOnce here because with QoS 1 or even QoS 2 the process waits for
+ // the ACK etc. The problem is that the SpinUntil below only waits until the
+ // flag is set. It does not wait until the client has sent the ACK
+ await client2.PublishStringAsync("reply");
+ }
+ };
+
+ await client1.PublishStringAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
+
+ await Task.Delay(500);
+
+ SpinWait.SpinUntil(() => replyReceived, TimeSpan.FromSeconds(10));
+
+ await Task.Delay(500);
+
+ Assert.IsTrue(replyReceived);
+ }
+ }
+
+ [TestMethod]
+ public async Task Send_Reply_In_Message_Handler_For_Same_Client()
+ {
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ await testEnvironment.StartServer();
+ var client = await testEnvironment.ConnectClient();
+
+ await client.SubscribeAsync("#");
+
+ var replyReceived = false;
+
+ client.ApplicationMessageReceivedAsync += e =>
+ {
+ if (e.ApplicationMessage.Topic == "request")
+ {
+#pragma warning disable 4014
+ Task.Run(() => client.PublishStringAsync("reply", null, MqttQualityOfServiceLevel.AtLeastOnce));
+#pragma warning restore 4014
+ }
+ else
+ {
+ replyReceived = true;
+ }
+
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ await client.PublishStringAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
+
+ SpinWait.SpinUntil(() => replyReceived, TimeSpan.FromSeconds(10));
+
+ Assert.IsTrue(replyReceived);
+ }
+ }
+
+ [TestMethod]
+ public async Task Set_ClientWasConnected_On_ClientDisconnect()
+ {
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ var server = await testEnvironment.StartServer();
+ var client = await testEnvironment.ConnectClient();
+
+ Assert.IsTrue(client.IsConnected);
+ client.DisconnectedAsync += e =>
+ {
+ Assert.IsTrue(e.ClientWasConnected);
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ await client.DisconnectAsync();
+ await Task.Delay(200);
+ }
+ }
+
+ [TestMethod]
+ public async Task Set_ClientWasConnected_On_ServerDisconnect()
+ {
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ var server = await testEnvironment.StartServer();
+ var client = await testEnvironment.ConnectClient();
+
+ Assert.IsTrue(client.IsConnected);
+ client.DisconnectedAsync += e =>
+ {
+ Assert.IsTrue(e.ClientWasConnected);
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ await server.StopAsync();
+ await Task.Delay(4000);
+ }
+ }
+
[TestMethod]
public async Task Subscribe_In_Callback_Events()
@@ -691,24 +875,25 @@ namespace MQTTnet.Tests.Client
var client = testEnvironment.CreateClient();
- client.ConnectedHandler = new MqttClientConnectedHandlerDelegate(async e =>
+ client.ConnectedAsync += async e =>
{
await client.SubscribeAsync("RCU/P1/H0001/R0003");
- var msg = new MqttApplicationMessageBuilder()
- .WithPayload("DA|18RS00SC00XI0000RV00R100R200R300R400L100L200L300L400Y100Y200AC0102031800BELK0000BM0000|")
+ var msg = new MqttApplicationMessageBuilder().WithPayload("DA|18RS00SC00XI0000RV00R100R200R300R400L100L200L300L400Y100Y200AC0102031800BELK0000BM0000|")
.WithTopic("RCU/P1/H0001/R0003");
await client.PublishAsync(msg.Build());
- });
+ };
- client.UseApplicationMessageReceivedHandler(c =>
+ client.ApplicationMessageReceivedAsync += e =>
{
lock (receivedMessages)
{
- receivedMessages.Add(c.ApplicationMessage);
+ receivedMessages.Add(e.ApplicationMessage);
}
- });
+
+ return PlatformAbstractionLayer.CompletedTask;
+ };
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).Build());
@@ -719,155 +904,6 @@ namespace MQTTnet.Tests.Client
}
}
- [TestMethod]
- public async Task Message_Send_Retry()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- testEnvironment.IgnoreClientLogErrors = true;
- testEnvironment.IgnoreServerLogErrors = true;
-
- await testEnvironment.StartServer(
- new MqttServerOptionsBuilder()
- .WithPersistentSessions()
- .WithDefaultCommunicationTimeout(TimeSpan.FromMilliseconds(250)));
-
- var client1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithCleanSession(false));
- await client1.SubscribeAsync("x", MqttQualityOfServiceLevel.AtLeastOnce);
-
- var retries = 0;
-
- async Task Handler1(MqttApplicationMessageReceivedEventArgs eventArgs)
- {
- retries++;
-
- await Task.Delay(1000);
- throw new Exception("Broken!");
- }
-
- client1.UseApplicationMessageReceivedHandler(Handler1);
-
- var client2 = await testEnvironment.ConnectClient();
- await client2.PublishAsync("x");
-
- await Task.Delay(3000);
-
- // The server should disconnect clients which are not responding.
- Assert.IsFalse(client1.IsConnected);
-
- await client1.ReconnectAsync().ConfigureAwait(false);
-
- await Task.Delay(1000);
-
- Assert.AreEqual(2, retries);
- }
- }
-
- [TestMethod]
- public async Task NoConnectedHandler_Connect_DoesNotThrowException()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- await testEnvironment.StartServer();
-
- var client = await testEnvironment.ConnectClient();
-
- Assert.IsTrue(client.IsConnected);
- }
- }
-
- [TestMethod]
- public async Task NoDisconnectedHandler_Disconnect_DoesNotThrowException()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- await testEnvironment.StartServer();
- var client = await testEnvironment.ConnectClient();
- Assert.IsTrue(client.IsConnected);
-
- await client.DisconnectAsync();
-
- Assert.IsFalse(client.IsConnected);
- }
- }
-
- [TestMethod]
- public async Task Frequent_Connects()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- await testEnvironment.StartServer();
-
- var clients = new List();
- for (var i = 0; i < 100; i++)
- {
- clients.Add(await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("a")));
- }
-
- await Task.Delay(500);
-
- var clientStatus = await testEnvironment.Server.GetClientStatusAsync();
- var sessionStatus = await testEnvironment.Server.GetSessionStatusAsync();
-
- for (var i = 0; i < 98; i++)
- {
- Assert.IsFalse(clients[i].IsConnected, $"clients[{i}] is not connected");
- }
-
- Assert.IsTrue(clients[99].IsConnected);
-
- Assert.AreEqual(1, clientStatus.Count);
- Assert.AreEqual(1, sessionStatus.Count);
-
- var receiveClient = clients[99];
- object receivedPayload = null;
- receiveClient.UseApplicationMessageReceivedHandler(e =>
- {
- receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
- });
-
- await receiveClient.SubscribeAsync("x");
-
- var sendClient = await testEnvironment.ConnectClient();
- await sendClient.PublishAsync("x", "1");
-
- await Task.Delay(250);
-
- Assert.AreEqual("1", receivedPayload);
- }
- }
-
- [TestMethod]
- public async Task No_Payload()
- {
- using (var testEnvironment = new TestEnvironment(TestContext))
- {
- await testEnvironment.StartServer();
-
- var sender = await testEnvironment.ConnectClient();
- var receiver = await testEnvironment.ConnectClient();
-
- var message = new MqttApplicationMessageBuilder()
- .WithTopic("A");
-
- await receiver.SubscribeAsync(new MqttClientSubscribeOptions
- {
- TopicFilters = new List { new MqttTopicFilter { Topic = "#" } }
- }, CancellationToken.None);
-
- MqttApplicationMessage receivedMessage = null;
- receiver.UseApplicationMessageReceivedHandler(e => receivedMessage = e.ApplicationMessage);
-
- await sender.PublishAsync(message.Build(), CancellationToken.None);
-
- await Task.Delay(1000);
-
- Assert.IsNotNull(receivedMessage);
- Assert.AreEqual("A", receivedMessage.Topic);
- Assert.AreEqual(null, receivedMessage.Payload);
- }
- }
-
[TestMethod]
public async Task Subscribe_With_QoS2()
{
@@ -878,23 +914,29 @@ namespace MQTTnet.Tests.Client
var client2 = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
var disconnectedFired = false;
- client1.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(c =>
+ client1.DisconnectedAsync += c =>
{
disconnectedFired = true;
- });
+ return PlatformAbstractionLayer.CompletedTask;
+ };
var messageReceived = false;
- client1.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(c =>
+ client1.ApplicationMessageReceivedAsync += e =>
{
messageReceived = true;
- });
+ return PlatformAbstractionLayer.CompletedTask;
+ };
await client1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("topic1").WithExactlyOnceQoS().Build());
await Task.Delay(500);
- var message = new MqttApplicationMessageBuilder().WithTopic("topic1").WithPayload("Hello World").WithExactlyOnceQoS().WithRetainFlag().Build();
-
+ var message = new MqttApplicationMessageBuilder().WithTopic("topic1")
+ .WithPayload("Hello World")
+ .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce)
+ .WithRetainFlag()
+ .Build();
+
await client2.PublishAsync(message);
await Task.Delay(500);
@@ -904,4 +946,4 @@ namespace MQTTnet.Tests.Client
}
}
}
-}
+}
\ No newline at end of file
diff --git a/Tests/MQTTnet.Core.Tests/Logger/Logger_Tests.cs b/Source/MQTTnet.Tests/Diagnostics/Logger_Tests.cs
similarity index 87%
rename from Tests/MQTTnet.Core.Tests/Logger/Logger_Tests.cs
rename to Source/MQTTnet.Tests/Diagnostics/Logger_Tests.cs
index 0e29362a..b5256236 100644
--- a/Tests/MQTTnet.Core.Tests/Logger/Logger_Tests.cs
+++ b/Source/MQTTnet.Tests/Diagnostics/Logger_Tests.cs
@@ -1,8 +1,12 @@
-using System;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using MQTTnet.Diagnostics.Logger;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
-namespace MQTTnet.Tests.Logger
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MQTTnet.Diagnostics;
+
+namespace MQTTnet.Tests.Diagnostics
{
[TestClass]
public sealed class Logger_Tests : BaseTestClass
diff --git a/Source/MQTTnet.Tests/Diagnostics/PacketInspection_Tests.cs b/Source/MQTTnet.Tests/Diagnostics/PacketInspection_Tests.cs
new file mode 100644
index 00000000..225f2cec
--- /dev/null
+++ b/Source/MQTTnet.Tests/Diagnostics/PacketInspection_Tests.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MQTTnet.Implementations;
+
+namespace MQTTnet.Tests.Diagnostics
+{
+ [TestClass]
+ public sealed class PacketInspection_Tests : BaseTestClass
+ {
+ [TestMethod]
+ public async Task Inspect_Client_Packets()
+ {
+ using (var testEnvironment = CreateTestEnvironment())
+ {
+ await testEnvironment.StartServer();
+
+ using (var mqttClient = testEnvironment.CreateClient())
+ {
+ var mqttClientOptions = testEnvironment.Factory.CreateClientOptionsBuilder()
+ .WithClientId("CLIENT_ID") // Must be fixed.
+ .WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
+ .Build();
+
+ var packets = new List();
+
+ mqttClient.InspectPackage += eventArgs =>
+ {
+ packets.Add(eventArgs.Direction + ":" + Convert.ToBase64String(eventArgs.Buffer));
+ return PlatformAbstractionLayer.CompletedTask;
+ };
+
+ await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
+
+ Assert.AreEqual(2, packets.Count);
+ Assert.AreEqual("Outbound:ECwABE1RVFQEAgAPACBJbnNwZWN0X0NsaWVudF9QYWNrZXRzX0NMSUVOVF9JRA==", packets[0]); // CONNECT
+ Assert.AreEqual("Inbound:IAIAAA==", packets[1]); // CONNACK
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tests/MQTTnet.Core.Tests/Logger/SourceLogger_Tests.cs b/Source/MQTTnet.Tests/Diagnostics/SourceLogger_Tests.cs
similarity index 66%
rename from Tests/MQTTnet.Core.Tests/Logger/SourceLogger_Tests.cs
rename to Source/MQTTnet.Tests/Diagnostics/SourceLogger_Tests.cs
index 1eb3d165..2785bc23 100644
--- a/Tests/MQTTnet.Core.Tests/Logger/SourceLogger_Tests.cs
+++ b/Source/MQTTnet.Tests/Diagnostics/SourceLogger_Tests.cs
@@ -1,7 +1,11 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using MQTTnet.Diagnostics.Logger;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
-namespace MQTTnet.Tests.Logger
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MQTTnet.Diagnostics;
+
+namespace MQTTnet.Tests.Diagnostics
{
[TestClass]
public sealed class SourceLogger_Tests : BaseTestClass
diff --git a/Tests/MQTTnet.Core.Tests/Extension_Tests.cs b/Source/MQTTnet.Tests/Extension_Tests.cs
similarity index 88%
rename from Tests/MQTTnet.Core.Tests/Extension_Tests.cs
rename to Source/MQTTnet.Tests/Extension_Tests.cs
index 68951000..fa72dbc3 100644
--- a/Tests/MQTTnet.Core.Tests/Extension_Tests.cs
+++ b/Source/MQTTnet.Tests/Extension_Tests.cs
@@ -1,4 +1,8 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -27,16 +31,7 @@ namespace MQTTnet.Tests
ct => Task.Delay(TimeSpan.FromMilliseconds(500), ct).ContinueWith(t => 5, ct),
TimeSpan.FromMilliseconds(100), CancellationToken.None);
}
-
- [TestMethod]
- public async Task TimeoutAfterCompleteInTime()
- {
- var result = await MqttTaskTimeout.WaitAsync(
- ct => Task.Delay(TimeSpan.FromMilliseconds(100), ct).ContinueWith(t => 5, ct),
- TimeSpan.FromMilliseconds(500), CancellationToken.None);
- Assert.AreEqual(5, result);
- }
-
+
[TestMethod]
public async Task TimeoutAfterWithInnerException()
{
diff --git a/Source/MQTTnet.Tests/Extensions/MqttPacketWriterExtensions.cs b/Source/MQTTnet.Tests/Extensions/MqttPacketWriterExtensions.cs
new file mode 100644
index 00000000..e254354f
--- /dev/null
+++ b/Source/MQTTnet.Tests/Extensions/MqttPacketWriterExtensions.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MQTTnet.Formatter;
+using MQTTnet.Protocol;
+
+namespace MQTTnet.Tests.Extensions
+{
+ public static class MqttPacketWriterExtensions
+ {
+ public static byte[] AddMqttHeader(this MqttBufferWriter writer, MqttControlPacketType header, byte[] body)
+ {
+ writer.WriteByte(MqttBufferWriter.BuildFixedHeader(header));
+ writer.WriteVariableByteInteger((uint)body.Length);
+ writer.WriteBinary(body, 0, body.Length);
+ return writer.GetBuffer();
+ }
+ }
+}
diff --git a/Tests/MQTTnet.Core.Tests/Factory/MqttFactory_Tests.cs b/Source/MQTTnet.Tests/Factory/MqttFactory_Tests.cs
similarity index 84%
rename from Tests/MQTTnet.Core.Tests/Factory/MqttFactory_Tests.cs
rename to Source/MQTTnet.Tests/Factory/MqttFactory_Tests.cs
index d7b5b274..b34d555e 100644
--- a/Tests/MQTTnet.Core.Tests/Factory/MqttFactory_Tests.cs
+++ b/Source/MQTTnet.Tests/Factory/MqttFactory_Tests.cs
@@ -1,9 +1,15 @@
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using MQTTnet.Diagnostics.Logger;
+using MQTTnet.Diagnostics;
using MQTTnet.Extensions.ManagedClient;
+using MQTTnet.Implementations;
+using MQTTnet.Server;
namespace MQTTnet.Tests.Factory
{
@@ -40,15 +46,20 @@ namespace MQTTnet.Tests.Factory
{
var clientOptions = new ManagedMqttClientOptionsBuilder();
- clientOptions.WithClientOptions(o => o.WithTcpServer("this_is_an_invalid_host").WithCommunicationTimeout(TimeSpan.FromSeconds(1)));
+ clientOptions.WithClientOptions(o => o.WithTcpServer("this_is_an_invalid_host").WithTimeout(TimeSpan.FromSeconds(1)));
// try connect to get some log entries
await managedClient.StartAsync(clientOptions.Build());
// wait at least connect timeout or we have some log messages
var tcs = new TaskCompletionSource