Compare commits

..
Author SHA1 Message Date
Christian 2ca838cbfa Fix build scripts. 2021-12-05 13:45:25 +01:00
Christian 37b598c009 Add support for net6.0. 2021-12-03 09:26:48 +01:00
Christian 49c5e3a9cc Align default values for connection options with MQTTv5 spec. 2021-12-01 22:14:51 +01:00
Christian 46b54fd751 Fix build errors. 2021-12-01 22:05:52 +01:00
Christian a9fcbd77e9 Merge branch 'master' into feature/Reason_Code_In_Subscription_Interceptor 2021-12-01 20:49:05 +01:00
Christian 5301d1b894 Fix build error. 2021-12-01 20:32:21 +01:00
Christian 2d1b4b68e6 Fix build warnings. 2021-12-01 20:28:59 +01:00
Christian 2da2658035 Refactor nuget package generation. 2021-12-01 20:20:33 +01:00
Christian 360b5644ad Remove Blazor test app. 2021-12-01 20:17:04 +01:00
Christian 24629449cb Update MQTTnet.sln.DotSettings 2021-11-28 12:48:20 +01:00
Christian c59969067c Adjust namespaces. 2021-11-20 10:48:18 +01:00
Christian 28310c69bb Fix build errors. 2021-11-13 10:28:56 +01:00
Christian b5b713570f Squashed commit of the following:
commit 2d94abdde3
Author: Christian <christian.kratky@googlemail.com>
Date:   Sat Nov 13 10:09:59 2021 +0100

    Update MQTTnet.nuspec

    Update docs.

commit f0ea13c07f
Author: logicaloud <47704763+logicaloud@users.noreply.github.com>
Date:   Sat Nov 13 22:05:54 2021 +1300

    Fix MQTT 3.1.1 session persistence when CleanSession=1 (#1294)

commit 47cd5aa3cd
Author: Volker Milbrandt <milbrandt@users.noreply.github.com>
Date:   Sat Nov 13 10:02:55 2021 +0100

    test ResultCode for invalid credentials (no userName but password) (#1270)

    fix order of parameters in exceptions AreEqual(expected, actual)

commit 258df6bb14
Author: HansM <HansM2013@gmx.de>
Date:   Thu Nov 11 20:10:35 2021 +0100

    Removed IDisposable from MqttClientConnection class. (#1288)

commit 85c3c0b352
Author: SeppPenner <HansM2013@gmx.de>
Date:   Mon Nov 8 08:50:59 2021 +0100

    MQTTnet.noUWP.sln: Removed no longer present MQTTnet.Server project.
2021-11-13 10:24:46 +01:00
Christian 7993db2d82 Merge branch 'master' of https://github.com/chkr1011/MQTTnet into feature/Reason_Code_In_Subscription_Interceptor
# Conflicts:
#	Build/MQTTnet.nuspec
2021-11-01 17:36:04 +01:00
Christian 22ccdf10c6 Migrate retained messages to the event based approach. 2021-11-01 11:20:46 +01:00
Christian 9ab85b3592 Merge branch 'master' of https://github.com/chkr1011/MQTTnet into feature/Reason_Code_In_Subscription_Interceptor
# Conflicts:
#	Source/MQTTnet/Server/Internal/MqttClientConnectionStatistics.cs
2021-10-30 10:19:08 +02:00
Christian 9d982eab5e Fix UnitTests. 2021-10-29 22:35:32 +02:00
Christian 6bad9f183c Merge branch 'master' of https://github.com/chkr1011/MQTTnet into feature/Reason_Code_In_Subscription_Interceptor
# Conflicts:
#	Build/MQTTnet.nuspec
#	Source/MQTTnet/Server/Internal/MqttClientSessionsManager.cs
2021-10-29 21:58:57 +02:00
Christian d5237bb3f5 Merge master. 2021-10-27 23:30:52 +02:00
Christian 1c6d4b9484 Remove obsolete code. 2021-10-27 23:27:18 +02:00
Christian a3f6a501dc Add interceptor for all packets. 2021-10-27 23:11:20 +02:00
Christian 369b262e07 Restore undelivered event. 2021-10-27 22:10:42 +02:00
Christian d411aa4f97 Move events to server level instead of options. 2021-10-27 22:01:45 +02:00
Christian 7297336786 Fix packet bus cancellation. 2021-10-26 22:12:34 +02:00
Christian 905306c312 Add more handling options to interceptor contexts. 2021-10-26 22:00:47 +02:00
Christian 131afc6ff2 Delete wrong Unit Tests. 2021-10-24 21:37:44 +02:00
Christian 43e4038212 Refactor code. 2021-10-24 21:19:40 +02:00
Christian 5af54963a0 Merge branch 'master' of https://github.com/chkr1011/MQTTnet into feature/Reason_Code_In_Subscription_Interceptor
# Conflicts:
#	Source/MQTTnet/Server/Internal/MqttClientSessionsManager.cs
2021-10-23 13:16:57 +02:00
Christian 0d985cff12 Remove obsolete data converter. 2021-10-23 13:12:02 +02:00
Christian 5974e2644e Create more factories for packets. 2021-10-23 10:58:21 +02:00
Christian 47bc8b4d31 Update docs. 2021-10-23 10:37:31 +02:00
Christian 1677db286c Start migration of server to new packet bus. 2021-10-22 22:44:06 +02:00
Christian 3835deb3d1 Refactor code. 2021-10-21 19:23:58 +02:00
Christian 212d88f167 Add new APIs. 2021-10-21 19:15:57 +02:00
Christian a15fbf532e Add session to subscribe interceptor. 2021-10-14 21:50:16 +02:00
Christian 87df9367a7 Improve Unit Tests. 2021-10-10 12:20:24 +02:00
Christian 8c9460337f Extend interceptors. 2021-10-10 10:56:14 +02:00
Christian db00682c75 Improve topic comparer. 2021-10-02 20:44:12 +02:00
798 changed files with 31352 additions and 43906 deletions
+3
View File
@@ -0,0 +1,3 @@
component_depth: 2
languages:
- csharp
+6 -5
View File
@@ -21,14 +21,14 @@ dotnet_naming_rule.static_readonly_rule.symbols = static_readonly_symbols
dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
dotnet_naming_style.lower_camel_case_style.required_prefix = _
dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case
dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
dotnet_naming_symbols.constants_symbols.applicable_kinds = field
dotnet_naming_symbols.constants_symbols.required_modifiers = const
dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field
dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field
dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static, readonly
dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
@@ -40,6 +40,7 @@ dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
# ReSharper properties
resharper_autodetect_indent_settings = true
resharper_default_private_modifier = implicit
resharper_place_accessorholder_attribute_on_same_line = false
resharper_use_indent_from_vs = false
@@ -60,7 +61,7 @@ resharper_web_config_wrong_module_highlighting = warning
[*]
charset = utf-8
end_of_line = crlf
trim_trailing_whitespace = true
trim_trailing_whitespace = false
insert_final_newline = false
indent_style = space
indent_size = 4
+63 -1
View File
@@ -1 +1,63 @@
* text=auto
###############################################################################
# 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
+5 -16
View File
@@ -7,25 +7,16 @@ assignees: ''
---
### Verification
Before opening a bug make sure that the following conditions are met.
<!-- Remove this chapter when all 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 component is your bug related to?
<!-- Remove the items which don't apply from the following list. -->
### Which project is your bug related to?
<!-- Remove the items which don't apply from the following list -->
- Client
- ManagedClient
- RpcClient
- MQTTnet.Server standalone
- Server
- Generic
### To Reproduce
Steps to reproduce the behavior:
@@ -48,9 +39,7 @@ 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.
**Ideally a Unit Test (which shows the error) is provided so that the behavior can be reproduced easily.**
Please provide full code examples below where possible to make it easier for the developers to check your issues.
```csharp
\\ Put your code here.
+1
View File
@@ -14,5 +14,6 @@ A clear and concise description of what you want to know.
<!-- Remove the items which don't apply from the following list -->
- Client
- ManagedClient
- MQTTnet.Server standalone
- Server
- Generic
@@ -15,6 +15,7 @@ A clear and concise description of what the problem is. Example. I'm am trying t
<!-- Remove the items which don't apply from the following list -->
- Client
- ManagedClient
- MQTTnet.Server standalone
- Server
- Generic
-132
View File
@@ -1,132 +0,0 @@
name: CI
on: [push, pull_request]
env:
VERSION: "5.2.0.${{github.run_number}}"
PACKAGE_SUFFIX: ""
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
10.0.x
- name: Checkout code
uses: actions/checkout@v4
- name: Load strong name certificate
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
run: |
echo "$SNC_BASE64" | base64 --decode > "${{ github.workspace }}/certificate.snk"
shell: bash
env:
SNC_BASE64: ${{ secrets.SNC_BASE64 }}
- name: Build package (signed)
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
run: dotnet build MQTTnet.slnx --configuration Release /p:FileVersion=${{ env.VERSION }} /p:AssemblyVersion=${{ env.VERSION }} /p:PackageVersion=${{ env.VERSION }}${{ env.PACKAGE_SUFFIX }} /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=${{ github.workspace }}/certificate.snk
- name: Build package (unsigned)
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }}
run: dotnet build MQTTnet.slnx --configuration Release /p:FileVersion=${{ env.VERSION }} /p:AssemblyVersion=${{ env.VERSION }} /p:PackageVersion=${{ env.VERSION }}${{ env.PACKAGE_SUFFIX }}
- name: Upload nuget packages
uses: actions/upload-artifact@v4
with:
name: nugets
path: |
${{ github.workspace }}/Source/**/*.nupkg
${{ github.workspace }}/Source/**/*.snupkg
test:
runs-on: ubuntu-latest
steps:
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
10.0.x
- name: Checkout code
uses: actions/checkout@v4
- name: Execute tests
run: dotnet test --framework net10.0 --configuration Release --project Source/MQTTnet.Tests/MQTTnet.Tests.csproj
sign:
needs: build
runs-on: windows-latest # Code signing must run on a Windows agent for Authenticode signing (dll/exe)
if: github.repository == 'dotnet/MQTTnet' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
steps:
- name: Download nuget packages
uses: actions/download-artifact@v4
with:
name: nugets
path: nugets
- name: Install sign CLI tool
run: dotnet tool install --tool-path . sign --version 0.9.1-beta.25379.1
- name: Sign nugets
shell: pwsh
run: >
./sign code azure-key-vault `
"**/*.nupkg" `
--base-directory "${{ github.workspace }}\nugets" `
--publisher-name "MQTTnet" `
--description "MQTTnet" `
--description-url "https://github.com/dotnet/MQTTnet" `
--azure-key-vault-tenant-id "${{ secrets.AZURE_TENANT_ID }}" `
--azure-key-vault-client-id "${{ secrets.AZURE_CLIENT_ID }}" `
--azure-key-vault-client-secret "${{ secrets.AZURE_CLIENT_SECRET }}" `
--azure-key-vault-certificate "${{ secrets.KEY_VAULT_CERTIFICATE_ID }}" `
--azure-key-vault-url "${{ secrets.KEY_VAULT_URL }}"
- name: Upload signed nuget packages
uses: actions/upload-artifact@v4
with:
name: signed-nugets
path: |
${{ github.workspace }}\nugets\**\*.nupkg
${{ github.workspace }}\nugets\**\*.snupkg
publish-myget:
if: ${{ github.event_name == 'push' }}
needs: sign
runs-on: ubuntu-latest
steps:
- name: Download signed nuget packages
uses: actions/download-artifact@v4
with:
name: signed-nugets
path: nugets
- name: Publish myget.com nugets
run: dotnet nuget push ${{ github.workspace }}/nugets/**/*.nupkg -k ${{ secrets.MYGET_API_KEY }} -s https://www.myget.org/F/mqttnet/api/v3/index.json --skip-duplicate
publish-nuget:
# Tag schema is 'v5.1.0-rc' etc.
if: startsWith(github.ref, 'refs/tags/v')
needs: [sign, test]
runs-on: ubuntu-latest
steps:
- name: Download signed nuget packages
uses: actions/download-artifact@v4
with:
name: signed-nugets
path: nugets
- name: Publish nuget.org nugets
run: dotnet nuget push ${{ github.workspace }}/nugets/**/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate
+67
View File
@@ -0,0 +1,67 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '23 9 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'csharp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+62
View File
@@ -0,0 +1,62 @@
# This is a basic workflow to help you get started with Actions
name: Deb Packaging
# Controls when the action will run.
on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Parse version
run: echo VERSION=$(grep '<Version>' Source/MQTTnet.Server/MQTTnet.Server.csproj | grep [0-9\.]* -o) >> $GITHUB_ENV
- name: Build MQTTnetServer
run: dotnet publish Source/MQTTnet.Server/MQTTnet.Server.csproj --configuration Release --self-contained --runtime linux-x64 --framework net5.0
- name: Set up installation directory
run: mkdir -p packaging/opt/MQTTnetServer
- name: Copy meta data
run: |
cp -R Build/deb-meta/* packaging/
chmod 755 packaging/DEBIAN/postinst
chmod 755 packaging/DEBIAN/postrm
chmod 755 packaging/DEBIAN/prerm
- name: Move artifacts to packaging directory
run: cp -R Source/MQTTnet.Server/bin/Release/net5.0/linux-x64/publish/* packaging/opt/MQTTnetServer
- name: Adjust files
run: |
rm packaging/opt/MQTTnetServer/appsettings.Development.json
mv packaging/opt/MQTTnetServer/appsettings.json packaging/opt/MQTTnetServer/appsettings.template.json
- name: Adjust permissions
run: |
cd packaging/opt/MQTTnetServer
find . -type f | xargs chmod -R 644
chmod 755 MQTTnet.Server
- name: Generate MD5s
run: |
cd packaging/
md5sum $(find * -type f -not -path 'DEBIAN/*') > DEBIAN/md5sums
- name: Patch meta
run: sed -i 's/\VERSIONPLACEHOLDER/${{ env.VERSION }}/' packaging/DEBIAN/control
- name: Package everything
run: dpkg-deb -v --build packaging/ mqttnet-server_${{ env.VERSION }}-1_amd64.deb
- name: Save artifact
uses: actions/upload-artifact@v2
with:
name: mqttnet-server
path: mqttnet-server_${{ env.VERSION }}-1_amd64.deb
+41
View File
@@ -0,0 +1,41 @@
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
Write-Host
Write-Host "Assembly version = $assemblyVersion"
Write-Host "Nuget version = $nugetVersion"
Write-Host "MSBuild path = $msbuild"
Write-Host
# Cleanup
Get-ChildItem -Path ".\..\" -Filter "*.nupkg" -Recurse | Remove-Item
Get-ChildItem -Path ".\..\" -Filter "*.snupkg" -Recurse | Remove-Item
# Build and execute tests
&$msbuild ..\Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj /t:Clean /t:Restore /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /verbosity:m
&$msbuild ..\Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj /t:Clean /t:Restore /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /verbosity:m
vstest.console.exe ..\Tests\MQTTnet.Core.Tests\bin\Release\net5.0\MQTTnet.Tests.dll
vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\net5.0\MQTTnet.AspNetCore.Tests.dll
$certificate = ".\..\..\Build\codeSigningKey.pfx"
# Build the core library
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Clean /t:Restore /t:Build /p:Configuration="Release" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /p:PackageVersion=$nugetVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=$certificate
# Build the ASP.NET extension
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Clean /t:Restore /t:Build /p:Configuration="Release" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /p:PackageVersion=$nugetVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=$certificate
# Build the RPC extension
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Clean /t:Restore /t:Build /p:Configuration="Release" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /p:PackageVersion=$nugetVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=$certificate
# Build the Managed Client extension
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Clean /t:Restore /t:Build /p:Configuration="Release" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /p:PackageVersion=$nugetVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=$certificate
# Build the WebSocket4Net extension
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Clean /t:Restore /t:Build /p:Configuration="Release" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /p:PackageVersion=$nugetVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=$certificate
Binary file not shown.
+13
View File
@@ -0,0 +1,13 @@
param([string]$apiKey)
Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile "nuget.exe"
$files = Get-ChildItem -Path ".\..\Source" -Recurse -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
-6
View File
@@ -1,6 +0,0 @@
# Code of Conduct
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).
-34
View File
@@ -1,34 +0,0 @@
<Project>
<PropertyGroup>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
<Product>MQTTnet</Product>
<Authors>The contributors of MQTTnet</Authors>
<Company>The contributors of MQTTnet</Company>
<Copyright>Copyright (c) .NET Foundation and Contributors</Copyright>
<DelaySign>false</DelaySign>
<SignAssembly>false</SignAssembly>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<NeutralLanguage>en-US</NeutralLanguage>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>1591;NETSDK1138;NU1803;NU1901;NU1902</NoWarn>
<NuGetAuditMode>all</NuGetAuditMode>
<NuGetAudit>true</NuGetAudit>
<NuGetAuditLevel>low</NuGetAuditLevel>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>latest</AnalysisLevel>
<AnalysisMode>recommended</AnalysisMode>
<AnalysisModeStyle>default</AnalysisModeStyle>
</PropertyGroup>
<PropertyGroup Condition="'$(GITHUB_ACTIONS)'=='true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
</Project>
-24
View File
@@ -1,24 +0,0 @@
<Project>
<PropertyGroup Condition="'$(GeneratePackageOnBuild)'=='true'">
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageIcon>nuget.png</PackageIcon>
<PackageProjectUrl>https://github.com/dotnet/MQTTnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/dotnet/MQTTnet.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>
<ItemGroup Condition="'$(GeneratePackageOnBuild)'=='true'">
<None Include="README.md" Pack="true" PackagePath="" Condition="Exists('README.md')" />
<None Include="..\..\Images\nuget.png" Pack="true" PackagePath="" />
<None Include="..\..\LICENSE" Pack="true" PackagePath="" />
</ItemGroup>
<ItemGroup Condition="'$(IncludeSymbols)'=='true'">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
</ItemGroup>
</Project>
+9
View File
@@ -0,0 +1,9 @@
# Import codeSigningKey.pfx
In order to import the key for code signing on a new developer machine use the following command within the VS developer command line:
sn –i .\codeSigningKey.pfx VS_KEY_62CA73019ED23333
The container name may be different.
# Check if the assembly has a strong name
sn -vf MQTTnet.dll
+2 -3
View File
@@ -1,7 +1,6 @@
The MIT License (MIT)
MIT License
Copyright (c) .NET Foundation and Contributors
All Rights Reserved
Copyright (c) 2016-2019 Christian Kratky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+258
View File
@@ -0,0 +1,258 @@
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
+239
View File
@@ -0,0 +1,239 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# 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\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
ReleaseNotes.md = ReleaseNotes.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
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
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} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
{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}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
EndGlobalSection
EndGlobal
+217
View File
@@ -0,0 +1,217 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_METHOD_CALL_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_METHOD_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">180</s:Int64>
<s:String x:Key="/Default/CodeStyle/CSharpFileLayoutPatterns/Pattern/@EntryValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&#xD;
&lt;Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"&gt;&#xD;
&lt;TypePattern DisplayName="Non-reorderable types"&gt;&#xD;
&lt;TypePattern.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Interface" /&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /&gt;&#xD;
&lt;HasAttribute Name="System.Runtime.InteropServices.ComImport" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;Kind Is="Struct" /&gt;&#xD;
&lt;HasAttribute Name="JetBrains.Annotations.NoReorderAttribute" /&gt;&#xD;
&lt;HasAttribute Name="JetBrains.Annotations.NoReorder" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/TypePattern.Match&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="xUnit.net Test Classes" RemoveRegions="All"&gt;&#xD;
&lt;TypePattern.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Class" /&gt;&#xD;
&lt;HasMember&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;HasAttribute Name="Xunit.FactAttribute" Inherited="True" /&gt;&#xD;
&lt;HasAttribute Name="Xunit.TheoryAttribute" Inherited="True" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/HasMember&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/TypePattern.Match&gt;&#xD;
&lt;Entry DisplayName="Setup/Teardown Methods"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Kind Is="Constructor" /&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;ImplementsInterface Name="System.IDisposable" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Kind Order="Constructor" /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="All other members" /&gt;&#xD;
&lt;Entry Priority="100" DisplayName="Test Methods"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;HasAttribute Name="Xunit.FactAttribute" /&gt;&#xD;
&lt;HasAttribute Name="Xunit.TheoryAttribute" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All"&gt;&#xD;
&lt;TypePattern.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Class" /&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestFixtureSourceAttribute" Inherited="True" /&gt;&#xD;
&lt;HasMember&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestAttribute" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestCaseAttribute" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/HasMember&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/TypePattern.Match&gt;&#xD;
&lt;Entry DisplayName="Setup/Teardown Methods"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestFixtureSetUpAttribute" Inherited="True" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestFixtureTearDownAttribute" Inherited="True" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.OneTimeSetUpAttribute" Inherited="True" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.OneTimeTearDownAttribute" Inherited="True" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="All other members" /&gt;&#xD;
&lt;Entry Priority="100" DisplayName="Test Methods"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestAttribute" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestCaseAttribute" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="Default Pattern"&gt;&#xD;
&lt;Entry Priority="100" DisplayName="Public Delegates"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;Kind Is="Delegate" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry Priority="100" DisplayName="Public Enums"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;Kind Is="Enum" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Static Fields and Constants"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Kind Is="Constant" /&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Field" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Kind Order="Constant Field" /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Fields"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Field" /&gt;&#xD;
&lt;Not&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/Not&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Readonly /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Constructors"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Constructor" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Events"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Event" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Properties, Indexers"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Kind Is="Property" /&gt;&#xD;
&lt;Kind Is="Indexer" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="All other members"&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal Protected Private" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Nested Types"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Type" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;/Patterns&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
-28
View File
@@ -1,28 +0,0 @@
<Solution>
<Configurations>
<Platform Name="Any CPU" />
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Folder Name="/Solution Items/">
<File Path=".github/workflows/ci.yml" />
<File Path="CODE-OF-CONDUCT.md" />
<File Path="Directory.Build.props" />
<File Path="Directory.Build.targets" />
<File Path="LICENSE" />
<File Path="README.md" />
<File Path="Source/ReleaseNotes.md" />
</Folder>
<Project Path="Samples/MQTTnet.Samples.csproj" />
<Project Path="Source/MQTTnet.AspnetCore/MQTTnet.AspNetCore.csproj" />
<Project Path="Source/MQTTnet.AspTestApp/MQTTnet.AspTestApp.csproj" />
<Project Path="Source/MQTTnet.Benchmarks/MQTTnet.Benchmarks.csproj" />
<Project Path="Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj" />
<Project Path="Source/MQTTnet.Extensions.Socks5/MQTTnet.Extensions.Socks5.csproj" />
<Project Path="Source/MQTTnet.Extensions.TopicTemplate/MQTTnet.Extensions.TopicTemplate.csproj" />
<Project Path="Source/MQTTnet.Server/MQTTnet.Server.csproj" />
<Project Path="Source/MQTTnet.TestApp/MQTTnet.TestApp.csproj" />
<Project Path="Source/MQTTnet.Tests.Extensions.Socks5/MQTTnet.Tests.Extensions.Socks5.csproj" />
<Project Path="Source/MQTTnet.Tests/MQTTnet.Tests.csproj" />
<Project Path="Source/MQTTnet/MQTTnet.csproj" />
</Solution>
-260
View File
@@ -1,260 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertIfStatementToSwitchStatement/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=InconsistentNaming/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UncatchableException/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnthrowableException/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_EMBEDDED_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_EXPR_MEMBER_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_INITIALIZER_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_INVOCATION_PARENS_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_PROPERTY_PATTERNS_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_USER_LINEBREAKS/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ANONYMOUSMETHOD_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_EMBEDDED_STATEMENT_ON_SAME_LINE/@EntryValue">NEVER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_INITIALIZER_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_PROPERTY_PATTERN_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_METHOD_CALL_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_METHOD_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_DECLARATION_LPAR/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_INVOCATION_LPAR/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARGUMENTS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARRAY_INITIALIZER_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_CHAINED_METHOD_CALLS/@EntryValue">CHOP_IF_LONG</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_EXTENDS_LIST_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">180</s:Int64>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_PARAMETERS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/KeepUserLineBreaks/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/ProcessingInstructionAttributesFormat/@EntryValue">OnDifferentLines</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/TagAttributesFormat/@EntryValue">OnDifferentLines</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpFileLayoutPatterns/Pattern/@EntryValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;
&lt;Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"&gt;
&lt;TypePattern DisplayName="Non-reorderable types"&gt;
&lt;TypePattern.Match&gt;
&lt;Or&gt;
&lt;And&gt;
&lt;Kind Is="Interface" /&gt;
&lt;Or&gt;
&lt;HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /&gt;
&lt;HasAttribute Name="System.Runtime.InteropServices.ComImport" /&gt;
&lt;/Or&gt;
&lt;/And&gt;
&lt;Kind Is="Struct" /&gt;
&lt;HasAttribute Name="JetBrains.Annotations.NoReorderAttribute" /&gt;
&lt;HasAttribute Name="JetBrains.Annotations.NoReorder" /&gt;
&lt;/Or&gt;
&lt;/TypePattern.Match&gt;
&lt;/TypePattern&gt;
&lt;TypePattern DisplayName="xUnit.net Test Classes" RemoveRegions="All"&gt;
&lt;TypePattern.Match&gt;
&lt;And&gt;
&lt;Kind Is="Class" /&gt;
&lt;HasMember&gt;
&lt;And&gt;
&lt;Kind Is="Method" /&gt;
&lt;HasAttribute Name="Xunit.FactAttribute" Inherited="True" /&gt;
&lt;HasAttribute Name="Xunit.TheoryAttribute" Inherited="True" /&gt;
&lt;/And&gt;
&lt;/HasMember&gt;
&lt;/And&gt;
&lt;/TypePattern.Match&gt;
&lt;Entry DisplayName="Setup/Teardown Methods"&gt;
&lt;Entry.Match&gt;
&lt;Or&gt;
&lt;Kind Is="Constructor" /&gt;
&lt;And&gt;
&lt;Kind Is="Method" /&gt;
&lt;ImplementsInterface Name="System.IDisposable" /&gt;
&lt;/And&gt;
&lt;/Or&gt;
&lt;/Entry.Match&gt;
&lt;Entry.SortBy&gt;
&lt;Kind Order="Constructor" /&gt;
&lt;/Entry.SortBy&gt;
&lt;/Entry&gt;
&lt;Entry DisplayName="All other members" /&gt;
&lt;Entry Priority="100" DisplayName="Test Methods"&gt;
&lt;Entry.Match&gt;
&lt;And&gt;
&lt;Kind Is="Method" /&gt;
&lt;HasAttribute Name="Xunit.FactAttribute" /&gt;
&lt;HasAttribute Name="Xunit.TheoryAttribute" /&gt;
&lt;/And&gt;
&lt;/Entry.Match&gt;
&lt;Entry.SortBy&gt;
&lt;Name /&gt;
&lt;/Entry.SortBy&gt;
&lt;/Entry&gt;
&lt;/TypePattern&gt;
&lt;TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All"&gt;
&lt;TypePattern.Match&gt;
&lt;And&gt;
&lt;Kind Is="Class" /&gt;
&lt;Or&gt;
&lt;HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" /&gt;
&lt;HasAttribute Name="NUnit.Framework.TestFixtureSourceAttribute" Inherited="True" /&gt;
&lt;HasMember&gt;
&lt;And&gt;
&lt;Kind Is="Method" /&gt;
&lt;HasAttribute Name="NUnit.Framework.TestAttribute" /&gt;
&lt;HasAttribute Name="NUnit.Framework.TestCaseAttribute" /&gt;
&lt;HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" /&gt;
&lt;/And&gt;
&lt;/HasMember&gt;
&lt;/Or&gt;
&lt;/And&gt;
&lt;/TypePattern.Match&gt;
&lt;Entry DisplayName="Setup/Teardown Methods"&gt;
&lt;Entry.Match&gt;
&lt;And&gt;
&lt;Kind Is="Method" /&gt;
&lt;Or&gt;
&lt;HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" /&gt;
&lt;HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" /&gt;
&lt;HasAttribute Name="NUnit.Framework.TestFixtureSetUpAttribute" Inherited="True" /&gt;
&lt;HasAttribute Name="NUnit.Framework.TestFixtureTearDownAttribute" Inherited="True" /&gt;
&lt;HasAttribute Name="NUnit.Framework.OneTimeSetUpAttribute" Inherited="True" /&gt;
&lt;HasAttribute Name="NUnit.Framework.OneTimeTearDownAttribute" Inherited="True" /&gt;
&lt;/Or&gt;
&lt;/And&gt;
&lt;/Entry.Match&gt;
&lt;/Entry&gt;
&lt;Entry DisplayName="All other members" /&gt;
&lt;Entry Priority="100" DisplayName="Test Methods"&gt;
&lt;Entry.Match&gt;
&lt;And&gt;
&lt;Kind Is="Method" /&gt;
&lt;HasAttribute Name="NUnit.Framework.TestAttribute" /&gt;
&lt;HasAttribute Name="NUnit.Framework.TestCaseAttribute" /&gt;
&lt;HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" /&gt;
&lt;/And&gt;
&lt;/Entry.Match&gt;
&lt;Entry.SortBy&gt;
&lt;Name /&gt;
&lt;/Entry.SortBy&gt;
&lt;/Entry&gt;
&lt;/TypePattern&gt;
&lt;TypePattern DisplayName="Default Pattern"&gt;
&lt;Entry Priority="100" DisplayName="Public Delegates"&gt;
&lt;Entry.Match&gt;
&lt;And&gt;
&lt;Access Is="Public" /&gt;
&lt;Kind Is="Delegate" /&gt;
&lt;/And&gt;
&lt;/Entry.Match&gt;
&lt;Entry.SortBy&gt;
&lt;Name /&gt;
&lt;/Entry.SortBy&gt;
&lt;/Entry&gt;
&lt;Entry Priority="100" DisplayName="Public Enums"&gt;
&lt;Entry.Match&gt;
&lt;And&gt;
&lt;Access Is="Public" /&gt;
&lt;Kind Is="Enum" /&gt;
&lt;/And&gt;
&lt;/Entry.Match&gt;
&lt;Entry.SortBy&gt;
&lt;Name /&gt;
&lt;/Entry.SortBy&gt;
&lt;/Entry&gt;
&lt;Entry DisplayName="Static Fields and Constants"&gt;
&lt;Entry.Match&gt;
&lt;Or&gt;
&lt;Kind Is="Constant" /&gt;
&lt;And&gt;
&lt;Kind Is="Field" /&gt;
&lt;Static /&gt;
&lt;/And&gt;
&lt;/Or&gt;
&lt;/Entry.Match&gt;
&lt;Entry.SortBy&gt;
&lt;Kind Order="Constant Field" /&gt;
&lt;/Entry.SortBy&gt;
&lt;/Entry&gt;
&lt;Entry DisplayName="Fields"&gt;
&lt;Entry.Match&gt;
&lt;And&gt;
&lt;Kind Is="Field" /&gt;
&lt;Not&gt;
&lt;Static /&gt;
&lt;/Not&gt;
&lt;/And&gt;
&lt;/Entry.Match&gt;
&lt;Entry.SortBy&gt;
&lt;Readonly /&gt;
&lt;Name /&gt;
&lt;/Entry.SortBy&gt;
&lt;/Entry&gt;
&lt;Entry DisplayName="Constructors"&gt;
&lt;Entry.Match&gt;
&lt;Kind Is="Constructor" /&gt;
&lt;/Entry.Match&gt;
&lt;Entry.SortBy&gt;
&lt;Static /&gt;
&lt;/Entry.SortBy&gt;
&lt;/Entry&gt;
&lt;Entry DisplayName="Events"&gt;
&lt;Entry.Match&gt;
&lt;Kind Is="Event" /&gt;
&lt;/Entry.Match&gt;
&lt;Entry.SortBy&gt;
&lt;Name /&gt;
&lt;/Entry.SortBy&gt;
&lt;/Entry&gt;
&lt;Entry DisplayName="Properties, Indexers"&gt;
&lt;Entry.Match&gt;
&lt;Or&gt;
&lt;Kind Is="Property" /&gt;
&lt;Kind Is="Indexer" /&gt;
&lt;/Or&gt;
&lt;/Entry.Match&gt;
&lt;Entry.SortBy&gt;
&lt;Access Order="Public Internal Protected Private" /&gt;
&lt;Name /&gt;
&lt;/Entry.SortBy&gt;
&lt;/Entry&gt;
&lt;Entry DisplayName="All other members"&gt;
&lt;Entry.SortBy&gt;
&lt;Access Order="Public Internal Protected Private" /&gt;
&lt;Name /&gt;
&lt;/Entry.SortBy&gt;
&lt;/Entry&gt;
&lt;Entry DisplayName="Nested Types"&gt;
&lt;Entry.Match&gt;
&lt;Kind Is="Type" /&gt;
&lt;/Entry.Match&gt;
&lt;/Entry&gt;
&lt;/TypePattern&gt;
&lt;/Patterns&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">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.</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECodeCleanup_002EFileHeader_002EFileHeaderSettingsMigrate/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002EMemberReordering_002EMigrations_002ECSharpFileLayoutPatternRemoveIsAttributeUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=catched/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=CONNACK/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGRESP/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBACK/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBCOMP/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBREC/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBREL/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=SUBACK/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Tnet/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=UNSUBACK/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
+103 -33
View File
@@ -1,20 +1,18 @@
<p align="center">
<img src="https://github.com/dotnet/MQTTnet/blob/master/Images/icon_det_256.png?raw=true" width="196">
<img src="https://github.com/chkr1011/MQTTnet/blob/master/Images/icon_det_256.png?raw=true" width="196">
<br/>
<br/>
</p>
[![NuGet Badge](https://img.shields.io/nuget/dt/MQTTnet)](https://www.nuget.org/packages/MQTTnet)
[![CI](https://github.com/dotnet/MQTTnet/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/dotnet/MQTTnet/actions/workflows/ci.yml)
[![MyGet](https://img.shields.io/myget/mqttnet/v/mqttnet?color=orange&label=preview)](https://www.myget.org/feed/mqttnet/package/nuget/MQTTnet)
![Size](https://img.shields.io/github/repo-size/dotnet/MQTTnet.svg)
[![NuGet Badge](https://buildstats.info/nuget/MQTTnet)](https://www.nuget.org/packages/MQTTnet)
[![Build status](https://ci.appveyor.com/api/projects/status/ycit86voxfevm2aa/branch/master?svg=true)](https://ci.appveyor.com/project/chkr1011/mqttnet)
[![BCH compliance](https://bettercodehub.com/edge/badge/chkr1011/MQTTnet?branch=master)](https://bettercodehub.com/)
[![Join the chat at https://gitter.im/MQTTnet/community](https://badges.gitter.im/MQTTnet/community.svg)](https://gitter.im/MQTTnet/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://raw.githubusercontent.com/dotnet/MQTTnet/master/LICENSE)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://raw.githubusercontent.com/chkr1011/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) and supports the MQTT protocol up to version 5.
It is compatible with mostly any supported .NET Framework version and CPU architecture.
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 <http://mqtt.org/>.
## Features
@@ -24,21 +22,20 @@ It is compatible with mostly any supported .NET Framework version and CPU archit
* 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 ~150.000 messages / second)*
* Performance optimized (processing ~70.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 (~636 tests)
* Unit tested (~250 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_.
\* 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_.
### Client
* Communication via TCP (+TLS) or WS (WebSocket) supported
* Included core _LowLevelMqttClient_ with low level functionality
* Also included _ManagedMqttClient_ which maintains the connection and subscriptions automatically. Also application
messages are queued and re-scheduled for higher QoS levels automatically.
* Also included _ManagedMqttClient_ which maintains the connection and subscriptions automatically. Also application messages are queued and re-scheduled for higher QoS levels automatically.
* Rx support (via another project)
* Compatible with Microsoft Azure IoT Hub
@@ -53,31 +50,104 @@ channel. The app for verification is part of this repository and stored in _/Tes
* WebSockets supported (via ASP.NET Core 2.0, separate nuget)
* A custom message interceptor can be added which allows transforming or extending every received application message
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
* Connect clients with different protocol versions at the same time.
## Getting Started
## MQTTnet Server
MQTTnet is delivered via <b>NuGet</b> package manager. You can find the packages
here: https://www.nuget.org/packages/MQTTnet/
_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
Use these command in the Package Manager console to install MQTTnet manually:
## Supported frameworks
```
Install-Package MQTTnet
```
* .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+
Samples for using MQTTnet are part of this repository. For starters these samples are recommended:
## Supported platforms
- [Connect with a broker](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Connection_Samples.cs)
- [Subscribing to data](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Subscribe_Samples.cs)
- [Publishing data](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Publish_Samples.cs)
- [Host own broker](https://github.com/dotnet/MQTTnet/blob/master/Samples/Server/Server_Simple_Samples.cs)
* x86
* x64
* AnyCPU
* ARM
## Code of Conduct
## Supported OS
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).
* Windows
* Windows 10 IoT Core
* Linux (Ubuntu, Raspbian etc.)
* macOS
* Android
* iOS
## .NET Foundation
## Supported MQTT versions
This project is supported by the [.NET Foundation](https://dotnetfoundation.org).
* 5.0.0
* 3.1.1
* 3.1.0
## Nuget
This library is available as a nuget package: <https://www.nuget.org/packages/MQTTnet/>
## Examples
Please find examples and the documentation at the Wiki of this repository (<https://github.com/chkr1011/MQTTnet/wiki>).
## 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.
## References
This library is used in the following projects:
* Azure Functions MQTT Bindings, <https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt>)
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
* MQTT Client Rx (Wrapper for Reactive Extensions, <https://github.com/1iveowl/MQTTClient.rx>)
* MQTT Client Rx (Managed Client Wrapper for Reactive Extensions, <https://github.com/mmuecke/RxMQTTnet>)
* 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., <https://github.com/chkr1011/MQTTnet.App>)
* Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>)
* SparkplugNet (Sparkplug library for .Net, <https://github.com/SeppPenner/SparkplugNet>)
* Silverback (Framework to build event-driven applications - support for MQTT, Kafka & RabbitMQ) <https://github.com/BEagle1984/silverback>
Further projects using this project can be found under https://github.com/chkr1011/MQTTnet/network/dependents.
If you use this library and want to see your project here please create a pull request.
## License
MIT License
MQTTnet Copyright (c) 2016-2021 Christian Kratky
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.
+14
View File
@@ -0,0 +1,14 @@
* [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] 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] Exposed user properties and reason string in subscribe result.
* [Client] Exposed user properties and reason string in unsubscribe result.
* [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 adjsuted namespaces (BREAKING CHANGE!).
* [Server] Introduced a new queueing approach for internal message process (packet bus).
-522
View File
@@ -1,522 +0,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.
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using MQTTnet.Formatter;
using MQTTnet.Protocol;
using MQTTnet.Samples.Helpers;
using MQTTnet.Server;
using MQTTnet.Server.EnhancedAuthentication;
namespace MQTTnet.Samples.Client;
public static class Client_Connection_Samples
{
const string mosquitto_org = @"
-----BEGIN CERTIFICATE-----
MIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL
BQAwgZAxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwG
A1UEBwwFRGVyYnkxEjAQBgNVBAoMCU1vc3F1aXR0bzELMAkGA1UECwwCQ0ExFjAU
BgNVBAMMDW1vc3F1aXR0by5vcmcxHzAdBgkqhkiG9w0BCQEWEHJvZ2VyQGF0Y2hv
by5vcmcwHhcNMjAwNjA5MTEwNjM5WhcNMzAwNjA3MTEwNjM5WjCBkDELMAkGA1UE
BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTES
MBAGA1UECgwJTW9zcXVpdHRvMQswCQYDVQQLDAJDQTEWMBQGA1UEAwwNbW9zcXVp
dHRvLm9yZzEfMB0GCSqGSIb3DQEJARYQcm9nZXJAYXRjaG9vLm9yZzCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAME0HKmIzfTOwkKLT3THHe+ObdizamPg
UZmD64Tf3zJdNeYGYn4CEXbyP6fy3tWc8S2boW6dzrH8SdFf9uo320GJA9B7U1FW
Te3xda/Lm3JFfaHjkWw7jBwcauQZjpGINHapHRlpiCZsquAthOgxW9SgDgYlGzEA
s06pkEFiMw+qDfLo/sxFKB6vQlFekMeCymjLCbNwPJyqyhFmPWwio/PDMruBTzPH
3cioBnrJWKXc3OjXdLGFJOfj7pP0j/dr2LH72eSvv3PQQFl90CZPFhrCUcRHSSxo
E6yjGOdnz7f6PveLIB574kQORwt8ePn0yidrTC1ictikED3nHYhMUOUCAwEAAaNT
MFEwHQYDVR0OBBYEFPVV6xBUFPiGKDyo5V3+Hbh4N9YSMB8GA1UdIwQYMBaAFPVV
6xBUFPiGKDyo5V3+Hbh4N9YSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggEBAGa9kS21N70ThM6/Hj9D7mbVxKLBjVWe2TPsGfbl3rEDfZ+OKRZ2j6AC
6r7jb4TZO3dzF2p6dgbrlU71Y/4K0TdzIjRj3cQ3KSm41JvUQ0hZ/c04iGDg/xWf
+pp58nfPAYwuerruPNWmlStWAXf0UTqRtg4hQDWBuUFDJTuWuuBvEXudz74eh/wK
sMwfu1HFvjy5Z0iMDU8PUDepjVolOCue9ashlS4EB5IECdSR2TItnAIiIwimx839
LdUdRudafMu5T5Xma182OC0/u/xRlEm+tvKGGmfFcN0piqVl8OrSPBgIlb+1IKJE
m/XriWr/Cq4h/JfB7NTsezVslgkBaoU=
-----END CERTIFICATE-----
";
public static async Task Clean_Disconnect()
{
/*
* This sample disconnects in a clean way. This will send a MQTT DISCONNECT packet
* to the server and close the connection afterward.
*
* See sample _Connect_Client_ for more details.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// This will send the DISCONNECT packet. Calling _Dispose_ without DisconnectAsync the
// connection is closed in a "not clean" way. See MQTT specification for more details.
await mqttClient.DisconnectAsync(new MqttClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.NormalDisconnection).Build());
}
public static async Task Connect()
{
/*
* 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 MqttClientFactory();
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_Using_Enhanced_Authentication()
{
/*
* This sample uses enhanced authentication (Kerberos) when creating the connection.
*/
/*
* Server part...
*/
var mqttServerFactory = new MqttServerFactory();
var serverOptions = mqttServerFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
var server = mqttServerFactory.CreateMqttServer(serverOptions);
server.ValidatingConnectionAsync += async args =>
{
if (args.AuthenticationMethod == "GS2-KRB5")
{
var result = await args.ExchangeEnhancedAuthenticationAsync(new ExchangeEnhancedAuthenticationOptions(), args.CancellationToken);
Console.WriteLine($"Received AUTH data from client: {Encoding.UTF8.GetString(result.AuthenticationData)}");
var authOptions = mqttServerFactory.CreateExchangeExtendedAuthenticationOptionsBuilder().WithAuthenticationData("reply context token").Build();
result = await args.ExchangeEnhancedAuthenticationAsync(authOptions, args.CancellationToken);
Console.WriteLine($"Received AUTH data from client: {Encoding.UTF8.GetString(result.AuthenticationData)}");
args.ResponseAuthenticationData = "outcome of authentication"u8.ToArray();
// Authentication DONE!
args.ReasonCode = MqttConnectReasonCode.Success; // Also the default!
}
else
{
args.ReasonCode = MqttConnectReasonCode.BadAuthenticationMethod;
}
};
await server.StartAsync();
/*
* Client part...
*/
var mqttClientFactory = new MqttClientFactory();
// Use Kerberos sample from the MQTT RFC.
var kerberosAuthenticationHandler = new SampleClientKerberosAuthenticationHandler();
var clientOptions = mqttClientFactory.CreateClientOptionsBuilder()
.WithTcpServer("localhost")
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithEnhancedAuthenticationHandler(kerberosAuthenticationHandler)
.WithEnhancedAuthentication("GS2-KRB5")
.Build();
var client = mqttClientFactory.CreateMqttClient();
var result = await client.ConnectAsync(clientOptions);
Console.WriteLine($"Client connect result: {result.ResultCode}");
}
public static async Task Connect_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 MqttClientFactory();
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_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 MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io")
.WithTlsOptions(
o =>
{
// The used public broker sometimes has invalid certificates. This sample accepts all
// certificates. This should not be used in live environments.
o.WithCertificateValidationHandler(_ => true);
// The default value is determined by the OS. Set manually to force version.
o.WithSslProtocols(SslProtocols.Tls12);
})
.Build();
using var timeout = new CancellationTokenSource(5000);
await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
Console.WriteLine("The MQTT client is connected.");
}
public static async Task Connect_Using_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 MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
.WithTlsOptions(
o => o.WithCertificateValidationHandler(
// The used public broker sometimes has invalid certificates. This sample accepts all
// certificates. This should not be used in live environments.
_ => true))
.Build();
// In MQTTv5 the response contains much more information.
using var timeout = new CancellationTokenSource(5000);
var response = await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
Console.WriteLine("The MQTT client is connected.");
response.DumpToConsole();
}
public static async Task Connect_Using_TLS_With_CA_File()
{
var mqttFactory = new MqttClientFactory();
var caChain = new X509Certificate2Collection();
caChain.ImportFromPem(mosquitto_org); // from https://test.mosquitto.org/ssl/mosquitto.org.crt
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
.WithTlsOptions(new MqttClientTlsOptionsBuilder().WithTrustChain(caChain).Build())
.Build();
var connAck = await mqttClient.ConnectAsync(mqttClientOptions);
Console.WriteLine("Connected to test.moquitto.org:8883 with CaFile mosquitto.org.crt: " + connAck.ResultCode);
}
public static async Task Connect_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 MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("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_With_Amazon_AWS()
{
/*
* This sample creates a simple MQTT client and connects to an Amazon Web Services broker.
*
* The broker requires special settings which are set here.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("amazon.web.services.broker")
// Disabling packet fragmentation is very important!
.WithoutPacketFragmentation()
.Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
Console.WriteLine("The MQTT client is connected.");
await mqttClient.DisconnectAsync();
}
public static async Task Disconnect_Clean()
{
/*
* This sample disconnects from the server with sending a DISCONNECT packet.
* This way of disconnecting is treated as a clean disconnect which will not
* trigger sending the last will etc.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// Calling _DisconnectAsync_ will send a DISCONNECT packet before closing the connection.
// Using a reason code requires MQTT version 5.0.0!
await mqttClient.DisconnectAsync(MqttClientDisconnectOptionsReason.ImplementationSpecificError);
}
public static async Task Disconnect_Non_Clean()
{
/*
* This sample disconnects from the server without sending a DISCONNECT packet.
* This way of disconnecting is treated as a non-clean disconnect which will
* trigger sending the last will etc.
*/
var mqttFactory = new MqttClientFactory();
var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// Calling _Dispose_ or use of a _using_ statement will close the transport connection
// without sending a DISCONNECT packet to the server.
mqttClient.Dispose();
}
public static async Task Inspect_Certificate_Validation_Errors()
{
/*
* This sample prints the certificate information while connection. This data can be used to decide whether a connection is secure or not
* including the reason for that status.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io", 8883)
.WithTlsOptions(
o =>
{
o.WithCertificateValidationHandler(
eventArgs =>
{
eventArgs.Certificate.Subject.DumpToConsole();
eventArgs.Certificate.GetExpirationDateString().DumpToConsole();
eventArgs.Chain.ChainPolicy.RevocationMode.DumpToConsole();
eventArgs.Chain.ChainStatus.DumpToConsole();
eventArgs.SslPolicyErrors.DumpToConsole();
return true;
});
})
.Build();
// In MQTTv5 the response contains much more information.
using var timeout = new CancellationTokenSource(5000);
await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
}
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 MQTTv5.0.0+.
*/
var mqttFactory = new MqttClientFactory();
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 deadlocks! Consider using the timer approach (see sample).
*/
var mqttFactory = new MqttClientFactory();
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(mqttClientOptions);
}
};
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 MqttClientFactory();
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 (!await mqttClient.TryPingAsync())
{
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));
}
}
});
Console.WriteLine("Press <Enter> to exit");
Console.ReadLine();
}
public static async Task 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 MqttClientFactory();
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.");
}
}
sealed class SampleClientKerberosAuthenticationHandler : IMqttEnhancedAuthenticationHandler
{
public async Task HandleEnhancedAuthenticationAsync(MqttEnhancedAuthenticationEventArgs eventArgs)
{
if (eventArgs.AuthenticationMethod != "GS2-KRB5")
{
throw new InvalidOperationException("Wrong authentication method");
}
var sendOptions = new SendMqttEnhancedAuthenticationDataOptions
{
Data = "initial context token"u8.ToArray()
};
await eventArgs.SendAsync(sendOptions);
var response = await eventArgs.ReceiveAsync(CancellationToken.None);
Console.WriteLine($"Received AUTH data from server: {Encoding.UTF8.GetString(response.AuthenticationData)}");
// No further data is required, but we have to fulfil the exchange.
sendOptions = new SendMqttEnhancedAuthenticationDataOptions
{
Data = []
};
await eventArgs.SendAsync(sendOptions, CancellationToken.None);
}
}
}
-107
View File
@@ -1,107 +0,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.
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
using System;
using System.Text;
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 MqttClientFactory();
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);
await mqttClient.DisconnectAsync();
Console.WriteLine("MQTT application message is published.");
}
public static async Task Publish_Multiple_Application_Messages()
{
/*
* This sample pushes multiple simple application message including a topic and a payload.
*
* See sample _Publish_Application_Message_ for more details.
*/
var mqttFactory = new MqttClientFactory();
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);
applicationMessage = new MqttApplicationMessageBuilder()
.WithTopic("samples/temperature/living_room")
.WithPayload("20.0")
.Build();
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
applicationMessage = new MqttApplicationMessageBuilder()
.WithTopic("samples/temperature/living_room")
.WithPayload("21.0")
.Build();
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
await mqttClient.DisconnectAsync();
Console.WriteLine("MQTT application message is published.");
}
public static MqttApplicationMessage Create_Message_With_Binary_User_Property()
{
/*
* MQTT v5 user properties are encoded as UTF-8 strings. When the UTF-8 payload is already available
* as a byte buffer, the builder APIs can avoid creating intermediate strings.
*/
var encodedValue = Encoding.UTF8.GetBytes("sensor-01");
return new MqttApplicationMessageBuilder()
.WithTopic("samples/metadata/binary")
.WithUserProperty("client-id", encodedValue.AsMemory())
.WithUserProperty("checksum", new ArraySegment<byte>(encodedValue))
.WithPayload("metadata")
.Build();
}
}
-86
View File
@@ -1,86 +0,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.
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
using MQTTnet.Extensions.Socks5;
using MQTTnet.Samples.Helpers;
namespace MQTTnet.Samples.Client;
public static class Client_Socks5_Samples
{
public static async Task Connect_Tcp_Via_Socks5_No_Auth()
{
/*
* This sample connects to an MQTT broker through a SOCKS5 proxy that does not
* require authentication. The SOCKS5 stream provider performs the proxy handshake
* (RFC 1928) and returns a connected stream that the MQTT TCP channel can use as if
* it were a direct TCP connection.
*
* Host name resolution is performed by the proxy by default (ATYP=DOMAINNAME), which
* is the desired behavior when the MQTT broker hostname is only reachable from inside
* the network the proxy lives in.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("broker.internal.example", 1883)
.WithSocks5Proxy(socks => socks
.WithHost("proxy.example.com")
.WithPort(1080))
.Build();
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(15));
var response = await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
Console.WriteLine("The MQTT client is connected through SOCKS5.");
response.DumpToConsole();
var disconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder().Build();
await mqttClient.DisconnectAsync(disconnectOptions, CancellationToken.None);
}
public static async Task Connect_Tcp_With_Tls_Via_Socks5_With_Credentials()
{
/*
* This sample connects to a TLS-protected MQTT broker through a SOCKS5 proxy that
* requires username/password authentication (RFC 1929).
*
* Note that the TLS handshake is layered on top of the SOCKS5 tunnel: the SNI used
* by SslStream is derived from the broker hostname (or the value passed to
* WithTargetHost), not from the proxy hostname.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("broker.internal.example", 8883)
.WithTlsOptions(tls => tls
.WithTargetHost("broker.internal.example"))
.WithSocks5Proxy(socks => socks
.WithHost("proxy.example.com")
.WithPort(1080)
.WithCredentials("alice", "s3cr3t")
.WithResolveDnsRemotely(true)
.WithHandshakeTimeout(TimeSpan.FromSeconds(10)))
.Build();
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(15));
var response = await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
Console.WriteLine("The MQTT client is connected through SOCKS5 with TLS.");
response.DumpToConsole();
var disconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder().Build();
await mqttClient.DisconnectAsync(disconnectOptions, CancellationToken.None);
}
}
-203
View File
@@ -1,203 +0,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.
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
// ReSharper disable UnusedMember.Local
using System.Text;
using MQTTnet.Packets;
using MQTTnet.Protocol;
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 MqttClientFactory();
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("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 Send_Responses()
{
/*
* This sample subscribes to a topic and sends a detailed response to the client. This requires at least QoS level 1 to work!
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
mqttClient.ApplicationMessageReceivedAsync += delegate(MqttApplicationMessageReceivedEventArgs args)
{
// Do some work with the message...
// Now respond to the broker with a reason code other than success.
args.ReasonCode = MqttApplicationMessageReceivedReasonCode.ImplementationSpecificError;
args.ResponseReasonString = "That did not work!";
// User properties require MQTT v5!
args.ResponseUserProperties.Add(new MqttUserProperty("My", Encoding.UTF8.GetBytes("Data")));
// Now the broker will resend the message again.
return Task.CompletedTask;
};
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder().WithTopicFilter("topic/test").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();
}
public static async Task Subscribe_Multiple_Topics()
{
/*
* This sample subscribes to several topics in a single request.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// Create the subscribe options including several topics with different options.
// It is also possible to all of these topics using a dedicated call of _SubscribeAsync_ per topic.
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
.WithTopicFilter(t => t.WithTopic("topic/1").WithAtLeastOnceQoS())
.WithTopicFilter(t => t.WithTopic("topic/2").WithAtMostOnceQoS())
.WithTopicFilter(t => t.WithTopic("topic/3").WithNoLocal())
.WithTopicFilter(t => t.WithRetainHandling(MqttRetainHandling.SendAtSubscribe))
.Build();
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
Console.WriteLine("MQTT client subscribed to topics.");
// The response contains additional data sent by the server after subscribing.
response.DumpToConsole();
}
public static async Task Subscribe_Topic()
{
/*
* This sample subscribes to a topic.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
var topicFilter = mqttFactory.CreateTopicFilterBuilder().WithTopic("mqttnet/samples/topic/2").WithAtLeastOnceQoS();
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder().WithTopicFilter(topicFilter).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();
}
static void ConcurrentProcessingDisableAutoAcknowledge(IMqttClient mqttClient, CancellationToken shutdownToken)
{
/*
* This sample shows how to achieve concurrent processing and not have message AutoAcknowledged
* This to have a proper QoS1 (at-least-once) experience for what at least MQTT specification can provide
*/
mqttClient.ApplicationMessageReceivedAsync += ea =>
{
ea.AutoAcknowledge = false;
async Task ProcessAsync()
{
// DO YOUR WORK HERE!
await Task.Delay(1000, shutdownToken);
await ea.AcknowledgeAsync(shutdownToken);
// WARNING: If process failures are not transient the message will be retried on every restart of the client
// A failed message will not be dispatched again to the client as MQTT does not have a NACK packet to let
// the broker know processing failed
//
// Optionally: Use a framework like Polly to create a retry policy: https://github.com/App-vNext/Polly#retry
}
_ = Task.Run(ProcessAsync, shutdownToken);
return Task.CompletedTask;
};
}
static void ConcurrentProcessingWithLimit(IMqttClient mqttClient, CancellationToken shutdownToken)
{
/*
* This sample shows how to achieve concurrent processing, with:
* - a maximum concurrency limit based on Environment.ProcessorCount
*/
var concurrent = new SemaphoreSlim(Environment.ProcessorCount);
mqttClient.ApplicationMessageReceivedAsync += async ea =>
{
await concurrent.WaitAsync(shutdownToken).ConfigureAwait(false);
async Task ProcessAsync()
{
try
{
// DO YOUR WORK HERE!
await Task.Delay(1000, shutdownToken);
}
finally
{
concurrent.Release();
}
}
_ = Task.Run(ProcessAsync, shutdownToken);
};
}
}
@@ -1,75 +0,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.
using MQTTnet.Extensions.TopicTemplate;
using MQTTnet.Protocol;
using MQTTnet.Samples.Helpers;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
// ReSharper disable UnusedMember.Local
namespace MQTTnet.Samples.Client;
public static class Client_Subscribe_TopicTemplate_Samples
{
static readonly MqttTopicTemplate SampleTemplate = new("mqttnet/samples/topic/{id}");
public static async Task Subscribe_Multiple_Topics_With_Template()
{
/*
* This sample subscribes to several topics in a single request.
*
* This sample requires the nuget 'MQTTnet.Extensions.TopicTemplate'.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
// Create the subscribe options including several topics with different options.
// It is also possible to all of these topics using a dedicated call of _SubscribeAsync_ per topic.
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
.WithTopicTemplate(SampleTemplate.WithParameter("id", "1"))
.WithTopicTemplate(SampleTemplate.WithParameter("id", "2"), noLocal: true)
.WithTopicTemplate(SampleTemplate.WithParameter("id", "3"), retainHandling: MqttRetainHandling.SendAtSubscribe)
.Build();
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
Console.WriteLine("MQTT client subscribed to topics.");
// The response contains additional data sent by the server after subscribing.
response.DumpToConsole();
}
public static async Task Subscribe_Topic_With_Template()
{
/*
* This sample subscribes to a topic.
*
* This sample requires the nuget 'MQTTnet.Extensions.TopicTemplate'.
*/
var mqttFactory = new MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder().WithTopicTemplate(SampleTemplate.WithParameter("id", "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();
}
}
-92
View File
@@ -1,92 +0,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.
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
using System.Globalization;
using System.Text;
using MQTTnet.Diagnostics.Logger;
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 MqttClientFactory(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 += (_, args) =>
{
var output = new StringBuilder();
output.AppendLine(CultureInfo.InvariantCulture, $">> [{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 MqttClientFactory(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.
}
}
}
@@ -1,53 +0,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.
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
using MQTTnet.Diagnostics.PacketInspection;
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 MqttClientFactory();
using var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
.WithTcpServer("broker.hivemq.com")
.Build();
mqttClient.InspectPacketAsync += OnInspectPacket;
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 OnInspectPacket(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;
}
}
-27
View File
@@ -1,27 +0,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.
using System.Text.Json;
namespace MQTTnet.Samples.Helpers;
internal static class ObjectExtensions
{
static readonly JsonSerializerOptions SerializerOptions = new()
{
WriteIndented = true
};
public static TObject DumpToConsole<TObject>(this TObject @object)
{
var output = "NULL";
if (@object != null)
{
output = JsonSerializer.Serialize(@object, SerializerOptions);
}
Console.WriteLine($"[{@object?.GetType().Name}]:\r\n{output}");
return @object;
}
}
-21
View File
@@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<NoWarn>$(NoWarn);CA1707</NoWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj"/>
<ProjectReference Include="..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj"/>
<ProjectReference Include="..\Source\MQTTnet.Extensions.Socks5\MQTTnet.Extensions.Socks5.csproj" />
<ProjectReference Include="..\Source\MQTTnet.Extensions.TopicTemplate\MQTTnet.Extensions.TopicTemplate.csproj" />
<ProjectReference Include="..\Source\MQTTnet.Server\MQTTnet.Server.csproj"/>
<ProjectReference Include="..\Source\MQTTnet\MQTTnet.csproj"/>
</ItemGroup>
</Project>
-72
View File
@@ -1,72 +0,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.
using System.Globalization;
using System.Reflection;
Console.BackgroundColor = ConsoleColor.White;
Console.BackgroundColor = ConsoleColor.Red;
Console.Title = "Samples - MQTTnet";
Console.WriteLine("----------------------------------------------");
Console.WriteLine("- Welcome to MQTTnet samples! -");
Console.WriteLine("----------------------------------------------");
Console.ResetColor();
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.WriteLine();
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("Please choose sample class (press Enter to continue): ");
Console.ResetColor();
var input = Console.ReadLine();
var selectedIndex = int.Parse(input ?? "0", CultureInfo.InvariantCulture);
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.WriteLine();
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("Please choose sample (press Enter to continue): ");
Console.ResetColor();
input = Console.ReadLine();
selectedIndex = int.Parse(input ?? "0", CultureInfo.InvariantCulture);
var selectedSampleMethod = sampleMethods[selectedIndex];
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("Executing sample...");
Console.ResetColor();
Console.WriteLine();
try
{
if (selectedSampleMethod.Invoke(null, null) is Task task)
{
await task;
}
}
catch (Exception exception)
{
Console.WriteLine(exception.ToString());
}
Console.WriteLine();
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
-85
View File
@@ -1,85 +0,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.
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
using MQTTnet.Extensions.Rpc;
using MQTTnet.Protocol;
namespace MQTTnet.Samples.RpcClient;
public static class RpcClient_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 MqttClientFactory();
// 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 RPC 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.
*/
}
-113
View File
@@ -1,113 +0,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.
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
// ReSharper disable EmptyConstructor
// ReSharper disable MemberCanBeMadeStatic.Local
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MQTTnet.AspNetCore;
using MQTTnet.Server;
namespace MQTTnet.Samples.Server;
public static class Server_ASP_NET_Samples
{
public static Task Start_Server_With_WebSockets_Support()
{
/*
* This sample starts a minimal ASP.NET Webserver including a hosted MQTT server.
*/
var host = Host.CreateDefaultBuilder(Array.Empty<string>())
.ConfigureWebHostDefaults(
webBuilder =>
{
webBuilder.UseKestrel(
o =>
{
// This will allow MQTT connections based on TCP port 1883.
o.ListenAnyIP(1883, l => l.UseMqtt());
// This will allow MQTT connections based on HTTP WebSockets with URI "localhost:5000/mqtt"
// See code below for URI configuration.
o.ListenAnyIP(5000); // Default HTTP pipeline
});
webBuilder.UseStartup<Startup>();
});
return host.RunConsoleAsync();
}
sealed class MqttController
{
public MqttController()
{
// Inject other services via constructor.
}
public static Task OnClientConnected(ClientConnectedEventArgs eventArgs)
{
Console.WriteLine($"Client '{eventArgs.ClientId}' connected.");
return Task.CompletedTask;
}
public static Task ValidateConnection(ValidatingConnectionEventArgs eventArgs)
{
Console.WriteLine($"Client '{eventArgs.ClientId}' wants to connect. Accepting!");
return Task.CompletedTask;
}
}
sealed class Startup
{
[SuppressMessage("Performance", "CA1822:Mark members as static")]
public void Configure(IApplicationBuilder app, IWebHostEnvironment environment, MqttController mqttController)
{
app.UseRouting();
app.UseEndpoints(
endpoints =>
{
endpoints.MapConnectionHandler<MqttConnectionHandler>(
"/mqtt",
httpConnectionDispatcherOptions => httpConnectionDispatcherOptions.WebSockets.SubProtocolSelector =
protocolList => protocolList.FirstOrDefault() ?? string.Empty);
});
app.UseMqttServer(
server =>
{
/*
* Attach event handlers etc. if required.
*/
server.ValidatingConnectionAsync += MqttController.ValidateConnection;
server.ClientConnectedAsync += MqttController.OnClientConnected;
});
}
[SuppressMessage("Performance", "CA1822:Mark members as static")]
public void ConfigureServices(IServiceCollection services)
{
services.AddHostedMqttServer(
optionsBuilder =>
{
optionsBuilder.WithDefaultEndpoint();
});
services.AddMqttConnectionHandler();
services.AddConnections();
services.AddSingleton<MqttController>();
}
}
}
@@ -1,50 +0,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.
using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Server;
namespace MQTTnet.Samples.Server;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
// ReSharper disable EmptyConstructor
// ReSharper disable MemberCanBeMadeStatic.Local
public static class Server_Diagnostics_Samples
{
public static async Task Get_Notified_When_Client_Received_Message()
{
/*
* This sample starts a MQTT server and attaches an event which gets fired whenever
* a client received and acknowledged a PUBLISH packet.
*/
var mqttServerFactory = new MqttServerFactory();
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
using var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions);
// Attach the event handler.
mqttServer.ClientAcknowledgedPublishPacketAsync += e =>
{
Console.WriteLine($"Client '{e.ClientId}' acknowledged packet {e.PublishPacket.PacketIdentifier} with topic '{e.PublishPacket.Topic}'");
// It is also possible to read additional data from the client response. This requires casting the response packet.
var qos1AcknowledgePacket = e.AcknowledgePacket as MqttPubAckPacket;
Console.WriteLine($"QoS 1 reason code: {qos1AcknowledgePacket?.ReasonCode}");
var qos2AcknowledgePacket = e.AcknowledgePacket as MqttPubCompPacket;
Console.WriteLine($"QoS 2 reason code: {qos2AcknowledgePacket?.ReasonCode}");
return CompletedTask.Instance;
};
await mqttServer.StartAsync();
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
await mqttServer.StopAsync();
}
}
@@ -1,74 +0,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.
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
// ReSharper disable EmptyConstructor
// ReSharper disable MemberCanBeMadeStatic.Local
using MQTTnet.Internal;
using MQTTnet.Protocol;
using MQTTnet.Server;
namespace MQTTnet.Samples.Server;
public static class Server_Intercepting_Samples
{
public static async Task Intercept_Application_Messages()
{
/*
* This sample starts a simple MQTT server which manipulate all processed application messages.
* Please see _Server_Simple_Samples_ for more details on how to start a server.
*/
var mqttServerFactory = new MqttServerFactory();
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
using var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions);
mqttServer.InterceptingPublishAsync += args =>
{
// Here we only change the topic of the received application message.
// but also changing the payload etc. is required. Changing the QoS after
// transmitting is not supported and makes no sense at all.
args.ApplicationMessage.Topic += "/manipulated";
return CompletedTask.Instance;
};
await mqttServer.StartAsync();
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
await mqttServer.StopAsync();
}
public static async Task Intercept_Subscription()
{
/*
* This sample starts a simple MQTT server which does not allow to subscribe to '#'
* Start the server and try to subscribe to '#' with a client of choice.
*/
var mqttServerFactory = new MqttServerFactory();
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
using var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions);
mqttServer.InterceptingSubscriptionAsync += args =>
{
if (args.TopicFilter.Topic.Equals("#", StringComparison.Ordinal))
{
args.Response.ReasonCode = MqttSubscribeReasonCode.NotAuthorized;
}
return CompletedTask.Instance;
};
await mqttServer.StartAsync();
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
await mqttServer.StopAsync();
}
}
@@ -1,141 +0,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.
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
using System.Buffers;
using System.Text.Json;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using MQTTnet.Server;
namespace MQTTnet.Samples.Server;
public static class Server_Retained_Messages_Samples
{
public static async Task Persist_Retained_Messages()
{
/*
* This sample starts a simple MQTT server which will store all retained messages in a file.
*/
var storePath = Path.Combine(Path.GetTempPath(), "RetainedMessages.json");
var mqttServerFactory = new MqttServerFactory();
// Due to security reasons the "default" endpoint (which is unencrypted) is not enabled by default!
var mqttServerOptions = mqttServerFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
using var server = mqttServerFactory.CreateMqttServer(mqttServerOptions);
// Make sure that the server will load the retained messages.
server.LoadingRetainedMessageAsync += async eventArgs =>
{
try
{
var models = await JsonSerializer.DeserializeAsync<List<MqttRetainedMessageModel>>(File.OpenRead(storePath)) ?? new List<MqttRetainedMessageModel>();
var retainedMessages = models.Select(m => m.ToApplicationMessage()).ToList();
eventArgs.LoadedRetainedMessages = retainedMessages;
Console.WriteLine("Retained messages loaded.");
}
catch (FileNotFoundException)
{
// Ignore because nothing is stored yet.
Console.WriteLine("No retained messages stored yet.");
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
};
// Make sure to persist the changed retained messages.
server.RetainedMessageChangedAsync += async eventArgs =>
{
try
{
// This sample uses the property _StoredRetainedMessages_ which will contain all(!) retained messages.
// The event args also contain the affected retained message (property ChangedRetainedMessage). This can be
// used to write all retained messages to dedicated files etc. Then all files must be loaded and a full list
// of retained messages must be provided in the loaded event.
var models = eventArgs.StoredRetainedMessages.Select(MqttRetainedMessageModel.Create);
var buffer = JsonSerializer.SerializeToUtf8Bytes(models);
await File.WriteAllBytesAsync(storePath, buffer);
Console.WriteLine("Retained messages saved.");
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
};
// Make sure to clear the retained messages when they are all deleted via API.
server.RetainedMessagesClearedAsync += _ =>
{
File.Delete(storePath);
return Task.CompletedTask;
};
await server.StartAsync();
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
}
sealed class MqttRetainedMessageModel
{
public string? ContentType { get; set; }
public byte[]? CorrelationData { get; set; }
public byte[]? Payload { get; set; }
public MqttPayloadFormatIndicator PayloadFormatIndicator { get; set; }
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
public string? ResponseTopic { get; set; }
public string? Topic { get; set; }
public List<MqttUserProperty>? UserProperties { get; set; }
public static MqttRetainedMessageModel Create(MqttApplicationMessage message)
{
ArgumentNullException.ThrowIfNull(message);
return new MqttRetainedMessageModel
{
Topic = message.Topic,
// Create a copy of the buffer from the payload segment because
// it cannot be serialized and deserialized with the JSON serializer.
Payload = message.Payload.ToArray(),
UserProperties = message.UserProperties,
ResponseTopic = message.ResponseTopic,
CorrelationData = message.CorrelationData,
ContentType = message.ContentType,
PayloadFormatIndicator = message.PayloadFormatIndicator,
QualityOfServiceLevel = message.QualityOfServiceLevel
// Other properties like "Retain" are not if interest in the storage.
// That's why a custom model makes sense.
};
}
public MqttApplicationMessage ToApplicationMessage()
{
return new MqttApplicationMessage
{
Topic = Topic,
PayloadSegment = new ArraySegment<byte>(Payload ?? Array.Empty<byte>()),
PayloadFormatIndicator = PayloadFormatIndicator,
ResponseTopic = ResponseTopic,
CorrelationData = CorrelationData,
ContentType = ContentType,
UserProperties = UserProperties,
QualityOfServiceLevel = QualityOfServiceLevel,
Dup = false,
Retain = true
};
}
}
}
-210
View File
@@ -1,210 +0,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.
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
using System.Globalization;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Protocol;
using MQTTnet.Server;
namespace MQTTnet.Samples.Server;
public static class Server_Simple_Samples
{
public static async Task Force_Disconnecting_Client()
{
/*
* This sample will disconnect a client.
*
* See _Run_Minimal_Server_ for more information.
*/
using var mqttServer = await StartMqttServer();
// Let the client connect.
await Task.Delay(TimeSpan.FromSeconds(5));
// Now disconnect the client (if connected).
var affectedClient = (await mqttServer.GetClientsAsync()).FirstOrDefault(c => c.Id == "MyClient");
if (affectedClient != null)
{
await affectedClient.DisconnectAsync();
}
}
public static async Task Publish_Message_From_Broker()
{
/*
* This sample will publish a message directly at the broker.
*
* See _Run_Minimal_Server_ for more information.
*/
using var mqttServer = await StartMqttServer();
// Create a new message using the builder as usual.
var message = new MqttApplicationMessageBuilder().WithTopic("HelloWorld").WithPayload("Test").Build();
// Now inject the new message at the broker.
await mqttServer.InjectApplicationMessage(
new InjectedMqttApplicationMessage(message)
{
SenderClientId = "SenderClientId"
});
}
public static async Task Run_Minimal_Server()
{
/*
* This sample starts a simple MQTT server which will accept any TCP connection.
*/
var mqttServerFactory = new MqttServerFactory();
// 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 = mqttServerFactory.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 Run_Server_With_Logging()
{
/*
* This sample starts a simple MQTT server and prints the logs to the output.
*
* IMPORTANT! Do not enable logging in live environment. It will decrease performance.
*
* See sample "Run_Minimal_Server" for more details.
*/
var mqttServerFactory = new MqttServerFactory(new ConsoleLogger());
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
using var mqttServer = mqttServerFactory.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 mqttServerFactory = new MqttServerFactory();
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
using var mqttServer = mqttServerFactory.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();
}
static async Task<MqttServer> StartMqttServer()
{
var mqttServerFactory = new MqttServerFactory();
// Due to security reasons the "default" endpoint (which is unencrypted) is not enabled by default!
var mqttServerOptions = mqttServerFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
var server = mqttServerFactory.CreateMqttServer(mqttServerOptions);
await server.StartAsync();
return server;
}
sealed class ConsoleLogger : IMqttNetLogger
{
readonly object _consoleSyncRoot = new();
public bool IsEnabled => true;
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[]? parameters, Exception? exception)
{
var foregroundColor = ConsoleColor.White;
switch (logLevel)
{
case MqttNetLogLevel.Verbose:
foregroundColor = ConsoleColor.White;
break;
case MqttNetLogLevel.Info:
foregroundColor = ConsoleColor.Green;
break;
case MqttNetLogLevel.Warning:
foregroundColor = ConsoleColor.DarkYellow;
break;
case MqttNetLogLevel.Error:
foregroundColor = ConsoleColor.Red;
break;
}
if (parameters?.Length > 0)
{
message = string.Format(CultureInfo.InvariantCulture, message, parameters);
}
lock (_consoleSyncRoot)
{
Console.ForegroundColor = foregroundColor;
Console.WriteLine(message);
if (exception != null)
{
Console.WriteLine(exception);
}
}
}
}
}
-71
View File
@@ -1,71 +0,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.
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using MQTTnet.Server;
namespace MQTTnet.Samples.Server;
public static class Server_TLS_Samples
{
public static async Task Run_Server_With_Self_Signed_Certificate()
{
/*
* This sample starts a simple MQTT server which will accept any TCP connection.
* It also has an encrypted connection using a self signed TLS certificate.
*
* See sample "Run_Minimal_Server" for more details.
*/
var mqttServerFactory = new MqttServerFactory();
// This certificate is self signed so that
var certificate = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(certificate).WithEncryptedEndpoint().Build();
using var mqttServer = mqttServerFactory.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();
}
static X509Certificate2 CreateSelfSignedCertificate(string oid)
{
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddIpAddress(IPAddress.Loopback);
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
sanBuilder.AddDnsName("localhost");
using var rsa = RSA.Create();
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
certRequest.CertificateExtensions.Add(
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new(oid) }, false));
certRequest.CertificateExtensions.Add(sanBuilder.Build());
using var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10));
#pragma warning disable SYSLIB0057
var pfxCertificate = new X509Certificate2(
certificate.Export(X509ContentType.Pfx),
(string)null!,
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
#pragma warning restore SYSLIB0057
return pfxCertificate;
}
}
@@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<IsPackable>false</IsPackable>
<NoWarn>$(NoWarn);CA1707</NoWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj"/>
<ProjectReference Include="..\..\Source\MQTTnet.Server\MQTTnet.Server.csproj"/>
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj"/>
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\"/>
</ItemGroup>
</Project>
@@ -1,34 +0,0 @@
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div>
<h1>MQTTnet ASP.NET Core Test App</h1>
</div>
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
<script>
const options = {
clean: true,
connectTimeout: 4000,
clientId: 'mqttnet_web_client'
}
const client = mqtt.connect('ws://localhost:5000/mqtt')
client.on('connect', function () {
console.log('Connected')
client.subscribe('#', function (err) {
if (!err) {
client.publish('test', 'Hello mqtt')
}
})
})
client.on('message', function (topic, message) {
console.log(message.toString())
})
</script>
@@ -1,25 +0,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.
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MQTTnet.AspTestApp.Pages;
public partial class IndexModel : PageModel
{
readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
LogOnGet();
}
[LoggerMessage(Level = LogLevel.Information, Message = "OnGet")]
private partial void LogOnGet();
}
@@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - MQTTnet</title>
</head>
<body>
@RenderBody()
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
@@ -1,2 +0,0 @@
@namespace MQTTnet.AspTestApp.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@@ -1,3 +0,0 @@
@{
Layout = "_Layout";
}
-70
View File
@@ -1,70 +0,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.
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.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();
@@ -1,9 +0,0 @@
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
@@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
-5
View File
@@ -1,5 +0,0 @@
{
"version": "1.0",
"defaultProvider": "cdnjs",
"libraries": []
}
@@ -1,50 +0,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.
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore;
public static class ApplicationBuilderExtensions
{
[Obsolete(
"This class is obsolete and will be removed in a future version. The recommended alternative is to use MapMqtt inside Microsoft.AspNetCore.Builder.UseEndpoints(...).")]
public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app, string path = "/mqtt")
{
app.UseWebSockets();
app.Use(
async (context, next) =>
{
if (!context.WebSockets.IsWebSocketRequest || context.Request.Path != path)
{
await next();
return;
}
string subProtocol = null;
if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
{
subProtocol = MqttSubProtocolSelector.SelectSubProtocol(requestedSubProtocolValues);
}
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
using var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol).ConfigureAwait(false);
await adapter.RunWebSocketConnectionAsync(webSocket, context);
});
return app;
}
public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action<MqttServer> configure)
{
var server = app.ApplicationServices.GetRequiredService<MqttServer>();
configure(server);
return app;
}
}
@@ -1,17 +1,15 @@
// 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;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore;
public sealed class AspNetMqttServerOptionsBuilder : MqttServerOptionsBuilder
namespace MQTTnet.AspNetCore
{
public AspNetMqttServerOptionsBuilder(IServiceProvider serviceProvider)
public class AspNetMqttServerOptionsBuilder : MqttServerOptionsBuilder
{
ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
public AspNetMqttServerOptionsBuilder(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public IServiceProvider ServiceProvider { get; }
}
public IServiceProvider ServiceProvider { get; }
}
}
@@ -1,20 +0,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.
using System.Runtime.InteropServices;
namespace MQTTnet.AspNetCore;
public static class BufferExtensions
{
public static ArraySegment<byte> GetArray(this ReadOnlyMemory<byte> memory)
{
if (!MemoryMarshal.TryGetArray(memory, out var result))
{
throw new InvalidOperationException("Buffer backed by array was expected");
}
return result;
}
}
@@ -0,0 +1,35 @@
using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Formatter;
using System;
using System.Net;
using MQTTnet.Client;
using MQTTnet.Diagnostics;
namespace MQTTnet.AspNetCore.Client
{
public class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
{
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetLogger logger)
{
if (options == null) throw new ArgumentNullException(nameof(options));
switch (options.ChannelOptions)
{
case MqttClientTcpOptions tcpOptions:
{
var endpoint = new DnsEndPoint(tcpOptions.Server, tcpOptions.GetPort());
var tcpConnection = new TcpConnection(endpoint);
var writer = new SpanBasedMqttPacketWriter();
var formatter = new MqttPacketFormatterAdapter(options.ProtocolVersion, writer);
return new MqttConnectionContext(formatter, tcpConnection);
}
default:
{
throw new NotSupportedException();
}
}
}
}
}
@@ -0,0 +1,22 @@
using System;
using System.Runtime.InteropServices;
namespace MQTTnet.AspNetCore.Client.Tcp
{
public static class BufferExtensions
{
public static ArraySegment<byte> GetArray(this Memory<byte> memory)
{
return ((ReadOnlyMemory<byte>)memory).GetArray();
}
public static ArraySegment<byte> GetArray(this ReadOnlyMemory<byte> memory)
{
if (!MemoryMarshal.TryGetArray(memory, out var result))
{
throw new InvalidOperationException("Buffer backed by array was expected");
}
return result;
}
}
}
@@ -0,0 +1,41 @@
using System.IO.Pipelines;
namespace MQTTnet.AspNetCore.Client.Tcp
{
public class DuplexPipe : IDuplexPipe
{
public DuplexPipe(PipeReader reader, PipeWriter writer)
{
Input = reader;
Output = writer;
}
public PipeReader Input { get; }
public PipeWriter Output { get; }
public static DuplexPipePair CreateConnectionPair(PipeOptions inputOptions, PipeOptions outputOptions)
{
var input = new Pipe(inputOptions);
var output = new Pipe(outputOptions);
var transportToApplication = new DuplexPipe(output.Reader, input.Writer);
var applicationToTransport = new DuplexPipe(input.Reader, output.Writer);
return new DuplexPipePair(applicationToTransport, transportToApplication);
}
// This class exists to work around issues with value tuple on .NET Framework
public readonly struct DuplexPipePair
{
public IDuplexPipe Transport { get; }
public IDuplexPipe Application { get; }
public DuplexPipePair(IDuplexPipe transport, IDuplexPipe application)
{
Transport = transport;
Application = application;
}
}
}
}
@@ -0,0 +1,70 @@
using System;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.AspNetCore.Client.Tcp
{
public class SocketAwaitable : ICriticalNotifyCompletion
{
private static readonly Action _callbackCompleted = () => { };
private readonly PipeScheduler _ioScheduler;
private Action _callback;
private int _bytesTransferred;
private SocketError _error;
public SocketAwaitable(PipeScheduler ioScheduler)
{
_ioScheduler = ioScheduler;
}
public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted);
public SocketAwaitable GetAwaiter() => this;
public int GetResult()
{
Debug.Assert(ReferenceEquals(_callback, _callbackCompleted));
_callback = null;
if (_error != SocketError.Success)
{
throw new SocketException((int)_error);
}
return _bytesTransferred;
}
public void OnCompleted(Action continuation)
{
if (ReferenceEquals(_callback, _callbackCompleted) ||
ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), _callbackCompleted))
{
Task.Run(continuation);
}
}
public void UnsafeOnCompleted(Action continuation)
{
OnCompleted(continuation);
}
public void Complete(int bytesTransferred, SocketError socketError)
{
_error = socketError;
_bytesTransferred = bytesTransferred;
var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted);
if (continuation != null)
{
_ioScheduler.Schedule(state => ((Action)state)(), continuation);
}
}
}
}
@@ -0,0 +1,37 @@
using System;
using System.IO.Pipelines;
using System.Net.Sockets;
namespace MQTTnet.AspNetCore.Client.Tcp
{
public class SocketReceiver
{
private readonly Socket _socket;
private readonly SocketAsyncEventArgs _eventArgs = new SocketAsyncEventArgs();
private readonly SocketAwaitable _awaitable;
public SocketReceiver(Socket socket, PipeScheduler scheduler)
{
_socket = socket;
_awaitable = new SocketAwaitable(scheduler);
_eventArgs.UserToken = _awaitable;
_eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
}
public SocketAwaitable ReceiveAsync(Memory<byte> buffer)
{
#if NETCOREAPP2_1
_eventArgs.SetBuffer(buffer);
#else
var segment = buffer.GetArray();
_eventArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);
#endif
if (!_socket.ReceiveAsync(_eventArgs))
{
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
}
return _awaitable;
}
}
}
@@ -0,0 +1,101 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Net.Sockets;
#if NETCOREAPP2_1
using System.Runtime.InteropServices;
#endif
namespace MQTTnet.AspNetCore.Client.Tcp
{
public class SocketSender
{
private readonly Socket _socket;
private readonly SocketAsyncEventArgs _eventArgs = new SocketAsyncEventArgs();
private readonly SocketAwaitable _awaitable;
private List<ArraySegment<byte>> _bufferList;
public SocketSender(Socket socket, PipeScheduler scheduler)
{
_socket = socket;
_awaitable = new SocketAwaitable(scheduler);
_eventArgs.UserToken = _awaitable;
_eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
}
public SocketAwaitable SendAsync(in ReadOnlySequence<byte> buffers)
{
if (buffers.IsSingleSegment)
{
return SendAsync(buffers.First);
}
#if NETCOREAPP2_1
if (!_eventArgs.MemoryBuffer.Equals(Memory<byte>.Empty))
#else
if (_eventArgs.Buffer != null)
#endif
{
_eventArgs.SetBuffer(null, 0, 0);
}
_eventArgs.BufferList = GetBufferList(buffers);
if (!_socket.SendAsync(_eventArgs))
{
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
}
return _awaitable;
}
private SocketAwaitable SendAsync(ReadOnlyMemory<byte> memory)
{
// The BufferList getter is much less expensive then the setter.
if (_eventArgs.BufferList != null)
{
_eventArgs.BufferList = null;
}
#if NETCOREAPP2_1
_eventArgs.SetBuffer(MemoryMarshal.AsMemory(memory));
#else
var segment = memory.GetArray();
_eventArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);
#endif
if (!_socket.SendAsync(_eventArgs))
{
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
}
return _awaitable;
}
private List<ArraySegment<byte>> GetBufferList(in ReadOnlySequence<byte> buffer)
{
Debug.Assert(!buffer.IsEmpty);
Debug.Assert(!buffer.IsSingleSegment);
if (_bufferList == null)
{
_bufferList = new List<ArraySegment<byte>>();
}
else
{
// Buffers are pooled, so it's OK to root them until the next multi-buffer write.
_bufferList.Clear();
}
foreach (var b in buffer)
{
_bufferList.Add(b.GetArray());
}
return _bufferList;
}
}
}
@@ -0,0 +1,268 @@
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features;
using MQTTnet.Exceptions;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace MQTTnet.AspNetCore.Client.Tcp
{
public class TcpConnection : ConnectionContext
{
private volatile bool _aborted;
private readonly EndPoint _endPoint;
private SocketSender _sender;
private SocketReceiver _receiver;
private Socket _socket;
private IDuplexPipe _application;
public bool IsConnected { get; private set; }
public override string ConnectionId { get; set; }
public override IFeatureCollection Features { get; }
public override IDictionary<object, object> Items { get; set; }
public override IDuplexPipe Transport { get; set; }
public TcpConnection(EndPoint endPoint)
{
_endPoint = endPoint;
}
public TcpConnection(Socket socket)
{
_socket = socket;
_endPoint = socket.RemoteEndPoint;
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
}
#if NETCOREAPP3_1 || NET5_0_OR_GREATER
public override ValueTask DisposeAsync()
#else
public Task DisposeAsync()
#endif
{
IsConnected = false;
Transport?.Output.Complete();
Transport?.Input.Complete();
_socket?.Dispose();
#if NETCOREAPP3_1 || NET5_0_OR_GREATER
return base.DisposeAsync();
}
#else
return Task.CompletedTask;
}
#endif
public async Task StartAsync()
{
if (_socket == null)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
await _socket.ConnectAsync(_endPoint);
}
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
Transport = pair.Transport;
_application = pair.Application;
_ = ExecuteAsync();
IsConnected = true;
}
private async Task ExecuteAsync()
{
Exception sendError = null;
try
{
// Spawn send and receive logic
var receiveTask = DoReceive();
var sendTask = DoSend();
// If the sending task completes then close the receive
// We don't need to do this in the other direction because the kestrel
// will trigger the output closing once the input is complete.
if (await Task.WhenAny(receiveTask, sendTask) == sendTask)
{
// Tell the reader it's being aborted
_socket.Dispose();
}
// Now wait for both to complete
await receiveTask;
sendError = await sendTask;
// Dispose the socket(should noop if already called)
_socket.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected exception in {nameof(TcpConnection)}.{nameof(StartAsync)}: " + ex);
}
finally
{
// Complete the output after disposing the socket
_application.Input.Complete(sendError);
}
}
private async Task DoReceive()
{
Exception error = null;
try
{
await ProcessReceives();
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset)
{
error = new MqttCommunicationException(ex);
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted ||
ex.SocketErrorCode == SocketError.ConnectionAborted ||
ex.SocketErrorCode == SocketError.Interrupted ||
ex.SocketErrorCode == SocketError.InvalidArgument)
{
if (!_aborted)
{
// Calling Dispose after ReceiveAsync can cause an "InvalidArgument" error on *nix.
error = ConnectionAborted();
}
}
catch (ObjectDisposedException)
{
if (!_aborted)
{
error = ConnectionAborted();
}
}
catch (IOException ex)
{
error = ex;
}
catch (Exception ex)
{
error = new IOException(ex.Message, ex);
}
finally
{
if (_aborted)
{
error = error ?? ConnectionAborted();
}
_application.Output.Complete(error);
}
}
private async Task ProcessReceives()
{
while (true)
{
// Ensure we have some reasonable amount of buffer space
var buffer = _application.Output.GetMemory();
var bytesReceived = await _receiver.ReceiveAsync(buffer);
if (bytesReceived == 0)
{
// FIN
break;
}
_application.Output.Advance(bytesReceived);
var flushTask = _application.Output.FlushAsync();
if (!flushTask.IsCompleted)
{
await flushTask;
}
var result = flushTask.GetAwaiter().GetResult();
if (result.IsCompleted)
{
// Pipe consumer is shut down, do we stop writing
break;
}
}
}
private Exception ConnectionAborted()
{
return new MqttCommunicationException("Connection Aborted");
}
private async Task<Exception> DoSend()
{
Exception error = null;
try
{
await ProcessSends();
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
{
}
catch (ObjectDisposedException)
{
}
catch (IOException ex)
{
error = ex;
}
catch (Exception ex)
{
error = new IOException(ex.Message, ex);
}
finally
{
_aborted = true;
_socket.Shutdown(SocketShutdown.Both);
}
return error;
}
private async Task ProcessSends()
{
while (true)
{
// Wait for data to write from the pipe producer
var result = await _application.Input.ReadAsync();
var buffer = result.Buffer;
if (result.IsCanceled)
{
break;
}
var end = buffer.End;
var isCompleted = result.IsCompleted;
if (!buffer.IsEmpty)
{
await _sender.SendAsync(buffer);
}
_application.Input.AdvanceTo(end);
if (isCompleted)
{
break;
}
}
}
}
}
@@ -1,15 +0,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.
using Microsoft.AspNetCore.Connections;
namespace MQTTnet.AspNetCore;
public static class ConnectionBuilderExtensions
{
public static IConnectionBuilder UseMqtt(this IConnectionBuilder builder)
{
return builder.UseConnectionHandler<MqttConnectionHandler>();
}
}
-44
View File
@@ -1,44 +0,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.
using System.IO.Pipelines;
namespace MQTTnet.AspNetCore;
public class DuplexPipe : IDuplexPipe
{
public DuplexPipe(PipeReader reader, PipeWriter writer)
{
Input = reader;
Output = writer;
}
public PipeReader Input { get; }
public PipeWriter Output { get; }
public static DuplexPipePair CreateConnectionPair(PipeOptions inputOptions, PipeOptions outputOptions)
{
var input = new Pipe(inputOptions);
var output = new Pipe(outputOptions);
var transportToApplication = new DuplexPipe(output.Reader, input.Writer);
var applicationToTransport = new DuplexPipe(input.Reader, output.Writer);
return new DuplexPipePair(applicationToTransport, transportToApplication);
}
// This class exists to work around issues with value tuple on .NET Framework
public readonly struct DuplexPipePair
{
public IDuplexPipe Transport { get; }
public IDuplexPipe Application { get; }
public DuplexPipePair(IDuplexPipe transport, IDuplexPipe application)
{
Transport = transport;
Application = application;
}
}
}
@@ -1,21 +0,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.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
namespace MQTTnet.AspNetCore;
public static class EndpointRouterExtensions
{
public static void MapMqtt(this IEndpointRouteBuilder endpoints, string pattern)
{
ArgumentNullException.ThrowIfNull(endpoints);
endpoints.MapConnectionHandler<MqttConnectionHandler>(pattern, options =>
{
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
});
}
}
@@ -0,0 +1,48 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore.Extensions
{
public static class ApplicationBuilderExtensions
{
[Obsolete("This class is obsolete and will be removed in a future version. The recommended alternative is to use MapMqtt inside Microsoft.AspNetCore.Builder.UseEndpoints(...).")]
public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app, string path = "/mqtt")
{
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (!context.WebSockets.IsWebSocketRequest || context.Request.Path != path)
{
await next();
return;
}
string subProtocol = null;
if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
{
subProtocol = MqttSubProtocolSelector.SelectSubProtocol(requestedSubProtocolValues);
}
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol).ConfigureAwait(false))
{
await adapter.RunWebSocketConnectionAsync(webSocket, context);
}
});
return app;
}
public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action<MqttServer> configure)
{
var server = app.ApplicationServices.GetRequiredService<MqttServer>();
configure(server);
return app;
}
}
}
@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Connections;
namespace MQTTnet.AspNetCore.Extensions
{
public static class ConnectionBuilderExtensions
{
public static IConnectionBuilder UseMqtt(this IConnectionBuilder builder)
{
return builder.UseConnectionHandler<MqttConnectionHandler>();
}
}
}
@@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Connections;
#if NETCOREAPP3_1
using System;
#endif
namespace MQTTnet.AspNetCore.Extensions
{
public static class ConnectionRouteBuilderExtensions
{
#if NETCOREAPP3_1
[Obsolete("This class is obsolete and will be removed in a future version. The recommended alternative is to use MapMqtt inside Microsoft.AspNetCore.Builder.UseEndpoints(...).")]
#endif
#if NETCOREAPP3_1 || NETCOREAPP2_1 || NETSTANDARD
public static void MapMqtt(this ConnectionsRouteBuilder connection, PathString path)
{
connection.MapConnectionHandler<MqttConnectionHandler>(path, options =>
{
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
});
}
#endif
}
}
@@ -0,0 +1,21 @@
#if NETCOREAPP3_1 || NET5_0_OR_GREATER
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
namespace MQTTnet.AspNetCore
{
public static class EndpointRouterExtensions
{
public static void MapMqtt(this IEndpointRouteBuilder endpoints, string pattern)
{
endpoints.MapConnectionHandler<MqttConnectionHandler>(pattern, options =>
{
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
});
}
}
}
#endif
@@ -0,0 +1,110 @@
using System;
using System.Buffers;
using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Packets;
namespace MQTTnet.AspNetCore.Extensions
{
public static class ReaderExtensions
{
public static bool TryDecode(this MqttPacketFormatterAdapter formatter,
SpanBasedMqttPacketBodyReader reader,
in ReadOnlySequence<byte> input,
out MqttBasePacket packet,
out SequencePosition consumed,
out SequencePosition observed,
out int bytesRead)
{
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
packet = null;
consumed = input.Start;
observed = input.End;
bytesRead = 0;
var copy = input;
if (copy.Length < 2)
{
return false;
}
var fixedheader = copy.First.Span[0];
if (!TryReadBodyLength(ref copy, out int headerLength, out var bodyLength))
{
return false;
}
if (copy.Length < bodyLength)
{
return false;
}
var bodySlice = copy.Slice(0, bodyLength);
var buffer = bodySlice.GetMemory();
reader.SetBuffer(buffer);
var receivedMqttPacket = new ReceivedMqttPacket(fixedheader, reader, buffer.Length + 2);
if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
{
formatter.DetectProtocolVersion(receivedMqttPacket);
}
packet = formatter.Decode(receivedMqttPacket);
consumed = bodySlice.End;
observed = bodySlice.End;
bytesRead = headerLength + bodyLength;
return true;
}
private static ReadOnlyMemory<byte> GetMemory(this in ReadOnlySequence<byte> input)
{
if (input.IsSingleSegment)
{
return input.First;
}
// Should be rare
return input.ToArray();
}
private static bool TryReadBodyLength(ref ReadOnlySequence<byte> 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;
var value = 0;
byte encodedByte;
var index = 1;
headerLength = 0;
bodyLength = 0;
var temp = input.Slice(0, Math.Min(5, input.Length)).GetMemory().Span;
do
{
if (index == temp.Length)
{
return false;
}
encodedByte = temp[index];
index++;
value += (byte)(encodedByte & 127) * multiplier;
if (multiplier > 128 * 128 * 128)
{
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", temp.Slice(1, index).ToArray())}).");
}
multiplier *= 128;
} while ((encodedByte & 128) != 0);
input = input.Slice(index);
headerLength = index;
bodyLength = value;
return true;
}
}
}
@@ -0,0 +1,98 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Implementations;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore.Extensions
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, MqttServerOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
services.AddSingleton(options);
services.AddHostedMqttServer();
return services;
}
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action<MqttServerOptionsBuilder> configure)
{
services.AddSingleton<MqttServerOptions>(s =>
{
var builder = new MqttServerOptionsBuilder();
configure(builder);
return builder.Build();
});
services.AddHostedMqttServer();
return services;
}
public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
{
services.AddSingleton<MqttServerOptions>(s =>
{
var builder = new AspNetMqttServerOptionsBuilder(s);
configure(builder);
return builder.Build();
});
services.AddHostedMqttServer();
return services;
}
// public static IServiceCollection AddHostedMqttServer<TOptions>(this IServiceCollection services)
// where TOptions : MqttServerOptions
// {
// services.AddSingleton<MqttServerOptions, TOptions>();
//
// services.AddHostedMqttServer();
//
// return services;
// }
static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
{
var logger = new MqttNetEventLogger();
services.AddSingleton<IMqttNetLogger>(logger);
services.AddSingleton<MqttHostedServer>();
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
services.AddSingleton<MqttServer>(s => s.GetService<MqttHostedServer>());
return services;
}
public static IServiceCollection AddMqttWebSocketServerAdapter(this IServiceCollection services)
{
services.AddSingleton<MqttWebSocketServerAdapter>();
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttWebSocketServerAdapter>());
return services;
}
public static IServiceCollection AddMqttTcpServerAdapter(this IServiceCollection services)
{
services.AddSingleton<MqttTcpServerAdapter>();
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttTcpServerAdapter>());
return services;
}
public static IServiceCollection AddMqttConnectionHandler(this IServiceCollection services)
{
services.AddSingleton<MqttConnectionHandler>();
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttConnectionHandler>());
return services;
}
}
}
@@ -1,26 +1,70 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
<AssemblyName>MQTTnet.AspNetCore</AssemblyName>
<RootNamespace>MQTTnet.AspNetCore</RootNamespace>
<IsAotCompatible>true</IsAotCompatible>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Company>The contributors of MQTTnet</Company>
<Product>MQTTnet</Product>
<Description>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.</Description>
<Authors>The contributors of MQTTnet</Authors>
<PackageId>MQTTnet.AspNetCore</PackageId>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Copyright>Christian Kratky 2016-2021</Copyright>
<PackageProjectUrl>https://github.com/chkr1011/MQTTnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/chkr1011/MQTTnet.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>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</PackageTags>
<NeutralLanguage>en-US</NeutralLanguage>
<EnableNETAnalyzers>True</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<PackageIcon>nuget.png</PackageIcon>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReleaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</PackageReleaseNotes>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App"/>
<None Include="..\..\Images\nuget.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MQTTnet.Server\MQTTnet.Server.csproj"/>
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj"/>
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net6.0' ">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1' and '$(TargetFramework)' != 'net5.0' and '$(TargetFramework)' != 'net6.0' ">
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
</ItemGroup>
</Project>
@@ -1,32 +0,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.
using MQTTnet.Adapter;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Formatter;
namespace MQTTnet.AspNetCore;
public sealed class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
{
public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger)
{
ArgumentNullException.ThrowIfNull(options);
switch (options.ChannelOptions)
{
case MqttClientTcpOptions tcpOptions:
{
var tcpConnection = new SocketConnection(tcpOptions.RemoteEndpoint);
var formatter = new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(4096, 65535));
return new MqttConnectionContext(formatter, tcpConnection);
}
default:
{
throw new NotSupportedException();
}
}
}
}
+154 -219
View File
@@ -1,257 +1,192 @@
// 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;
using System.IO.Pipelines;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Connections.Features;
using Microsoft.AspNetCore.Http.Features;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Internal;
using MQTTnet.Packets;
using System;
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;
public sealed class MqttConnectionContext : IMqttChannelAdapter
namespace MQTTnet.AspNetCore
{
readonly ConnectionContext _connection;
readonly AsyncLock _writerLock = new();
PipeReader _input;
PipeWriter _output;
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
public sealed class MqttConnectionContext : IMqttChannelAdapter
{
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
_connection = connection ?? throw new ArgumentNullException(nameof(connection));
if (!(_connection is SocketConnection tcp) || tcp.IsConnected)
readonly AsyncLock _writerLock = new AsyncLock();
readonly SpanBasedMqttPacketBodyReader _reader;
PipeReader _input;
PipeWriter _output;
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
{
_input = connection.Transport.Input;
_output = connection.Transport.Output;
}
}
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
Connection = connection ?? throw new ArgumentNullException(nameof(connection));
public long BytesReceived { get; private set; }
public long BytesSent { get; private set; }
public X509Certificate2 ClientCertificate
{
get
{
// mqtt over tcp
var tlsFeature = _connection.Features.Get<ITlsConnectionFeature>();
if (tlsFeature != null)
if (Connection.Transport != null)
{
return tlsFeature.ClientCertificate;
_input = Connection.Transport.Input;
_output = Connection.Transport.Output;
}
// mqtt over websocket
var httpFeature = _connection.Features.Get<IHttpContextFeature>();
return httpFeature?.HttpContext?.Connection.ClientCertificate;
}
}
public bool IsSecureConnection
{
get
{
// mqtt over tcp
var tlsFeature = _connection.Features.Get<ITlsConnectionFeature>();
if (tlsFeature != null)
{
return true;
}
// mqtt over websocket
var httpFeature = _connection.Features.Get<IHttpContextFeature>();
if (httpFeature?.HttpContext != null)
{
return httpFeature.HttpContext.Request.IsHttps;
}
return false;
}
}
public EndPoint LocalEndPoint
{
get
{
// mqtt over tcp
if (_connection.LocalEndPoint != null)
{
return _connection.LocalEndPoint;
}
// mqtt over websocket
var httpFeature = _connection.Features.Get<IHttpConnectionFeature>();
if (httpFeature?.LocalIpAddress != null)
{
return new IPEndPoint(httpFeature.LocalIpAddress, httpFeature.LocalPort);
}
return null;
}
}
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
public EndPoint RemoteEndPoint
{
get
{
// mqtt over tcp
if (_connection.RemoteEndPoint != null)
{
return _connection.RemoteEndPoint;
}
// mqtt over websocket
var httpFeature = _connection.Features.Get<IHttpConnectionFeature>();
if (httpFeature?.RemoteIpAddress != null)
{
return new IPEndPoint(httpFeature.RemoteIpAddress, httpFeature.RemotePort);
}
return null;
}
}
public async Task ConnectAsync(CancellationToken cancellationToken)
{
if (_connection is SocketConnection tcp && !tcp.IsConnected)
{
await tcp.StartAsync().ConfigureAwait(false);
_reader = new SpanBasedMqttPacketBodyReader();
}
_input = _connection.Transport.Input;
_output = _connection.Transport.Output;
}
public Task DisconnectAsync(CancellationToken cancellationToken)
{
_input?.Complete();
_output?.Complete();
return Task.CompletedTask;
}
public void Dispose()
{
_writerLock.Dispose();
}
public async Task<MqttPacket> ReceivePacketAsync(CancellationToken cancellationToken)
{
try
public string Endpoint
{
while (!cancellationToken.IsCancellationRequested)
get
{
ReadResult readResult;
var readTask = _input.ReadAsync(cancellationToken);
if (readTask.IsCompleted)
#if NETCOREAPP3_1
if (Connection?.RemoteEndPoint != null)
{
readResult = readTask.Result;
return Connection.RemoteEndPoint.ToString();
}
else
#endif
var connection = Http?.HttpContext?.Connection;
if (connection == null)
{
readResult = await readTask.ConfigureAwait(false);
return Connection.ConnectionId;
}
var buffer = readResult.Buffer;
var consumed = buffer.Start;
var observed = buffer.Start;
try
{
if (!buffer.IsEmpty)
{
if (PacketFormatterAdapter.TryDecode(buffer, out var packet, out consumed, out observed, out var received))
{
BytesReceived += received;
return packet;
}
}
else if (readResult.IsCompleted)
{
throw new MqttCommunicationException("Connection Aborted");
}
}
finally
{
// The buffer was sliced up to where it was consumed, so we can just advance to the start.
// We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
// before yielding the read again.
_input.AdvanceTo(consumed, observed);
}
return $"{connection.RemoteIpAddress}:{connection.RemotePort}";
}
}
catch (Exception exception)
{
// completing the channel makes sure that there is no more data read after a protocol error
_input?.Complete(exception);
_output?.Complete(exception);
throw;
public bool IsSecureConnection => Http?.HttpContext?.Request?.IsHttps ?? false;
public X509Certificate2 ClientCertificate => Http?.HttpContext?.Connection?.ClientCertificate;
public ConnectionContext Connection { get; }
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
public long BytesSent { get; set; }
public long BytesReceived { get; set; }
public bool IsReadingPacket { get; private set; }
IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
if (Connection is TcpConnection tcp && !tcp.IsConnected)
{
await tcp.StartAsync().ConfigureAwait(false);
}
_input = Connection.Transport.Input;
_output = Connection.Transport.Output;
}
cancellationToken.ThrowIfCancellationRequested();
return null;
}
public void ResetStatistics()
{
BytesReceived = 0;
BytesSent = 0;
}
public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
{
using (await _writerLock.EnterAsync(cancellationToken).ConfigureAwait(false))
public Task DisconnectAsync(CancellationToken cancellationToken)
{
_input?.Complete();
_output?.Complete();
return Task.CompletedTask;
}
public async Task<MqttBasePacket> ReceivePacketAsync(CancellationToken cancellationToken)
{
var input = Connection.Transport.Input;
try
{
var buffer = PacketFormatterAdapter.Encode(packet);
if (buffer.Payload.Length == 0)
while (!cancellationToken.IsCancellationRequested)
{
// zero copy
// https://github.com/dotnet/runtime/blob/e31ddfdc4f574b26231233dc10c9a9c402f40590/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeWriter.cs#L279
await _output.WriteAsync(buffer.Packet, cancellationToken).ConfigureAwait(false);
}
else
{
WritePacketBuffer(_output, buffer);
await _output.FlushAsync(cancellationToken).ConfigureAwait(false);
}
ReadResult readResult;
var readTask = input.ReadAsync(cancellationToken);
if (readTask.IsCompleted)
{
readResult = readTask.Result;
}
else
{
readResult = await readTask.ConfigureAwait(false);
}
BytesSent += buffer.Length;
var buffer = readResult.Buffer;
var consumed = buffer.Start;
var observed = buffer.Start;
try
{
if (!buffer.IsEmpty)
{
if (PacketFormatterAdapter.TryDecode(_reader, buffer, out var packet, out consumed, out observed, out var received))
{
BytesReceived += received;
return packet;
}
else
{
// we did receive something but the message is not yet complete
IsReadingPacket = true;
}
}
else if (readResult.IsCompleted)
{
throw new MqttCommunicationException("Connection Aborted");
}
}
finally
{
// The buffer was sliced up to where it was consumed, so we can just advance to the start.
// We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
// before yielding the read again.
input.AdvanceTo(consumed, observed);
}
}
}
catch (Exception e)
{
// completing the channel makes sure that there is no more data read after a protocol error
_input?.Complete(e);
_output?.Complete(e);
throw;
}
finally
{
PacketFormatterAdapter.Cleanup();
IsReadingPacket = false;
}
cancellationToken.ThrowIfCancellationRequested();
return null;
}
public void ResetStatistics()
{
BytesReceived = 0;
BytesSent = 0;
}
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
var formatter = PacketFormatterAdapter;
using (await _writerLock.WaitAsync(cancellationToken).ConfigureAwait(false))
{
var buffer = formatter.Encode(packet);
var msg = buffer.ToArray().AsMemory();
var output = _output;
var result = await output.WriteAsync(msg, cancellationToken).ConfigureAwait(false);
if (result.IsCompleted)
{
BytesSent += msg.Length;
}
PacketFormatterAdapter.FreeBuffer();
}
}
public void Dispose()
{
}
}
static void WritePacketBuffer(PipeWriter output, MqttPacketBuffer buffer)
{
// copy MqttPacketBuffer's Packet and Payload to the same buffer block of PipeWriter
// MqttPacket will be transmitted within the bounds of a WebSocket frame after PipeWriter.FlushAsync
var span = output.GetSpan(buffer.Length);
buffer.Packet.AsSpan().CopyTo(span);
var offset = buffer.Packet.Count;
buffer.Payload.CopyTo(span.Slice(offset));
output.Advance(buffer.Length);
}
}
}
@@ -1,53 +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 Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Formatter;
using MQTTnet.Server;
using System;
using System.Threading.Tasks;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
namespace MQTTnet.AspNetCore;
public sealed class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
namespace MQTTnet.AspNetCore
{
MqttServerOptions _serverOptions;
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
public void Dispose()
public class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
{
}
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
public override async Task OnConnectedAsync(ConnectionContext connection)
{
// required for websocket transport to work
var transferFormatFeature = connection.Features.Get<ITransferFormatFeature>();
if (transferFormatFeature != null)
public override async Task OnConnectedAsync(ConnectionContext connection)
{
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
// required for websocket transport to work
var transferFormatFeature = connection.Features.Get<ITransferFormatFeature>();
if (transferFormatFeature != null)
{
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
}
var writer = new SpanBasedMqttPacketWriter();
var formatter = new MqttPacketFormatterAdapter(writer);
using (var adapter = new MqttConnectionContext(formatter, connection))
{
var clientHandler = ClientHandler;
if (clientHandler != null)
{
await clientHandler(adapter).ConfigureAwait(false);
}
}
}
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax));
using var adapter = new MqttConnectionContext(formatter, connection);
var clientHandler = ClientHandler;
if (clientHandler != null)
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
{
return Task.CompletedTask;
}
public Task StopAsync()
{
return Task.CompletedTask;
}
public void Dispose()
{
await clientHandler(adapter).ConfigureAwait(false);
}
}
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
{
_serverOptions = options;
return Task.CompletedTask;
}
public Task StopAsync()
{
return Task.CompletedTask;
}
}
}
+20 -35
View File
@@ -1,44 +1,29 @@
// 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.Diagnostics.Logger;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore;
public sealed class MqttHostedServer : MqttServer, IHostedService
namespace MQTTnet.AspNetCore
{
readonly IHostApplicationLifetime _hostApplicationLifetime;
readonly MqttServerFactory _mqttFactory;
public MqttHostedServer(
IHostApplicationLifetime hostApplicationLifetime,
MqttServerFactory mqttFactory,
MqttServerOptions options,
IEnumerable<IMqttServerAdapter> adapters,
IMqttNetLogger logger) : base(options, adapters, logger)
public sealed class MqttHostedServer : MqttServer, IHostedService
{
_mqttFactory = mqttFactory ?? throw new ArgumentNullException(nameof(mqttFactory));
_hostApplicationLifetime = hostApplicationLifetime;
}
public MqttHostedServer(MqttServerOptions options, IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger)
: base(options, adapters, logger)
{
}
public async Task StartAsync(CancellationToken cancellationToken)
{
// The yield makes sure that the hosted service is considered up and running.
await Task.Yield();
public Task StartAsync(CancellationToken cancellationToken)
{
_ = StartAsync();
return Task.CompletedTask;
}
_hostApplicationLifetime.ApplicationStarted.Register(OnStarted);
}
public Task StopAsync(CancellationToken cancellationToken)
{
return StopAsync(_mqttFactory.CreateMqttServerStopOptionsBuilder().Build());
}
void OnStarted()
{
_ = StartAsync();
public Task StopAsync(CancellationToken cancellationToken)
{
return StopAsync();
}
}
}
@@ -1,31 +1,33 @@
// 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;
namespace MQTTnet.AspNetCore;
public static class MqttSubProtocolSelector
namespace MQTTnet.AspNetCore
{
public static string SelectSubProtocol(HttpRequest request)
public static class MqttSubProtocolSelector
{
ArgumentNullException.ThrowIfNull(request);
string subProtocol = null;
if (request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
public static string SelectSubProtocol(HttpRequest request)
{
subProtocol = SelectSubProtocol(requestedSubProtocolValues);
if (request == null) throw new ArgumentNullException(nameof(request));
string subProtocol = null;
if (request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
{
subProtocol = SelectSubProtocol(requestedSubProtocolValues);
}
return subProtocol;
}
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
{
if (requestedSubProtocolValues == null) throw new ArgumentNullException(nameof(requestedSubProtocolValues));
return subProtocol;
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
return requestedSubProtocolValues
.OrderByDescending(p => p.Length)
.FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
}
}
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
{
ArgumentNullException.ThrowIfNull(requestedSubProtocolValues);
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
return requestedSubProtocolValues.OrderByDescending(p => p.Length).FirstOrDefault(p => p.ToLowerInvariant().StartsWith("mqtt", StringComparison.InvariantCulture));
}
}
}
@@ -1,64 +1,64 @@
// 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.Net;
using System.Net.WebSockets;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using MQTTnet.Server;
using System;
using System.Net.WebSockets;
using System.Threading.Tasks;
using MQTTnet.Diagnostics;
namespace MQTTnet.AspNetCore;
public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
namespace MQTTnet.AspNetCore
{
IMqttNetLogger _logger = MqttNetNullLogger.Instance;
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
public void Dispose()
public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
{
}
IMqttNetLogger _logger = new MqttNetNullLogger();
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
{
ArgumentNullException.ThrowIfNull(webSocket);
var remoteAddress = httpContext.Connection.RemoteIpAddress;
var remoteEndPoint = remoteAddress == null ? null : new IPEndPoint(remoteAddress, httpContext.Connection.RemotePort);
var clientCertificate = await httpContext.Connection.GetClientCertificateAsync().ConfigureAwait(false);
try
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
{
var isSecureConnection = clientCertificate != null;
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
return Task.CompletedTask;
}
var clientHandler = ClientHandler;
if (clientHandler != null)
public Task StopAsync()
{
return Task.CompletedTask;
}
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
{
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";
var clientCertificate = await httpContext.Connection.GetClientCertificateAsync().ConfigureAwait(false);
try
{
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
var channel = new MqttWebSocketChannel(webSocket, remoteEndPoint, isSecureConnection, clientCertificate);
var isSecureConnection = clientCertificate != null;
using var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger);
await clientHandler(channelAdapter).ConfigureAwait(false);
var clientHandler = ClientHandler;
if (clientHandler != null)
{
var writer = new SpanBasedMqttPacketWriter();
var formatter = new MqttPacketFormatterAdapter(writer);
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, null, _logger))
{
await clientHandler(channelAdapter).ConfigureAwait(false);
}
}
}
finally
{
clientCertificate?.Dispose();
}
}
finally
public void Dispose()
{
clientCertificate?.Dispose();
}
}
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
return Task.CompletedTask;
}
public Task StopAsync()
{
return Task.CompletedTask;
}
}
@@ -1,134 +0,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.
using System.Buffers;
using System.Runtime.InteropServices;
using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Packets;
namespace MQTTnet.AspNetCore;
public static class ReaderExtensions
{
public static bool TryDecode(
this MqttPacketFormatterAdapter formatter,
in ReadOnlySequence<byte> input,
out MqttPacket packet,
out SequencePosition consumed,
out SequencePosition observed,
out int bytesRead)
{
ArgumentNullException.ThrowIfNull(formatter);
packet = null;
consumed = input.Start;
observed = input.End;
bytesRead = 0;
var copy = input;
if (copy.Length < 2)
{
return false;
}
if (!TryReadBodyLength(ref copy, out var headerLength, out var bodyLength))
{
return false;
}
var fixedHeader = copy.First.Span[0];
copy = copy.Slice(headerLength);
if (copy.Length < bodyLength)
{
return false;
}
var bodySlice = copy.Slice(0, bodyLength);
var bodySegment = GetArraySegment(ref bodySlice);
var receivedMqttPacket = new ReceivedMqttPacket(fixedHeader, bodySegment, headerLength + bodyLength);
if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
{
formatter.DetectProtocolVersion(receivedMqttPacket);
}
packet = formatter.Decode(receivedMqttPacket);
consumed = bodySlice.End;
observed = bodySlice.End;
bytesRead = headerLength + bodyLength;
return true;
}
static ArraySegment<byte> GetArraySegment(ref ReadOnlySequence<byte> input)
{
if (input.IsSingleSegment && MemoryMarshal.TryGetArray(input.First, out var segment))
{
return segment;
}
// Should be rare
var array = input.ToArray();
return new ArraySegment<byte>(array);
}
static void ThrowProtocolViolationException(ReadOnlySpan<byte> valueSpan, int index)
{
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", valueSpan.Slice(1, index).ToArray())}).");
}
static bool TryReadBodyLength(ref ReadOnlySequence<byte> input, out int headerLength, out int bodyLength)
{
var valueSequence = input.Slice(0, Math.Min(5, input.Length));
if (valueSequence.IsSingleSegment)
{
var valueSpan = valueSequence.FirstSpan;
return TryReadBodyLength(valueSpan, out headerLength, out bodyLength);
}
else
{
Span<byte> valueSpan = stackalloc byte[8];
valueSequence.CopyTo(valueSpan);
valueSpan = valueSpan.Slice(0, (int)valueSequence.Length);
return TryReadBodyLength(valueSpan, out headerLength, out bodyLength);
}
}
static bool TryReadBodyLength(ReadOnlySpan<byte> span, 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;
var value = 0;
byte encodedByte;
var index = 1;
headerLength = 0;
bodyLength = 0;
do
{
if (index == span.Length)
{
return false;
}
encodedByte = span[index];
index++;
value += (byte)(encodedByte & 127) * multiplier;
if (multiplier > 128 * 128 * 128)
{
ThrowProtocolViolationException(span, index);
}
multiplier *= 128;
} while ((encodedByte & 128) != 0);
headerLength = index;
bodyLength = value;
return true;
}
}
@@ -1,108 +0,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.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Server;
using MQTTnet.Server.Internal.Adapter;
namespace MQTTnet.AspNetCore;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, MqttServerOptions options)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(options);
services.AddSingleton(options);
services.AddHostedMqttServer();
return services;
}
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action<MqttServerOptionsBuilder> configure)
{
ArgumentNullException.ThrowIfNull(services);
var serverOptionsBuilder = new MqttServerOptionsBuilder();
configure?.Invoke(serverOptionsBuilder);
var options = serverOptionsBuilder.Build();
return AddHostedMqttServer(services, options);
}
public static void AddHostedMqttServer(this IServiceCollection services)
{
// The user may have these services already registered.
services.TryAddSingleton<IMqttNetLogger>(MqttNetNullLogger.Instance);
services.TryAddSingleton(new MqttServerFactory());
services.AddSingleton<MqttHostedServer>();
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
services.AddSingleton<MqttServer>(s => s.GetService<MqttHostedServer>());
}
public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
{
ArgumentNullException.ThrowIfNull(services);
services.AddSingleton(
s =>
{
var builder = new AspNetMqttServerOptionsBuilder(s);
configure(builder);
return builder.Build();
});
services.AddHostedMqttServer();
return services;
}
public static IServiceCollection AddMqttConnectionHandler(this IServiceCollection services)
{
services.AddSingleton<MqttConnectionHandler>();
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttConnectionHandler>());
return services;
}
public static void AddMqttLogger(this IServiceCollection services, IMqttNetLogger logger)
{
ArgumentNullException.ThrowIfNull(services);
services.AddSingleton(logger);
}
public static IServiceCollection AddMqttServer(this IServiceCollection serviceCollection, Action<MqttServerOptionsBuilder> configure = null)
{
ArgumentNullException.ThrowIfNull(serviceCollection);
serviceCollection.AddMqttConnectionHandler();
serviceCollection.AddHostedMqttServer(configure);
return serviceCollection;
}
public static IServiceCollection AddMqttTcpServerAdapter(this IServiceCollection services)
{
services.AddSingleton<MqttTcpServerAdapter>();
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttTcpServerAdapter>());
return services;
}
public static IServiceCollection AddMqttWebSocketServerAdapter(this IServiceCollection services)
{
services.AddSingleton<MqttWebSocketServerAdapter>();
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttWebSocketServerAdapter>());
return services;
}
}
@@ -1,68 +0,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.
using System.Diagnostics;
using System.IO.Pipelines;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
namespace MQTTnet.AspNetCore;
public class SocketAwaitable(PipeScheduler ioScheduler) : ICriticalNotifyCompletion
{
static readonly Action CallbackCompleted = () =>
{
};
int _bytesTransferred;
Action _callback;
SocketError _error;
public bool IsCompleted => ReferenceEquals(_callback, CallbackCompleted);
public void Complete(int bytesTransferred, SocketError socketError)
{
_error = socketError;
_bytesTransferred = bytesTransferred;
var continuation = Interlocked.Exchange(ref _callback, CallbackCompleted);
if (continuation != null)
{
ioScheduler.Schedule(state => ((Action)state)(), continuation);
}
}
public SocketAwaitable GetAwaiter()
{
return this;
}
public int GetResult()
{
Debug.Assert(ReferenceEquals(_callback, CallbackCompleted));
_callback = null;
if (_error != SocketError.Success)
{
throw new SocketException((int)_error);
}
return _bytesTransferred;
}
public void OnCompleted(Action continuation)
{
if (ReferenceEquals(_callback, CallbackCompleted) || ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), CallbackCompleted))
{
Task.Run(continuation);
}
}
public void UnsafeOnCompleted(Action continuation)
{
OnCompleted(continuation);
}
}
@@ -1,263 +0,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.
using System.IO.Pipelines;
using System.Net;
using System.Net.Sockets;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features;
using MQTTnet.Exceptions;
namespace MQTTnet.AspNetCore;
public sealed class SocketConnection : ConnectionContext
{
readonly EndPoint _endPoint;
volatile bool _aborted;
IDuplexPipe _application;
SocketReceiver _receiver;
SocketSender _sender;
Socket _socket;
public SocketConnection(EndPoint endPoint)
{
_endPoint = endPoint;
}
public SocketConnection(Socket socket)
{
_socket = socket;
_endPoint = socket.RemoteEndPoint;
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
}
public override string ConnectionId { get; set; }
public override IFeatureCollection Features { get; }
public bool IsConnected { get; private set; }
public override IDictionary<object, object> Items { get; set; }
public override IDuplexPipe Transport { get; set; }
public override ValueTask DisposeAsync()
{
IsConnected = false;
Transport?.Output.Complete();
Transport?.Input.Complete();
_socket?.Dispose();
return base.DisposeAsync();
}
public async Task StartAsync()
{
if (_socket == null)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
await _socket.ConnectAsync(_endPoint);
}
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
Transport = pair.Transport;
_application = pair.Application;
_ = ExecuteAsync();
IsConnected = true;
}
static MqttCommunicationException ConnectionAborted()
{
return new MqttCommunicationException("Connection Aborted");
}
async Task DoReceive()
{
Exception error = null;
try
{
await ProcessReceives();
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset)
{
error = new MqttCommunicationException(ex);
}
catch (SocketException ex) when (ex.SocketErrorCode is SocketError.OperationAborted or SocketError.ConnectionAborted or SocketError.Interrupted or SocketError.InvalidArgument)
{
if (!_aborted)
{
// Calling Dispose after ReceiveAsync can cause an "InvalidArgument" error on *nix.
error = ConnectionAborted();
}
}
catch (ObjectDisposedException)
{
if (!_aborted)
{
error = ConnectionAborted();
}
}
catch (IOException ex)
{
error = ex;
}
catch (Exception ex)
{
error = new IOException(ex.Message, ex);
}
finally
{
if (_aborted)
{
error ??= ConnectionAborted();
}
await _application.Output.CompleteAsync(error);
}
}
async Task<Exception> DoSend()
{
Exception error = null;
try
{
await ProcessSends();
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
{
}
catch (ObjectDisposedException)
{
}
catch (IOException ex)
{
error = ex;
}
catch (Exception ex)
{
error = new IOException(ex.Message, ex);
}
finally
{
_aborted = true;
_socket.Shutdown(SocketShutdown.Both);
}
return error;
}
async Task ExecuteAsync()
{
Exception sendError = null;
try
{
// Spawn send and receive logic
var receiveTask = DoReceive();
var sendTask = DoSend();
// If the sending task completes then close the receive
// We don't need to do this in the other direction because the kestrel
// will trigger the output closing once the input is complete.
if (await Task.WhenAny(receiveTask, sendTask).ConfigureAwait(false) == sendTask)
{
// Tell the reader it's being aborted
_socket.Dispose();
}
// Now wait for both to complete
await receiveTask;
sendError = await sendTask;
// Dispose the socket(should noop if already called)
_socket.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected exception in {nameof(SocketConnection)}.{nameof(StartAsync)}: " + ex);
}
finally
{
// Complete the output after disposing the socket
await _application.Input.CompleteAsync(sendError).ConfigureAwait(false);
}
}
async Task ProcessReceives()
{
while (true)
{
// Ensure we have some reasonable amount of buffer space
var buffer = _application.Output.GetMemory();
var bytesReceived = await _receiver.ReceiveAsync(buffer);
if (bytesReceived == 0)
{
// FIN
break;
}
_application.Output.Advance(bytesReceived);
var flushTask = _application.Output.FlushAsync();
FlushResult result;
if (!flushTask.IsCompleted)
{
result = await flushTask;
}
else
{
result = flushTask.GetAwaiter().GetResult();
}
if (result.IsCompleted)
{
// Pipe consumer is shut down, do we stop writing
break;
}
}
}
async Task ProcessSends()
{
while (true)
{
// Wait for data to write from the pipe producer
var result = await _application.Input.ReadAsync();
var buffer = result.Buffer;
if (result.IsCanceled)
{
break;
}
var end = buffer.End;
var isCompleted = result.IsCompleted;
if (!buffer.IsEmpty)
{
await _sender.SendAsync(buffer);
}
_application.Input.AdvanceTo(end);
if (isCompleted)
{
break;
}
}
}
}
@@ -1,40 +0,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.
using System.IO.Pipelines;
using System.Net.Sockets;
namespace MQTTnet.AspNetCore;
public sealed class SocketReceiver : IDisposable
{
readonly SocketAwaitable _awaitable;
readonly SocketAsyncEventArgs _eventArgs = new();
readonly Socket _socket;
public SocketReceiver(Socket socket, PipeScheduler scheduler)
{
_socket = socket;
_awaitable = new SocketAwaitable(scheduler);
_eventArgs.UserToken = _awaitable;
_eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
}
public SocketAwaitable ReceiveAsync(Memory<byte> buffer)
{
_eventArgs.SetBuffer(buffer);
if (!_socket.ReceiveAsync(_eventArgs))
{
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
}
return _awaitable;
}
public void Dispose()
{
_eventArgs.Dispose();
}
}
-96
View File
@@ -1,96 +0,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.
using System.Buffers;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace MQTTnet.AspNetCore;
public sealed class SocketSender : IDisposable
{
readonly SocketAwaitable _awaitable;
readonly SocketAsyncEventArgs _eventArgs = new();
readonly Socket _socket;
List<ArraySegment<byte>> _bufferList;
public SocketSender(Socket socket, PipeScheduler scheduler)
{
_socket = socket;
_awaitable = new SocketAwaitable(scheduler);
_eventArgs.UserToken = _awaitable;
_eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
}
public void Dispose()
{
_eventArgs.Dispose();
}
public SocketAwaitable SendAsync(in ReadOnlySequence<byte> buffers)
{
if (buffers.IsSingleSegment)
{
return SendAsync(buffers.First);
}
if (!_eventArgs.MemoryBuffer.Equals(Memory<byte>.Empty))
{
_eventArgs.SetBuffer(null, 0, 0);
}
_eventArgs.BufferList = GetBufferList(buffers);
if (!_socket.SendAsync(_eventArgs))
{
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
}
return _awaitable;
}
List<ArraySegment<byte>> GetBufferList(in ReadOnlySequence<byte> buffer)
{
Debug.Assert(!buffer.IsEmpty);
Debug.Assert(!buffer.IsSingleSegment);
if (_bufferList == null)
{
_bufferList = new List<ArraySegment<byte>>();
}
else
{
// Buffers are pooled, so it's OK to root them until the next multi-buffer write.
_bufferList.Clear();
}
foreach (var b in buffer)
{
_bufferList.Add(b.GetArray());
}
return _bufferList;
}
SocketAwaitable SendAsync(ReadOnlyMemory<byte> memory)
{
// The BufferList getter is much less expensive then the setter.
if (_eventArgs.BufferList != null)
{
_eventArgs.BufferList = null;
}
_eventArgs.SetBuffer(MemoryMarshal.AsMemory(memory));
if (!_socket.SendAsync(_eventArgs))
{
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
}
return _awaitable;
}
}
@@ -0,0 +1,129 @@
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using System;
using System.Buffers.Binary;
using System.Text;
namespace MQTTnet.AspNetCore
{
public class SpanBasedMqttPacketBodyReader : IMqttPacketBodyReader
{
ReadOnlyMemory<byte> _buffer;
int _offset;
public void SetBuffer(ReadOnlyMemory<byte> 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<byte> 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;
}
}
}
@@ -0,0 +1,144 @@
using MQTTnet.Formatter;
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Text;
namespace MQTTnet.AspNetCore
{
public class SpanBasedMqttPacketWriter : IMqttPacketWriter
{
readonly ArrayPool<byte> _pool = ArrayPool<byte>.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;
}
}
}
@@ -1 +0,0 @@
[assembly: DoNotParallelize]
@@ -1,60 +0,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.
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using MQTTnet.Internal;
namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class AsyncLockBenchmark : BaseBenchmark
{
[Benchmark]
public async Task Synchronize_100_Tasks()
{
const int tasksCount = 100;
var tasks = new Task[tasksCount];
var asyncLock = new AsyncLock();
var globalI = 0;
for (var i = 0; i < tasksCount; i++)
{
tasks[i] = Task.Run(
async () =>
{
using (await asyncLock.EnterAsync().ConfigureAwait(false))
{
var localI = globalI;
await Task.Delay(5).ConfigureAwait(false); // Increase the chance for wrong data.
localI++;
globalI = localI;
}
});
}
await Task.WhenAll(tasks).ConfigureAwait(false);
if (globalI != tasksCount)
{
throw new InvalidOperationException($"Code is broken ({globalI})!");
}
}
[Benchmark]
public async Task Wait_100_000_Times()
{
var asyncLock = new AsyncLock();
using var cancellationToken = new CancellationTokenSource();
for (var i = 0; i < 100000; i++)
{
using (await asyncLock.EnterAsync(cancellationToken.Token).ConfigureAwait(false))
{
}
}
}
}
@@ -1,9 +0,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.
namespace MQTTnet.Benchmarks;
public abstract class BaseBenchmark
{
}
@@ -1,92 +0,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.
using BenchmarkDotNet.Attributes;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Tests.Mockups;
namespace MQTTnet.Benchmarks;
[MemoryDiagnoser]
public sealed class ChannelAdapterBenchmark : BaseBenchmark, IDisposable
{
MqttChannelAdapter _channelAdapter;
int _iterations;
MqttPublishPacket _packet;
MemoryStream _stream;
[Benchmark]
public void Receive_10000_Messages()
{
_stream.Position = 0;
for (var i = 0; i < 10000; i++)
{
_channelAdapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
}
_stream.Position = 0;
}
[Benchmark]
public void Send_10000_Messages()
{
_stream.Position = 0;
for (var i = 0; i < 10000; i++)
{
_channelAdapter.SendPacketAsync(_packet, CancellationToken.None).GetAwaiter().GetResult();
}
_stream.Position = 0;
}
[GlobalSetup]
public void Setup()
{
_packet = new MqttPublishPacket
{
Topic = "A"
};
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
var serializedPacket = Join(serializer.Encode(_packet).Join());
_iterations = 10000;
_stream = new MemoryStream(_iterations * serializedPacket.Length);
for (var i = 0; i < _iterations; i++)
{
_stream.Write(serializedPacket, 0, serializedPacket.Length);
}
_stream.Position = 0;
var channel = new MemoryMqttChannel(_stream);
_channelAdapter = new MqttChannelAdapter(channel, serializer, new MqttNetEventLogger());
}
static byte[] Join(params ArraySegment<byte>[] chunks)
{
var buffer = new MemoryStream();
foreach (var chunk in chunks)
{
buffer.Write(chunk.Array, chunk.Offset, chunk.Count);
}
return buffer.ToArray();
}
public void Dispose()
{
_channelAdapter?.Dispose();
_stream?.Dispose();
}
}
@@ -1,91 +0,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.
using System.Diagnostics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using MQTTnet.Diagnostics.Logger;
namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter]
[MemoryDiagnoser]
public class LoggerBenchmark : BaseBenchmark
{
MqttNetNullLogger _nullLogger;
MqttNetSourceLogger _sourceNullLogger;
MqttNetEventLogger _eventLogger;
MqttNetSourceLogger _sourceEventLogger;
MqttNetEventLogger _eventLoggerNoListener;
MqttNetSourceLogger _sourceEventLoggerNoListener;
bool _useHandler;
[GlobalSetup]
public void Setup()
{
_nullLogger = new MqttNetNullLogger();
_sourceNullLogger = _nullLogger.WithSource("Source");
_eventLogger = new MqttNetEventLogger();
_eventLogger.LogMessagePublished += OnLogMessagePublished;
_sourceEventLogger = _eventLogger.WithSource("Source");
_eventLoggerNoListener = new MqttNetEventLogger();
_sourceEventLoggerNoListener = _eventLoggerNoListener.WithSource("Source");
}
void OnLogMessagePublished(object sender, MqttNetLogMessagePublishedEventArgs eventArgs)
{
if (_useHandler)
{
Debug.WriteLine(eventArgs.LogMessage.ToString());
}
}
[Benchmark]
public void Log_10000_Messages_No_Listener()
{
_useHandler = false;
for (var i = 0; i < 10000; i++)
{
_sourceEventLoggerNoListener.Verbose("test log message {0}", "parameter");
}
}
[Benchmark]
public void Log_10000_Messages_With_To_String()
{
_useHandler = true;
for (var i = 0; i < 10000; i++)
{
_sourceEventLogger.Verbose("test log message {0}", "parameter");
}
}
[Benchmark]
public void Log_10000_Messages_Without_To_String()
{
_useHandler = false;
for (var i = 0; i < 10000; i++)
{
_sourceEventLogger.Verbose("test log message {0}", "parameter");
}
}
[Benchmark]
public void Log_10000_Messages_With_NullLogger()
{
for (var i = 0; i < 10000; i++)
{
_sourceNullLogger.Verbose("test log message {0}", "parameter");
}
}
}
@@ -1,22 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<DebugType>Full</DebugType>
<IsPackable>false</IsPackable>
<NoWarn>$(NoWarn);CS8892;CA1707;CA1051;CA1822</NoWarn>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Source\MQTTnet.Server\MQTTnet.Server.csproj"/>
<ProjectReference Include="..\..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj"/>
<ProjectReference Include="..\MQTTnet.Tests\MQTTnet.Tests.csproj"/>
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj"/>
</ItemGroup>
</Project>
@@ -1,39 +0,0 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter, RankColumn]
[MemoryDiagnoser]
public class MemoryCopyBenchmark
{
const int MaxLength = 1024 * 8;
byte[] _source;
byte[] _target;
[Params(64 - 1, 128 - 1, 256 - 1, 512 - 1, 1024 - 1, 2048 - 1, 5096 - 1)]
public int Length { get; set; }
[GlobalSetup]
public void Setup()
{
_source = new byte[MaxLength];
_target = new byte[MaxLength];
}
[Benchmark(Baseline = true)]
public void Array_Copy()
{
Array.Copy(_source, 0, _target, 0, Length);
}
[Benchmark]
public void Memory_Copy()
{
Internal.MqttMemoryHelper.Copy(_source, 0, _target, 0, Length);
}
}
@@ -1,196 +0,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.
using BenchmarkDotNet.Attributes;
using MQTTnet.Packets;
using MQTTnet.Server;
namespace MQTTnet.Benchmarks;
[MemoryDiagnoser]
public sealed class MessageDeliveryBenchmark : BaseBenchmark, IDisposable
{
List<string> _allSubscribedTopics; // Keep track of the subset of topics that are subscribed
CancellationTokenSource _cancellationTokenSource;
object _lockMsgCount;
int _messagesExpectedCount;
int _messagesReceivedCount;
Dictionary<string, IMqttClient> _mqttPublisherClientsByPublisherName;
MqttServer _mqttServer;
List<IMqttClient> _mqttSubscriberClients;
[Params(1000, 10000)] public int _numPublishers;
[Params(5, 10, 20, 50)] public int _numSubscribedTopicsPerSubscriber;
[Params(10)] public int _numSubscribers;
[Params(1, 5)] public int _numTopicsPerPublisher;
Dictionary<string, string> _publisherByTopic;
Dictionary<string, List<string>> _topicsByPublisher;
[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;
}
[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 };
// 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
{
// Ignore all errors.
}
_cancellationTokenSource.Dispose();
if (_messagesReceivedCount < _messagesExpectedCount)
{
throw new InvalidOperationException($"Messages Received Count mismatch, expected {_messagesExpectedCount}, received {_messagesReceivedCount}");
}
}
public void Dispose()
{
_cancellationTokenSource?.Dispose();
_mqttServer?.Dispose();
}
[GlobalSetup]
public void Setup()
{
_lockMsgCount = new object();
TopicGenerator.Generate(_numPublishers, _numTopicsPerPublisher, out _topicsByPublisher, out _, out _);
// Create server
var serverFactory = new MqttServerFactory();
var clientFactory = new MqttClientFactory();
var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
_mqttServer = serverFactory.CreateMqttServer(serverOptions);
_mqttServer.StartAsync().GetAwaiter().GetResult();
// Create publisher clients
_mqttPublisherClientsByPublisherName = [];
foreach (var pt in _topicsByPublisher)
{
var publisherName = pt.Key;
var mqttClient = clientFactory.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<IMqttClient>();
for (var i = 0; i < _numSubscribers; i++)
{
var mqttSubscriberClient = clientFactory.CreateMqttClient();
_mqttSubscriberClients.Add(mqttSubscriberClient);
var subscriberOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").WithClientId("subscriber" + i).Build();
mqttSubscriberClient.ApplicationMessageReceivedAsync += _ =>
{
// 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();
}
var allTopics = new List<string>();
_publisherByTopic = [];
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<string>();
var totalNumTopics = _numPublishers * _numTopicsPerPublisher;
var topicIndexStep = totalNumTopics / (_numSubscribedTopicsPerSubscriber * _numSubscribers);
if (topicIndexStep * _numSubscribedTopicsPerSubscriber * _numSubscribers != totalNumTopics)
{
throw new InvalidOperationException(
$"The total number of topics must be divisible by the number of subscribed topics across all subscribers. Total number of topics: {totalNumTopics}, topic step: {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 MqttClientSubscribeOptionsBuilder().WithTopicFilter(new MqttTopicFilter { Topic = topic }).Build();
mqttSubscriber.SubscribeAsync(subOptions).GetAwaiter().GetResult();
}
}
Task.Delay(1000).GetAwaiter().GetResult();
}
}
@@ -1,48 +0,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.
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using MQTTnet.Server;
namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter]
[RankColumn]
[MemoryDiagnoser]
public class MessageProcessingBenchmark : BaseBenchmark
{
MqttApplicationMessage _message;
IMqttClient _mqttClient;
MqttServer _mqttServer;
[Benchmark]
public void Send_10000_Messages()
{
for (var i = 0; i < 10000; i++)
{
_mqttClient.PublishAsync(_message).GetAwaiter().GetResult();
}
}
[GlobalSetup]
public void Setup()
{
var serverOptions = new MqttServerOptionsBuilder().Build();
var serverFactory = new MqttServerFactory();
_mqttServer = serverFactory.CreateMqttServer(serverOptions);
var clientFactory = new MqttClientFactory();
_mqttClient = clientFactory.CreateMqttClient();
_mqttServer.StartAsync().GetAwaiter().GetResult();
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
_mqttClient.ConnectAsync(clientOptions).GetAwaiter().GetResult();
_message = new MqttApplicationMessageBuilder().WithTopic("A").Build();
}
}
@@ -1,72 +0,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.
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using MQTTnet.AspNetCore;
using MQTTnet.Diagnostics.Logger;
namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class MessageProcessingMqttConnectionContextBenchmark : BaseBenchmark
{
IWebHost _host;
IMqttClient _mqttClient;
MqttApplicationMessage _message;
[GlobalSetup]
public void Setup()
{
_host = WebHost.CreateDefaultBuilder()
.UseKestrel(o => o.ListenAnyIP(1883, l => l.UseMqtt()))
.ConfigureServices(services => {
services
.AddHostedMqttServer(mqttServerOptions => mqttServerOptions.WithoutDefaultEndpoint())
.AddMqttConnectionHandler();
})
.Configure(app => {
app.UseMqttServer(_ => {
});
})
.Build();
var factory = new MqttClientFactory();
_mqttClient = factory.CreateMqttClient(new MqttNetEventLogger(), new MqttClientConnectionContextFactory());
_host.StartAsync().GetAwaiter().GetResult();
var clientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("localhost").Build();
_mqttClient.ConnectAsync(clientOptions).GetAwaiter().GetResult();
_message = new MqttApplicationMessageBuilder()
.WithTopic("A")
.Build();
}
[GlobalCleanup]
public void Cleanup()
{
_mqttClient.DisconnectAsync().GetAwaiter().GetResult();
_mqttClient.Dispose();
_host.StopAsync().GetAwaiter().GetResult();
_host.Dispose();
}
[Benchmark]
public void Send_10000_Messages()
{
for (var i = 0; i < 10000; i++)
{
_mqttClient.PublishAsync(_message).GetAwaiter().GetResult();
}
}
}
@@ -1,41 +0,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.
using System.Text;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using MQTTnet.Formatter;
namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class MqttBufferReaderBenchmark
{
byte[] _buffer;
int _bufferLength;
[GlobalSetup]
public void GlobalSetup()
{
var writer = new MqttBufferWriter(1024, 1024);
writer.WriteString("hgfjkdfkjlghfdjghdfljkdfhgdlkjfshgsldkfjghsdflkjghdsflkjhrstiuoghlkfjbhnfbutghjoiöjhklötnbhtroliöuhbjntluiobkjzbhtdrlskbhtruhjkfthgbkftgjhgfiklhotriuöhbjtrsioöbtrsötrhträhtrühjtriüoätrhjtsrölbktrbnhtrulöbionhströloubinströoliubhnsöotrunbtöroisntröointröioujhgötiohjgötorshjnbgtöorihbnjtröoihbjntröobntröoibntrjhötrohjbtröoihntröoibnrtoiöbtrjnboöitrhjtnriohötrhjtöroihjtroöihjtroösibntsroönbotöirsbntöoihjntröoihntroöbtrboöitrnhoöitrhjntröoishbnjtröosbhtröbntriohjtröoijtöoitbjöotibjnhöotirhbjntroöibhnjrtoöibnhtroöibnhtörsbnhtöoirbnhtöroibntoörhjnbträöbtrbträbtrbtirbätrsibohjntrsöiobthnjiohjsrtoib");
_buffer = writer.GetBuffer();
_bufferLength = writer.Length;
}
[Benchmark]
public void Use_Span()
{
var span = _buffer.AsSpan(0, _bufferLength);
Encoding.UTF8.GetString(span);
}
[Benchmark]
public void Use_Encoding()
{
Encoding.UTF8.GetString(_buffer, 0, _bufferLength);
}
}
@@ -1,94 +0,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.
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using MQTTnet.Formatter;
using MQTTnet.Tests.Mockups;
namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public class MqttPacketReaderWriterBenchmark : BaseBenchmark
{
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.WriteBinary(_demoPayload);
writer.WriteByte(0x01);
writer.WriteByte(0x02);
writer.WriteVariableByteInteger(5647382);
writer.WriteString("A relative short string.");
writer.WriteVariableByteInteger(8574489);
writer.WriteBinary(_demoPayload);
writer.WriteByte(2);
writer.WriteByte(0x02);
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
writer.WriteBinary(_demoPayload);
_readPayload = new ArraySegment<byte>(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.WriteBinary(_demoPayload);
writer.WriteByte(2);
writer.WriteByte(0x02);
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
writer.WriteBinary(_demoPayload);
writer.Reset(0);
}
}
}
@@ -1,92 +0,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.
using System.Buffers;
using System.Reflection;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using MQTTnet.Channel;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Implementations;
using MQTTnet.Server;
using MQTTnet.Server.Internal.Adapter;
namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser]
public sealed class MqttTcpChannelBenchmark : BaseBenchmark, IDisposable
{
MqttTcpChannel _clientChannel;
MqttServer _mqttServer;
IMqttChannel _serverChannel;
[Benchmark]
public async Task Send_10000_Chunks()
{
var size = 5;
var iterations = 10000;
await Task.WhenAll(WriteAsync(iterations, size), ReadAsync(iterations, size));
}
[GlobalSetup]
public void Setup()
{
var serverFactory = new MqttServerFactory();
var tcpServer = new MqttTcpServerAdapter();
tcpServer.ClientHandler += args =>
{
_serverChannel = (IMqttChannel)args.GetType().GetField("_channel", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(args);
return Task.CompletedTask;
};
var serverOptions = new MqttServerOptionsBuilder().Build();
_mqttServer = serverFactory.CreateMqttServer(serverOptions, new[] { tcpServer }, new MqttNetEventLogger());
_mqttServer.StartAsync().GetAwaiter().GetResult();
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
var tcpOptions = (MqttClientTcpOptions)clientOptions.ChannelOptions;
_clientChannel = new MqttTcpChannel(new MqttClientOptions { ChannelOptions = tcpOptions });
_clientChannel.ConnectAsync(CancellationToken.None).GetAwaiter().GetResult();
}
async Task ReadAsync(int iterations, int size)
{
await Task.Yield();
var expected = iterations * size;
long read = 0;
while (read < expected)
{
var readResult = await _clientChannel.ReadAsync(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
read += readResult;
}
}
async Task WriteAsync(int iterations, int size)
{
await Task.Yield();
var buffer = new ReadOnlySequence<byte>(new byte[size]);
for (var i = 0; i < iterations; i++)
{
await _serverChannel.WriteAsync(buffer, true, CancellationToken.None).ConfigureAwait(false);
}
}
public void Dispose()
{
_clientChannel?.Dispose();
_mqttServer?.Dispose();
_serverChannel?.Dispose();
}
}
-136
View File
@@ -1,136 +0,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.
using BenchmarkDotNet.Running;
namespace MQTTnet.Benchmarks;
public static class Program
{
static int _selectedBenchmarkIndex;
static List<Type> _benchmarks;
public static void Main(string[] arguments)
{
_benchmarks = CollectBenchmarks();
HandleArguments(arguments);
while (true)
{
RenderMenu();
var key = Console.ReadKey(true);
if (key.Key == ConsoleKey.DownArrow)
{
_selectedBenchmarkIndex++;
if (_selectedBenchmarkIndex > _benchmarks.Count - 1)
{
_selectedBenchmarkIndex = 0;
}
}
else if (key.Key == ConsoleKey.UpArrow)
{
_selectedBenchmarkIndex--;
if (_selectedBenchmarkIndex < 0)
{
_selectedBenchmarkIndex = _benchmarks.Count - 1;
}
}
else if (key.Key == ConsoleKey.Escape)
{
Environment.Exit(0);
}
else if (key.Key == ConsoleKey.Enter)
{
break;
}
}
BenchmarkRunner.Run(_benchmarks[_selectedBenchmarkIndex]);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Press any key to exit");
Console.ReadLine();
}
static List<Type> CollectBenchmarks()
{
var benchmarks = new List<Type>();
var types = typeof(Program).Assembly.GetExportedTypes();
foreach (var type in types)
{
if (typeof(BaseBenchmark).IsAssignableFrom(type))
{
if (type != typeof(BaseBenchmark))
{
benchmarks.Add(type);
}
}
}
return benchmarks.OrderBy(b => b.Name).ToList();
}
static void HandleArguments(string[] arguments)
{
if (arguments.Length == 0)
{
return;
}
// Allow for preselection to avoid developer frustration.
if (int.TryParse(arguments[0], out var benchmarkIndex))
{
_selectedBenchmarkIndex = benchmarkIndex;
return;
}
_selectedBenchmarkIndex = _benchmarks.FindIndex(b => b.Name.Equals(arguments[0], StringComparison.Ordinal));
if (_selectedBenchmarkIndex < 0)
{
_selectedBenchmarkIndex = 0;
}
if (_selectedBenchmarkIndex > _benchmarks.Count - 1)
{
_selectedBenchmarkIndex = _benchmarks.Count - 1;
}
}
static void RenderMenu()
{
Console.Clear();
Console.WriteLine($"MQTTnet - Benchmarks");
Console.WriteLine("-----------------------------------------------");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Press arrow keys for benchmark selection");
Console.WriteLine("Press Enter for benchmark execution");
Console.WriteLine("Press Esc for exit");
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine();
for (var index = 0; index < _benchmarks.Count; index++)
{
var benchmark = _benchmarks[index];
if (_selectedBenchmarkIndex == index)
{
Console.Write("-> ");
}
else
{
Console.Write(" ");
}
Console.WriteLine(benchmark.Name);
}
Console.WriteLine();
}
}
@@ -1,97 +0,0 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using MQTTnet.AspNetCore;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using System.Buffers;
using System.IO.Pipelines;
namespace MQTTnet.Benchmarks;
[SimpleJob(RuntimeMoniker.Net60)]
[RPlotExporter, RankColumn]
[MemoryDiagnoser]
public sealed class ReaderExtensionsBenchmark : IDisposable, IAsyncDisposable
{
MqttPacketFormatterAdapter _mqttPacketFormatter;
MemoryStream _stream;
[GlobalSetup]
public void GlobalSetup()
{
_mqttPacketFormatter = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
var mqttMessage = new MqttApplicationMessageBuilder()
.WithTopic("topic")
.WithPayload(new byte[10 * 1024])
.Build();
var packet = MqttPublishPacketFactory.Create(mqttMessage);
var buffer = _mqttPacketFormatter.Encode(packet);
_stream = new MemoryStream();
_stream.Write(buffer.Packet);
_stream.Write(buffer.Payload.ToArray());
_mqttPacketFormatter.Cleanup();
}
[Benchmark]
public async Task After()
{
_stream.Position = 0;
var input = PipeReader.Create(_stream);
while (true)
{
ReadResult readResult;
var readTask = input.ReadAsync();
if (readTask.IsCompleted)
{
readResult = readTask.Result;
}
else
{
readResult = await readTask.ConfigureAwait(false);
}
var buffer = readResult.Buffer;
var consumed = buffer.Start;
var observed = buffer.Start;
try
{
if (!buffer.IsEmpty)
{
if (_mqttPacketFormatter.TryDecode(buffer, out _, out consumed, out observed, out _))
{
break;
}
}
else if (readResult.IsCompleted)
{
throw new MqttCommunicationException("Connection Aborted");
}
}
finally
{
// The buffer was sliced up to where it was consumed, so we can just advance to the start.
// We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
// before yielding the read again.
input.AdvanceTo(consumed, observed);
}
}
}
public void Dispose()
{
_stream?.Dispose();
}
public async ValueTask DisposeAsync()
{
if (_stream != null)
{
await _stream.DisposeAsync();
}
}
}

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