mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 00:15:15 +08:00
Compare commits
93
Commits
v2.8.4
...
v3.0.0-alpha1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38f2453c92 | ||
|
|
058bb65d31 | ||
|
|
38b215e9b5 | ||
|
|
45df33c9a5 | ||
|
|
89becaa191 | ||
|
|
6c87e4e039 | ||
|
|
cbe06d6b87 | ||
|
|
74e4fd8b3f | ||
|
|
cc113a6c34 | ||
|
|
fca9876111 | ||
|
|
ac9f6a36f8 | ||
|
|
929b60e044 | ||
|
|
acb433dd4c | ||
|
|
c4e8bf7f39 | ||
|
|
ba4fdfb51c | ||
|
|
dfda916283 | ||
|
|
40326df9a9 | ||
|
|
a94444098a | ||
|
|
0c13e7a504 | ||
|
|
0dba57fe75 | ||
|
|
6483f896c1 | ||
|
|
8fa9943c3d | ||
|
|
195357835b | ||
|
|
28b2562112 | ||
|
|
ab24368de1 | ||
|
|
9b51404b4d | ||
|
|
619eaf284d | ||
|
|
86c348df58 | ||
|
|
fdb2ec897c | ||
|
|
b091fee554 | ||
|
|
14ec6ee793 | ||
|
|
0cfef0649a | ||
|
|
7292af7db4 | ||
|
|
bc1a197c4f | ||
|
|
ccc275902f | ||
|
|
0f5fb8aa3a | ||
|
|
83eca982e9 | ||
|
|
017f17a6b1 | ||
|
|
b3564e01a4 | ||
|
|
43105f71d8 | ||
|
|
40c408a9c0 | ||
|
|
7baff7c080 | ||
|
|
91a918d30b | ||
|
|
2a93fc18e4 | ||
|
|
31de6ee186 | ||
|
|
74d59673a8 | ||
|
|
d1c3d9b453 | ||
|
|
baa9241216 | ||
|
|
2c6c3ac6a4 | ||
|
|
b2c31331b6 | ||
|
|
9ff39c1fd9 | ||
|
|
210d57b254 | ||
|
|
7292cedd2f | ||
|
|
48d5afa799 | ||
|
|
3db22eea51 | ||
|
|
2fbb44ec66 | ||
|
|
d7b98080f5 | ||
|
|
3e7b41e712 | ||
|
|
5f18f35e02 | ||
|
|
af39e87703 | ||
|
|
ace25af0e8 | ||
|
|
8d78455414 | ||
|
|
fed16d7710 | ||
|
|
fcff3a2c8d | ||
|
|
803dd6f64e | ||
|
|
a1e258eb24 | ||
|
|
e9df698a1f | ||
|
|
bddcbf6999 | ||
|
|
be22e2551d | ||
|
|
4e01c7f13d | ||
|
|
7181813f12 | ||
|
|
6fc9f12c34 | ||
|
|
cc4e37fa40 | ||
|
|
ccadd4e41a | ||
|
|
a5caab62f8 | ||
|
|
2bff218806 | ||
|
|
95c30a2843 | ||
|
|
28efbcdb3d | ||
|
|
ca7952ab33 | ||
|
|
62693af196 | ||
|
|
5abee2512f | ||
|
|
c9a6155834 | ||
|
|
aad3ab903f | ||
|
|
4c76a898b6 | ||
|
|
d273148bc5 | ||
|
|
5314f5c67a | ||
|
|
22aab44d49 | ||
|
|
b819746165 | ||
|
|
73d33e2200 | ||
|
|
740bc3aa30 | ||
|
|
583b32f17a | ||
|
|
b7ab29473b | ||
|
|
5a59cd278c |
-10
@@ -1,10 +0,0 @@
|
||||
language: csharp
|
||||
solution: MQTTnet.sln
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- dotnet: 2.0.0
|
||||
mono: none
|
||||
dist: trusty
|
||||
env: DOTNETCORE=1 # optional, can be used to take different code paths in your script
|
||||
- mono: latest
|
||||
@@ -10,15 +10,15 @@
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>This is a support library to integrate MQTTnet into AspNetCore.</description>
|
||||
<releaseNotes>* For more release notes please check the MQTTnet release notes.
|
||||
</releaseNotes>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="2.8.4" />
|
||||
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.0" />
|
||||
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.0.1" />
|
||||
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.0.1" />
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
|
||||
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.3" />
|
||||
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.1.1" />
|
||||
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.1.1" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
|
||||
@@ -10,12 +10,11 @@
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description>
|
||||
<releaseNotes>* For more release notes please check the MQTTnet release notes.
|
||||
</releaseNotes>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="2.8.4" />
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
|
||||
@@ -10,12 +10,11 @@
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
|
||||
<releaseNotes>* For more release notes please check the MQTTnet release notes.
|
||||
</releaseNotes>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="2.8.4" />
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
|
||||
@@ -10,7 +10,12 @@
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
||||
<releaseNotes>* [Client] Fixed a deadlock when an exception is fired while connecting (thanks to @malibVB).
|
||||
<releaseNotes> * [Core] Added support for MQTTv5 packages.
|
||||
* [Client] Added new MQTTv5 features to options builder.
|
||||
* [Client] Added uniform API across all supported MQTT versions (BREAKING CHANGE!)
|
||||
* [Server] Added support for MQTTv5 clients. The server will still return _success_ for all cases at the moment even if more granular codes are available.
|
||||
|
||||
* [Note] Due to MQTTv5 a lot of new classes were introduced. This required adding new namespaces as well. Most classes are backward compatible but new namespaces must be added.
|
||||
</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
@@ -19,7 +24,7 @@
|
||||
<dependency id="NETStandard.Library" version="1.3.0" />
|
||||
<dependency id="System.Net.Security" version="4.3.2" />
|
||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.1" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
<group targetFramework="netstandard2.0">
|
||||
<dependency id="NETStandard.Library" version="2.0.0" />
|
||||
@@ -28,7 +33,7 @@
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
<group targetFramework="uap10.0">
|
||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.4" />
|
||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.9" />
|
||||
</group>
|
||||
<group targetFramework="net452">
|
||||
</group>
|
||||
|
||||
+5
-1
@@ -39,7 +39,11 @@ if ($path) {
|
||||
|
||||
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
|
||||
|
||||
New-Item -ItemType Directory -Force -Path .\NuGet
|
||||
(Get-Content MQTTnet.AspNetCore.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.AspNetCore.nuspec
|
||||
(Get-Content MQTTnet.Extensions.Rpc.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.Rpc.nuspec
|
||||
(Get-Content MQTTnet.Extensions.ManagedClient.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.ManagedClient.nuspec
|
||||
|
||||
New-Item -ItemType Directory -Force -Path .\NuGet
|
||||
.\nuget.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,222 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2010
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Build\build.ps1 = Build\build.ps1
|
||||
Build\MQTTnet.AspNetCore.nuspec = Build\MQTTnet.AspNetCore.nuspec
|
||||
Build\MQTTnet.Extensions.ManagedClient.nuspec = Build\MQTTnet.Extensions.ManagedClient.nuspec
|
||||
Build\MQTTnet.Extensions.Rpc.nuspec = Build\MQTTnet.Extensions.Rpc.nuspec
|
||||
Build\MQTTnet.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
|
||||
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
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+1
-1
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2010
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
||||
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
|
||||
|
||||
@@ -20,12 +20,13 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
||||
|
||||
* Async support
|
||||
* TLS 1.2 support for client and server (but not UWP servers)
|
||||
* Extensible communication channels (i.e. In-Memory, TCP, TCP+TLS, WS)
|
||||
* Extensible communication channels (e.g. In-Memory, TCP, TCP+TLS, WS)
|
||||
* Lightweight (only the low level implementation of MQTT, no overhead)
|
||||
* Performance optimized (processing ~70.000 messages / second)*
|
||||
* Uniform API across all supported versions of the MQTT protocol
|
||||
* Interfaces included for mocking and testing
|
||||
* Access to internal trace messages
|
||||
* Unit tested (~90 tests)
|
||||
* Unit tested (~120 tests)
|
||||
|
||||
\* 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_.
|
||||
|
||||
@@ -48,6 +49,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
||||
* 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.
|
||||
|
||||
## Supported frameworks
|
||||
|
||||
@@ -62,7 +64,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
||||
|
||||
## Supported MQTT versions
|
||||
|
||||
* 5.0.0 (planned)
|
||||
* 5.0.0
|
||||
* 3.1.1
|
||||
* 3.1.0
|
||||
|
||||
@@ -85,16 +87,17 @@ This project also listed at Open Collective (https://opencollective.com/mqttnet)
|
||||
|
||||
This library is used in the following projects:
|
||||
|
||||
* Azure Functions MQTT Bindings, [CaseOnline.Azure.WebJobs.Extensions.Mqtt](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 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))
|
||||
* Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>)
|
||||
|
||||
If you use this library and want to see your project here please let me know.
|
||||
If you use this library and want to see your project here please create a pull request.
|
||||
|
||||
## MIT License
|
||||
|
||||
Copyright (c) 2017-2018 Christian Kratky
|
||||
Copyright (c) 2017-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
|
||||
|
||||
@@ -3,8 +3,9 @@ using System.Net;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore.Client.Tcp;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client
|
||||
{
|
||||
@@ -14,15 +15,13 @@ namespace MQTTnet.AspNetCore.Client
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
var serializer = new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion };
|
||||
|
||||
switch (options.ChannelOptions)
|
||||
{
|
||||
case MqttClientTcpOptions tcpOptions:
|
||||
{
|
||||
var endpoint = new DnsEndPoint(tcpOptions.Server, tcpOptions.GetPort());
|
||||
var tcpConnection = new TcpConnection(endpoint);
|
||||
return new MqttConnectionContext(serializer, tcpConnection);
|
||||
return new MqttConnectionContext(new MqttPacketFormatterAdapter(options.ProtocolVersion), tcpConnection);
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore.Client.Tcp;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Serializer;
|
||||
using System;
|
||||
using System.IO.Pipelines;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class MqttConnectionContext : IMqttChannelAdapter
|
||||
{
|
||||
public MqttConnectionContext(
|
||||
IMqttPacketSerializer packetSerializer,
|
||||
ConnectionContext connection)
|
||||
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
|
||||
{
|
||||
PacketSerializer = packetSerializer;
|
||||
Connection = connection;
|
||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||
Connection = connection ?? throw new ArgumentNullException(nameof(connection));
|
||||
}
|
||||
|
||||
public string Endpoint => Connection.ConnectionId;
|
||||
public ConnectionContext Connection { get; }
|
||||
public IMqttPacketSerializer PacketSerializer { get; }
|
||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
public event EventHandler ReadingPacketStarted;
|
||||
public event EventHandler ReadingPacketCompleted;
|
||||
|
||||
private readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Connection is TcpConnection tcp && !tcp.IsConnected)
|
||||
@@ -59,7 +60,7 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
else
|
||||
{
|
||||
readResult = await readTask;
|
||||
readResult = await readTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var buffer = readResult.Buffer;
|
||||
@@ -71,7 +72,7 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
if (!buffer.IsEmpty)
|
||||
{
|
||||
if (PacketSerializer.TryDeserialize(buffer, out var packet, out consumed, out observed))
|
||||
if (PacketFormatterAdapter.TryDecode(buffer, out var packet, out consumed, out observed))
|
||||
{
|
||||
return packet;
|
||||
}
|
||||
@@ -83,7 +84,7 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
else if (readResult.IsCompleted)
|
||||
{
|
||||
break;
|
||||
throw new MqttCommunicationException("Connection Aborted");
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -104,10 +105,20 @@ namespace MQTTnet.AspNetCore
|
||||
return null;
|
||||
}
|
||||
|
||||
public Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
var buffer = PacketSerializer.Serialize(packet);
|
||||
return Connection.Transport.Output.WriteAsync(buffer.AsMemory(), cancellationToken).AsTask();
|
||||
var buffer = PacketFormatterAdapter.Encode(packet).AsMemory();
|
||||
var output = Connection.Transport.Output;
|
||||
|
||||
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await output.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_writerSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Server;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
|
||||
{
|
||||
{
|
||||
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
||||
|
||||
public override async Task OnConnectedAsync(ConnectionContext connection)
|
||||
@@ -21,9 +21,7 @@ namespace MQTTnet.AspNetCore
|
||||
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
|
||||
}
|
||||
|
||||
|
||||
var serializer = new MqttPacketSerializer();
|
||||
using (var adapter = new MqttConnectionContext(serializer, connection))
|
||||
using (var adapter = new MqttConnectionContext(new MqttPacketFormatterAdapter(), connection))
|
||||
{
|
||||
var args = new MqttServerAdapterClientAcceptedEventArgs(adapter);
|
||||
ClientAccepted?.Invoke(this, args);
|
||||
@@ -41,7 +39,7 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ using System.Net.WebSockets;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
@@ -27,7 +27,7 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
|
||||
|
||||
var clientAdapter = new MqttChannelAdapter(new MqttWebSocketChannel(webSocket, endpoint), new MqttPacketSerializer(), new MqttNetLogger().CreateChildLogger(nameof(MqttWebSocketServerAdapter)));
|
||||
var clientAdapter = new MqttChannelAdapter(new MqttWebSocketChannel(webSocket, endpoint), new MqttPacketFormatterAdapter(), new MqttNetLogger().CreateChildLogger(nameof(MqttWebSocketServerAdapter)));
|
||||
|
||||
var eventArgs = new MqttServerAdapterClientAcceptedEventArgs(clientAdapter);
|
||||
ClientAccepted?.Invoke(this, eventArgs);
|
||||
@@ -37,7 +37,7 @@ namespace MQTTnet.AspNetCore
|
||||
await eventArgs.SessionTask.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StopAsync().GetAwaiter().GetResult();
|
||||
|
||||
@@ -2,19 +2,22 @@
|
||||
using System.Buffers;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Serializer;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public static class ReaderExtensions
|
||||
{
|
||||
public static bool TryDeserialize(this IMqttPacketSerializer serializer, in ReadOnlySequence<byte> input, out MqttBasePacket packet, out SequencePosition consumed, out SequencePosition observed)
|
||||
public static bool TryDecode(this MqttPacketFormatterAdapter formatter, in ReadOnlySequence<byte> input, out MqttBasePacket packet, out SequencePosition consumed, out SequencePosition observed)
|
||||
{
|
||||
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
|
||||
|
||||
packet = null;
|
||||
consumed = input.Start;
|
||||
observed = input.End;
|
||||
var copy = input;
|
||||
|
||||
if (copy.Length < 2)
|
||||
{
|
||||
return false;
|
||||
@@ -33,7 +36,7 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
var bodySlice = copy.Slice(0, bodyLength);
|
||||
var buffer = bodySlice.GetArray();
|
||||
packet = serializer.Deserialize(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(buffer, 0, buffer.Length)));
|
||||
packet = formatter.Decode(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(buffer, 0, buffer.Length), buffer.Length + 2));
|
||||
consumed = bodySlice.End;
|
||||
observed = bodySlice.End;
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ApplicationMessageSkippedEventArgs : EventArgs
|
||||
{
|
||||
public ApplicationMessageSkippedEventArgs(ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
||||
}
|
||||
|
||||
public ManagedMqttApplicationMessage ApplicationMessage { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
@@ -10,11 +11,13 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
bool IsStarted { get; }
|
||||
bool IsConnected { get; }
|
||||
int PendingApplicationMessagesCount { get; }
|
||||
IManagedMqttClientOptions Options { get; }
|
||||
|
||||
event EventHandler<MqttClientConnectedEventArgs> Connected;
|
||||
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
||||
|
||||
event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
|
||||
event EventHandler<ApplicationMessageSkippedEventArgs> ApplicationMessageSkipped;
|
||||
|
||||
event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed;
|
||||
event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
@@ -12,5 +14,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
TimeSpan ConnectionCheckInterval { get; }
|
||||
|
||||
IManagedMqttClientStorage Storage { get; }
|
||||
|
||||
int MaxPendingMessages { get; }
|
||||
|
||||
MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MqttClientConnectedEventArgs = MQTTnet.Client.Connecting.MqttClientConnectedEventArgs;
|
||||
using MqttClientDisconnectedEventArgs = MQTTnet.Client.Disconnecting.MqttClientDisconnectedEventArgs;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ManagedMqttClient : IManagedMqttClient
|
||||
{
|
||||
private readonly BlockingCollection<ManagedMqttApplicationMessage> _messageQueue = new BlockingCollection<ManagedMqttApplicationMessage>();
|
||||
private readonly BlockingQueue<ManagedMqttApplicationMessage> _messageQueue = new BlockingQueue<ManagedMqttApplicationMessage>();
|
||||
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
|
||||
private readonly HashSet<string> _unsubscriptions = new HashSet<string>();
|
||||
|
||||
@@ -24,7 +28,6 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
private CancellationTokenSource _publishingCancellationToken;
|
||||
|
||||
private ManagedMqttClientStorageManager _storageManager;
|
||||
private IManagedMqttClientOptions _options;
|
||||
|
||||
private bool _subscriptionsNotPushed;
|
||||
|
||||
@@ -44,12 +47,14 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
public bool IsConnected => _mqttClient.IsConnected;
|
||||
public bool IsStarted => _connectionCancellationToken != null;
|
||||
public int PendingApplicationMessagesCount => _messageQueue.Count;
|
||||
public IManagedMqttClientOptions Options { get; private set; }
|
||||
|
||||
public event EventHandler<MqttClientConnectedEventArgs> Connected;
|
||||
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
||||
|
||||
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||
public event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
|
||||
public event EventHandler<ApplicationMessageSkippedEventArgs> ApplicationMessageSkipped;
|
||||
|
||||
public event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed;
|
||||
public event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed;
|
||||
@@ -66,16 +71,16 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
if (_connectionCancellationToken != null) throw new InvalidOperationException("The managed client is already started.");
|
||||
|
||||
_options = options;
|
||||
Options = options;
|
||||
|
||||
if (_options.Storage != null)
|
||||
if (Options.Storage != null)
|
||||
{
|
||||
_storageManager = new ManagedMqttClientStorageManager(_options.Storage);
|
||||
_storageManager = new ManagedMqttClientStorageManager(Options.Storage);
|
||||
var messages = await _storageManager.LoadQueuedMessagesAsync().ConfigureAwait(false);
|
||||
|
||||
foreach (var message in messages)
|
||||
{
|
||||
_messageQueue.Add(message);
|
||||
_messageQueue.Enqueue(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,31 +98,55 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
StopPublishing();
|
||||
StopMaintainingConnection();
|
||||
|
||||
while (_messageQueue.Any())
|
||||
{
|
||||
_messageQueue.Take();
|
||||
}
|
||||
_messageQueue.Clear();
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PublishAsync(MqttApplicationMessage applicationMessage)
|
||||
public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
return PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build());
|
||||
await PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build()).ConfigureAwait(false);
|
||||
return new MqttClientPublishResult();
|
||||
}
|
||||
|
||||
public async Task PublishAsync(ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
if (_storageManager != null)
|
||||
ManagedMqttApplicationMessage removedMessage = null;
|
||||
lock (_messageQueue)
|
||||
{
|
||||
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
|
||||
if (_messageQueue.Count >= Options.MaxPendingMessages)
|
||||
{
|
||||
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
|
||||
{
|
||||
_logger.Verbose("Skipping publish of new application message because internal queue is full.");
|
||||
ApplicationMessageSkipped?.Invoke(this, new ApplicationMessageSkippedEventArgs(applicationMessage));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage)
|
||||
{
|
||||
removedMessage = _messageQueue.RemoveFirst();
|
||||
_logger.Verbose("Removed oldest application message from internal queue because it is full.");
|
||||
ApplicationMessageSkipped?.Invoke(this, new ApplicationMessageSkippedEventArgs(removedMessage));
|
||||
}
|
||||
}
|
||||
|
||||
_messageQueue.Enqueue(applicationMessage);
|
||||
}
|
||||
|
||||
_messageQueue.Add(applicationMessage);
|
||||
if (_storageManager != null)
|
||||
{
|
||||
if (removedMessage != null)
|
||||
{
|
||||
await _storageManager.RemoveAsync(removedMessage).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
|
||||
@@ -157,7 +186,6 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_messageQueue?.Dispose();
|
||||
_connectionCancellationToken?.Dispose();
|
||||
_publishingCancellationToken?.Dispose();
|
||||
}
|
||||
@@ -180,7 +208,15 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
finally
|
||||
{
|
||||
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Error while disconnecting.");
|
||||
}
|
||||
|
||||
_logger.Info("Stopped");
|
||||
}
|
||||
}
|
||||
@@ -193,7 +229,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
if (connectionState == ReconnectionResult.NotConnected)
|
||||
{
|
||||
StopPublishing();
|
||||
await Task.Delay(_options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false);
|
||||
await Task.Delay(Options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -206,7 +242,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
if (connectionState == ReconnectionResult.StillConnected)
|
||||
{
|
||||
await Task.Delay(_options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
|
||||
await Task.Delay(Options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -228,7 +264,15 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested && _mqttClient.IsConnected)
|
||||
{
|
||||
var message = _messageQueue.Take(cancellationToken);
|
||||
//Peek at the message without dequeueing in order to prevent the
|
||||
//possibility of the queue growing beyond the configured cap.
|
||||
//Previously, messages could be re-enqueued if there was an
|
||||
//exception, and this re-enqueueing did not honor the cap.
|
||||
//Furthermore, because re-enqueueing would shuffle the order
|
||||
//of the messages, the DropOldestQueuedMessage strategy would
|
||||
//be unable to know which message is actually the oldest and would
|
||||
//instead drop the first item in the queue.
|
||||
var message = _messageQueue.PeekAndWait();
|
||||
if (message == null)
|
||||
{
|
||||
continue;
|
||||
@@ -258,6 +302,16 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
try
|
||||
{
|
||||
_mqttClient.PublishAsync(message.ApplicationMessage).GetAwaiter().GetResult();
|
||||
lock (_messageQueue) //lock to avoid conflict with this.PublishAsync
|
||||
{
|
||||
//While publishing this message, this.PublishAsync could have booted this
|
||||
//message off the queue to make room for another (when using a cap
|
||||
//with the DropOldestQueuedMessage strategy). If the first item
|
||||
//in the queue is equal to this message, then it's safe to remove
|
||||
//it from the queue. If not, that means this.PublishAsync has already
|
||||
//removed it, in which case we don't want to do anything.
|
||||
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
|
||||
}
|
||||
_storageManager?.RemoveAsync(message).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (MqttCommunicationException exception)
|
||||
@@ -266,9 +320,19 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
_logger.Warning(exception, $"Publishing application ({message.Id}) message failed.");
|
||||
|
||||
if (message.ApplicationMessage.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
||||
if (message.ApplicationMessage.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||
{
|
||||
_messageQueue.Add(message);
|
||||
//If QoS 0, we don't want this message to stay on the queue.
|
||||
//If QoS 1 or 2, it's possible that, when using a cap, this message
|
||||
//has been booted off the queue by this.PublishAsync, in which case this
|
||||
//thread will not continue to try to publish it. While this does
|
||||
//contradict the expected behavior of QoS 1 and 2, that's also true
|
||||
//for the usage of a message queue cap, so it's still consistent
|
||||
//with prior behavior in that way.
|
||||
lock (_messageQueue) //lock to avoid conflict with this.PublishAsync
|
||||
{
|
||||
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -291,7 +355,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
lock (_subscriptions)
|
||||
{
|
||||
subscriptions = _subscriptions.Select(i => new TopicFilter(i.Key, i.Value)).ToList();
|
||||
subscriptions = _subscriptions.Select(i => new TopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value }).ToList();
|
||||
|
||||
unsubscriptions = new HashSet<string>(_unsubscriptions);
|
||||
_unsubscriptions.Clear();
|
||||
@@ -334,7 +398,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
try
|
||||
{
|
||||
await _mqttClient.ConnectAsync(_options.ClientOptions).ConfigureAwait(false);
|
||||
await _mqttClient.ConnectAsync(Options.ClientOptions).ConfigureAwait(false);
|
||||
return ReconnectionResult.Reconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
@@ -12,5 +14,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
public TimeSpan ConnectionCheckInterval { get; set; } = TimeSpan.FromSeconds(1);
|
||||
|
||||
public IManagedMqttClientStorage Storage { get; set; }
|
||||
|
||||
public int MaxPendingMessages { get; set; } = int.MaxValue;
|
||||
|
||||
public MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; set; } = MqttPendingMessagesOverflowStrategy.DropNewMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
@@ -8,6 +10,18 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
private readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions();
|
||||
private MqttClientOptionsBuilder _clientOptionsBuilder;
|
||||
|
||||
public ManagedMqttClientOptionsBuilder WithMaxPendingMessages(int value)
|
||||
{
|
||||
_options.MaxPendingMessages = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ManagedMqttClientOptionsBuilder WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy value)
|
||||
{
|
||||
_options.PendingMessagesOverflowStrategy = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ManagedMqttClientOptionsBuilder WithAutoReconnectDelay(TimeSpan value)
|
||||
{
|
||||
_options.AutoReconnectDelay = value;
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public async Task AddAsync(ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
_messages.Add(applicationMessage);
|
||||
@@ -37,6 +39,8 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public async Task RemoveAsync(ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
var index = _messages.IndexOf(applicationMessage);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Serializer;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
@@ -10,8 +10,8 @@ namespace MQTTnet.Adapter
|
||||
{
|
||||
string Endpoint { get; }
|
||||
|
||||
IMqttPacketSerializer PacketSerializer { get; }
|
||||
|
||||
MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
|
||||
event EventHandler ReadingPacketStarted;
|
||||
|
||||
event EventHandler ReadingPacketCompleted;
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
public interface IMqttClientAdapterFactory
|
||||
{
|
||||
@@ -7,9 +7,9 @@ using System.Threading.Tasks;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Serializer;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
@@ -22,26 +22,27 @@ namespace MQTTnet.Adapter
|
||||
|
||||
private readonly IMqttNetChildLogger _logger;
|
||||
private readonly IMqttChannel _channel;
|
||||
private readonly MqttPacketReader _packetReader;
|
||||
|
||||
private readonly byte[] _fixedHeaderBuffer = new byte[2];
|
||||
|
||||
private readonly byte[] _singleByteBuffer = new byte[1];
|
||||
|
||||
|
||||
private bool _isDisposed;
|
||||
|
||||
public MqttChannelAdapter(IMqttChannel channel, IMqttPacketSerializer serializer, IMqttNetChildLogger logger)
|
||||
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetChildLogger logger)
|
||||
{
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
|
||||
PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
|
||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||
|
||||
_packetReader = new MqttPacketReader(_channel);
|
||||
|
||||
_logger = logger.CreateChildLogger(nameof(MqttChannelAdapter));
|
||||
}
|
||||
|
||||
public string Endpoint => _channel.Endpoint;
|
||||
|
||||
public IMqttPacketSerializer PacketSerializer { get; }
|
||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
|
||||
public event EventHandler ReadingPacketStarted;
|
||||
public event EventHandler ReadingPacketCompleted;
|
||||
@@ -91,17 +92,17 @@ namespace MQTTnet.Adapter
|
||||
WrapException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var packetData = PacketSerializer.Serialize(packet);
|
||||
var packetData = PacketFormatterAdapter.Encode(packet);
|
||||
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
|
||||
PacketSerializer.FreeBuffer();
|
||||
PacketFormatterAdapter.FreeBuffer();
|
||||
|
||||
_logger.Verbose("TX >>> {0}", packet);
|
||||
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -140,13 +141,18 @@ namespace MQTTnet.Adapter
|
||||
return null;
|
||||
}
|
||||
|
||||
var packet = PacketSerializer.Deserialize(receivedMqttPacket);
|
||||
if (!PacketFormatterAdapter.ProtocolVersion.HasValue)
|
||||
{
|
||||
PacketFormatterAdapter.DetectProtocolVersion(receivedMqttPacket);
|
||||
}
|
||||
|
||||
var packet = PacketFormatterAdapter.Decode(receivedMqttPacket);
|
||||
if (packet == null)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Received malformed packet.");
|
||||
}
|
||||
|
||||
_logger.Verbose("RX <<< {0}", packet);
|
||||
_logger.Verbose("RX ({0} bytes) <<< {1}", receivedMqttPacket.TotalLength, packet);
|
||||
|
||||
return packet;
|
||||
}
|
||||
@@ -168,7 +174,7 @@ namespace MQTTnet.Adapter
|
||||
|
||||
private async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var fixedHeader = await MqttPacketReader.ReadFixedHeaderAsync(_channel, _fixedHeaderBuffer, _singleByteBuffer, cancellationToken).ConfigureAwait(false);
|
||||
var fixedHeader = await _packetReader.ReadFixedHeaderAsync(_fixedHeaderBuffer, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -176,7 +182,7 @@ namespace MQTTnet.Adapter
|
||||
|
||||
if (fixedHeader.RemainingLength == 0)
|
||||
{
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, null);
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, null, 2);
|
||||
}
|
||||
|
||||
var body = new byte[fixedHeader.RemainingLength];
|
||||
@@ -192,9 +198,9 @@ namespace MQTTnet.Adapter
|
||||
}
|
||||
|
||||
#if WINDOWS_UWP
|
||||
var readBytes = await _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false);
|
||||
var readBytes = await _channel.ReadAsync(body, bodyOffset, (int)chunkSize, cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
// async/await is not used to avoid the overhead of context switches. We assume that the reamining data
|
||||
// async/await is not used to avoid the overhead of context switches. We assume that the remaining data
|
||||
// has been sent from the sender directly after the initial bytes.
|
||||
var readBytes = _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
#endif
|
||||
@@ -205,7 +211,8 @@ namespace MQTTnet.Adapter
|
||||
bodyOffset += readBytes;
|
||||
} while (bodyOffset < body.Length);
|
||||
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, new MqttPacketBodyReader(body, 0, body.Length));
|
||||
var bodyReader = new MqttPacketBodyReader(body, 0, body.Length);
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, bodyReader, fixedHeader.TotalLength);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
public class MqttConnectingFailedException : MqttCommunicationException
|
||||
{
|
||||
public MqttConnectingFailedException(MqttConnectReturnCode returnCode)
|
||||
: base($"Connecting with MQTT server failed ({returnCode}).")
|
||||
public MqttConnectingFailedException(MqttClientConnectResultCode resultCode)
|
||||
: base($"Connecting with MQTT server failed ({resultCode.ToString()}).")
|
||||
{
|
||||
ReturnCode = returnCode;
|
||||
ResultCode = resultCode;
|
||||
}
|
||||
|
||||
public MqttConnectReturnCode ReturnCode { get; }
|
||||
public MqttClientConnectResultCode ResultCode { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
public class ReceivedMqttPacket
|
||||
{
|
||||
public ReceivedMqttPacket(byte fixedHeader, MqttPacketBodyReader body)
|
||||
public ReceivedMqttPacket(byte fixedHeader, MqttPacketBodyReader body, int totalLength)
|
||||
{
|
||||
FixedHeader = fixedHeader;
|
||||
Body = body;
|
||||
TotalLength = totalLength;
|
||||
}
|
||||
|
||||
public byte FixedHeader { get; }
|
||||
|
||||
public MqttPacketBodyReader Body { get; }
|
||||
|
||||
public int TotalLength { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet
|
||||
@@ -29,7 +30,7 @@ namespace MQTTnet
|
||||
}
|
||||
}
|
||||
|
||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic)
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IApplicationMessagePublisher publisher, string topic)
|
||||
{
|
||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
@@ -38,7 +39,7 @@ namespace MQTTnet
|
||||
.WithTopic(topic));
|
||||
}
|
||||
|
||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload)
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload)
|
||||
{
|
||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
@@ -48,7 +49,7 @@ namespace MQTTnet
|
||||
.WithPayload(payload));
|
||||
}
|
||||
|
||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
@@ -59,7 +60,7 @@ namespace MQTTnet
|
||||
.WithQualityOfServiceLevel(qualityOfServiceLevel));
|
||||
}
|
||||
|
||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
|
||||
{
|
||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
@@ -71,7 +72,7 @@ namespace MQTTnet
|
||||
.WithRetainFlag(retain));
|
||||
}
|
||||
|
||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder)
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IApplicationMessagePublisher publisher, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder)
|
||||
{
|
||||
var message = builder(new MqttApplicationMessageBuilder()).Build();
|
||||
return publisher.PublishAsync(message);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace MQTTnet.Client.Connecting
|
||||
{
|
||||
public class MqttClientConnectResult
|
||||
{
|
||||
public bool IsSessionPresent { get; set; }
|
||||
|
||||
public MqttClientConnectResultCode ResultCode { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace MQTTnet.Client.Connecting
|
||||
{
|
||||
public enum MqttClientConnectResultCode
|
||||
{
|
||||
Success = 0,
|
||||
UnspecifiedError = 128,
|
||||
MalformedPacket = 129,
|
||||
ProtocolError = 130,
|
||||
ImplementationSpecificError = 131,
|
||||
UnsupportedProtocolVersion = 132,
|
||||
ClientIdentifierNotValid = 133,
|
||||
BadUserNameOrPassword = 134,
|
||||
NotAuthorized = 135,
|
||||
ServerUnavailable = 136,
|
||||
ServerBusy = 137,
|
||||
Banned = 138,
|
||||
BadAuthenticationMethod = 140,
|
||||
TopicNameInvalid = 144,
|
||||
PacketTooLarge = 149,
|
||||
QuotaExceeded = 151,
|
||||
PayloadFormatInvalid = 153,
|
||||
RetainNotSupported = 154,
|
||||
QoSNotSupported = 155,
|
||||
UseAnotherServer = 156,
|
||||
ServerMoved = 157,
|
||||
ConnectionRateExceeded = 159
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Client.Connecting
|
||||
{
|
||||
public class MqttClientConnectedEventArgs : EventArgs
|
||||
{
|
||||
public MqttClientConnectedEventArgs(MqttClientConnectResult connectResult)
|
||||
{
|
||||
ConnectResult = connectResult ?? throw new ArgumentNullException(nameof(connectResult));
|
||||
}
|
||||
|
||||
public MqttClientConnectResult ConnectResult { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace MQTTnet.Client.Disconnecting
|
||||
{
|
||||
public class MqttClientDisconnectOptions
|
||||
{
|
||||
public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection;
|
||||
|
||||
public string ReasonString { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace MQTTnet.Client.Disconnecting
|
||||
{
|
||||
public enum MqttClientDisconnectReason
|
||||
{
|
||||
NormalDisconnection = 0,
|
||||
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Disconnecting
|
||||
{
|
||||
public class MqttClientDisconnectedEventArgs : EventArgs
|
||||
{
|
||||
@@ -1,20 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Client.Subscribing;
|
||||
using MQTTnet.Client.Unsubscribing;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public interface IMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
|
||||
{
|
||||
bool IsConnected { get; }
|
||||
IMqttClientOptions Options { get; }
|
||||
|
||||
event EventHandler<MqttClientConnectedEventArgs> Connected;
|
||||
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
||||
|
||||
Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options);
|
||||
Task DisconnectAsync();
|
||||
Task DisconnectAsync(MqttClientDisconnectOptions options);
|
||||
|
||||
Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
|
||||
Task UnsubscribeAsync(IEnumerable<string> topics);
|
||||
Task<MqttClientSubscribeResult> SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
|
||||
Task<MqttClientUnsubscribeResult> UnsubscribeAsync(IEnumerable<string> topics);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
|
||||
@@ -5,9 +5,16 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Client.PacketDispatcher;
|
||||
using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Client.Subscribing;
|
||||
using MQTTnet.Client.Unsubscribing;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
@@ -23,13 +30,12 @@ namespace MQTTnet.Client
|
||||
private readonly IMqttClientAdapterFactory _adapterFactory;
|
||||
private readonly IMqttNetChildLogger _logger;
|
||||
|
||||
private IMqttClientOptions _options;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
internal Task _packetReceiverTask;
|
||||
internal Task _keepAliveMessageSenderTask;
|
||||
private IMqttChannelAdapter _adapter;
|
||||
private bool _cleanDisconnectInitiated;
|
||||
private int _disconnectGate;
|
||||
private long _disconnectGate;
|
||||
|
||||
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
|
||||
{
|
||||
@@ -44,6 +50,7 @@ namespace MQTTnet.Client
|
||||
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||
|
||||
public bool IsConnected { get; private set; }
|
||||
public IMqttClientOptions Options { get; private set; }
|
||||
|
||||
public async Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options)
|
||||
{
|
||||
@@ -54,7 +61,8 @@ namespace MQTTnet.Client
|
||||
|
||||
try
|
||||
{
|
||||
_options = options;
|
||||
Options = options;
|
||||
|
||||
_packetIdentifierProvider.Reset();
|
||||
_packetDispatcher.Reset();
|
||||
|
||||
@@ -62,27 +70,28 @@ namespace MQTTnet.Client
|
||||
_disconnectGate = 0;
|
||||
_adapter = _adapterFactory.CreateClientAdapter(options, _logger);
|
||||
|
||||
_logger.Verbose($"Trying to connect with server ({_options.ChannelOptions}).");
|
||||
await _adapter.ConnectAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
_logger.Verbose($"Trying to connect with server ({Options.ChannelOptions}).");
|
||||
await _adapter.ConnectAsync(Options.CommunicationTimeout, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
_logger.Verbose("Connection with server established.");
|
||||
|
||||
StartReceivingPackets(_cancellationTokenSource.Token);
|
||||
|
||||
var connectResponse = await AuthenticateAsync(options.WillMessage, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
var connectResult = await AuthenticateAsync(options.WillMessage, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
_logger.Verbose("MQTT connection with server established.");
|
||||
|
||||
_sendTracker.Restart();
|
||||
|
||||
if (_options.KeepAlivePeriod != TimeSpan.Zero)
|
||||
if (Options.KeepAlivePeriod != TimeSpan.Zero)
|
||||
{
|
||||
StartSendingKeepAliveMessages(_cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
IsConnected = true;
|
||||
Connected?.Invoke(this, new MqttClientConnectedEventArgs(connectResponse.IsSessionPresent));
|
||||
Connected?.Invoke(this, new MqttClientConnectedEventArgs(connectResult));
|
||||
|
||||
_logger.Info("Connected.");
|
||||
return new MqttClientConnectResult(connectResponse.IsSessionPresent);
|
||||
|
||||
return connectResult;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -97,7 +106,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync()
|
||||
public async Task DisconnectAsync(MqttClientDisconnectOptions options)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -105,7 +114,8 @@ namespace MQTTnet.Client
|
||||
|
||||
if (IsConnected && !_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
await SendAsync(new MqttDisconnectPacket(), _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
var disconnectPacket = CreateDisconnectPacket(options);
|
||||
await SendAsync(disconnectPacket, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -117,7 +127,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
|
||||
public async Task<MqttClientSubscribeResult> SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
|
||||
{
|
||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||
|
||||
@@ -125,21 +135,16 @@ namespace MQTTnet.Client
|
||||
|
||||
var subscribePacket = new MqttSubscribePacket
|
||||
{
|
||||
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(),
|
||||
TopicFilters = topicFilters.ToList()
|
||||
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier()
|
||||
};
|
||||
|
||||
var response = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
subscribePacket.TopicFilters.AddRange(topicFilters);
|
||||
|
||||
if (response.SubscribeReturnCodes.Count != subscribePacket.TopicFilters.Count)
|
||||
{
|
||||
throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1].");
|
||||
}
|
||||
|
||||
return subscribePacket.TopicFilters.Select((t, i) => new MqttSubscribeResult(t, response.SubscribeReturnCodes[i])).ToList();
|
||||
var subAckPacket = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientSubscribeResult(subscribePacket, subAckPacket);
|
||||
}
|
||||
|
||||
public async Task UnsubscribeAsync(IEnumerable<string> topicFilters)
|
||||
public async Task<MqttClientUnsubscribeResult> UnsubscribeAsync(IEnumerable<string> topicFilters)
|
||||
{
|
||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||
|
||||
@@ -147,34 +152,47 @@ namespace MQTTnet.Client
|
||||
|
||||
var unsubscribePacket = new MqttUnsubscribePacket
|
||||
{
|
||||
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(),
|
||||
TopicFilters = topicFilters.ToList()
|
||||
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier()
|
||||
};
|
||||
|
||||
await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
unsubscribePacket.TopicFilters.AddRange(topicFilters);
|
||||
|
||||
var unsubAckPacket = await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientUnsubscribeResult(unsubscribePacket, unsubAckPacket);
|
||||
}
|
||||
|
||||
public Task PublishAsync(MqttApplicationMessage applicationMessage)
|
||||
public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
ThrowIfNotConnected();
|
||||
|
||||
var publishPacket = applicationMessage.ToPublishPacket();
|
||||
var publishPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePublishPacket(applicationMessage);
|
||||
|
||||
switch (applicationMessage.QualityOfServiceLevel)
|
||||
{
|
||||
case MqttQualityOfServiceLevel.AtMostOnce:
|
||||
{
|
||||
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
|
||||
return SendAsync(publishPacket, _cancellationTokenSource.Token);
|
||||
await SendAsync(publishPacket, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
return new MqttClientPublishResult();
|
||||
}
|
||||
case MqttQualityOfServiceLevel.AtLeastOnce:
|
||||
{
|
||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier();
|
||||
return SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, _cancellationTokenSource.Token);
|
||||
var response = await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
|
||||
var result = new MqttClientPublishResult();
|
||||
if (response.ReasonCode != null)
|
||||
{
|
||||
result.ReasonCode = (MqttClientPublishReasonCode)response.ReasonCode;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
case MqttQualityOfServiceLevel.ExactlyOnce:
|
||||
{
|
||||
return PublishExactlyOnce(publishPacket, _cancellationTokenSource.Token);
|
||||
return await PublishExactlyOnceAsync(publishPacket, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
default:
|
||||
{
|
||||
@@ -193,30 +211,27 @@ namespace MQTTnet.Client
|
||||
_adapter = null;
|
||||
}
|
||||
|
||||
private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
|
||||
private async Task<MqttClientConnectResult> AuthenticateAsync(MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
|
||||
{
|
||||
var connectPacket = new MqttConnectPacket
|
||||
{
|
||||
ClientId = _options.ClientId,
|
||||
Username = _options.Credentials?.Username,
|
||||
Password = _options.Credentials?.Password,
|
||||
CleanSession = _options.CleanSession,
|
||||
KeepAlivePeriod = (ushort)_options.KeepAlivePeriod.TotalSeconds,
|
||||
WillMessage = willApplicationMessage
|
||||
};
|
||||
var connectPacket = _adapter.PacketFormatterAdapter.DataConverter.CreateConnectPacket(
|
||||
willApplicationMessage,
|
||||
Options);
|
||||
|
||||
var response = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
|
||||
if (response.ConnectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
||||
var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var result = _adapter.PacketFormatterAdapter.DataConverter.CreateClientConnectResult(connAckPacket);
|
||||
|
||||
if (result.ResultCode != MqttClientConnectResultCode.Success)
|
||||
{
|
||||
throw new MqttConnectingFailedException(response.ConnectReturnCode);
|
||||
throw new MqttConnectingFailedException(result.ResultCode);
|
||||
}
|
||||
|
||||
return response;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void ThrowIfNotConnected()
|
||||
{
|
||||
if (!IsConnected) throw new MqttCommunicationException("The client is not connected.");
|
||||
if (!IsConnected || Interlocked.Read(ref _disconnectGate) == 1) throw new MqttCommunicationException("The client is not connected.");
|
||||
}
|
||||
|
||||
private void ThrowIfConnected(string message)
|
||||
@@ -229,19 +244,19 @@ namespace MQTTnet.Client
|
||||
var clientWasConnected = IsConnected;
|
||||
|
||||
InitiateDisconnect();
|
||||
|
||||
|
||||
IsConnected = false;
|
||||
|
||||
try
|
||||
{
|
||||
await WaitForTaskAsync(_packetReceiverTask, sender).ConfigureAwait(false);
|
||||
await WaitForTaskAsync(_keepAliveMessageSenderTask, sender).ConfigureAwait(false);
|
||||
|
||||
if (_adapter != null)
|
||||
{
|
||||
await _adapter.DisconnectAsync(_options.CommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
|
||||
await _adapter.DisconnectAsync(Options.CommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await WaitForTaskAsync(_packetReceiverTask, sender).ConfigureAwait(false);
|
||||
await WaitForTaskAsync(_keepAliveMessageSenderTask, sender).ConfigureAwait(false);
|
||||
|
||||
_logger.Verbose("Disconnected from adapter.");
|
||||
}
|
||||
catch (Exception adapterException)
|
||||
@@ -303,9 +318,9 @@ namespace MQTTnet.Client
|
||||
try
|
||||
{
|
||||
await _adapter.SendPacketAsync(requestPacket, cancellationToken).ConfigureAwait(false);
|
||||
var respone = await Internal.TaskExtensions.TimeoutAfterAsync(ct => packetAwaiter.Task, _options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
|
||||
return await packetAwaiter.WaitOneAsync(Options.CommunicationTimeout);
|
||||
|
||||
return (TResponsePacket)respone;
|
||||
//return (TResponsePacket)await Internal.TaskExtensions.TimeoutAfterAsync(ct => packetAwaiter.Task, Options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (MqttCommunicationTimedOutException)
|
||||
{
|
||||
@@ -320,16 +335,16 @@ namespace MQTTnet.Client
|
||||
|
||||
private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.Verbose("Start sending keep alive packets.");
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Verbose("Start sending keep alive packets.");
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var keepAliveSendInterval = TimeSpan.FromSeconds(_options.KeepAlivePeriod.TotalSeconds * 0.75);
|
||||
if (_options.KeepAliveSendInterval.HasValue)
|
||||
var keepAliveSendInterval = TimeSpan.FromSeconds(Options.KeepAlivePeriod.TotalSeconds * 0.75);
|
||||
if (Options.KeepAliveSendInterval.HasValue)
|
||||
{
|
||||
keepAliveSendInterval = _options.KeepAliveSendInterval.Value;
|
||||
keepAliveSendInterval = Options.KeepAliveSendInterval.Value;
|
||||
}
|
||||
|
||||
var waitTime = keepAliveSendInterval - _sendTracker.Elapsed;
|
||||
@@ -374,14 +389,13 @@ namespace MQTTnet.Client
|
||||
|
||||
private async Task ReceivePacketsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.Verbose("Start receiving packets.");
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Verbose("Start receiving packets.");
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (packet != null && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -435,7 +449,7 @@ namespace MQTTnet.Client
|
||||
|
||||
if (packet is MqttDisconnectPacket)
|
||||
{
|
||||
return DisconnectAsync();
|
||||
return DisconnectAsync(null);
|
||||
}
|
||||
|
||||
if (packet is MqttPubRelPacket pubRelPacket)
|
||||
@@ -458,14 +472,14 @@ namespace MQTTnet.Client
|
||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
{
|
||||
FireApplicationMessageReceivedEvent(publishPacket);
|
||||
return SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }, cancellationToken);
|
||||
return SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier, ReasonCode = MqttPubAckReasonCode.Success }, cancellationToken);
|
||||
}
|
||||
|
||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
||||
{
|
||||
// QoS 2 is implement as method "B" (4.3.3 QoS 2: Exactly once delivery)
|
||||
FireApplicationMessageReceivedEvent(publishPacket);
|
||||
return SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }, cancellationToken);
|
||||
return SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier, ReasonCode = MqttPubRecReasonCode.Success }, cancellationToken);
|
||||
}
|
||||
|
||||
throw new MqttCommunicationException("Received a not supported QoS level.");
|
||||
@@ -475,23 +489,33 @@ namespace MQTTnet.Client
|
||||
{
|
||||
var response = new MqttPubCompPacket
|
||||
{
|
||||
PacketIdentifier = pubRelPacket.PacketIdentifier
|
||||
PacketIdentifier = pubRelPacket.PacketIdentifier,
|
||||
ReasonCode = MqttPubCompReasonCode.Success
|
||||
};
|
||||
|
||||
return SendAsync(response, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task PublishExactlyOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
private async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier();
|
||||
|
||||
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
var pubRelPacket = new MqttPubRelPacket
|
||||
{
|
||||
PacketIdentifier = pubRecPacket.PacketIdentifier
|
||||
PacketIdentifier = pubRecPacket.PacketIdentifier,
|
||||
ReasonCode = MqttPubRelReasonCode.Success
|
||||
};
|
||||
|
||||
await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
|
||||
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
|
||||
var result = new MqttClientPublishResult();
|
||||
|
||||
if (pubRecPacket.ReasonCode != null)
|
||||
{
|
||||
result.ReasonCode = (MqttClientPublishReasonCode)pubRecPacket.ReasonCode;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void StartReceivingPackets(CancellationToken cancellationToken)
|
||||
@@ -499,8 +523,8 @@ namespace MQTTnet.Client
|
||||
_packetReceiverTask = Task.Factory.StartNew(
|
||||
() => ReceivePacketsAsync(cancellationToken),
|
||||
cancellationToken,
|
||||
TaskCreationOptions.LongRunning,
|
||||
TaskScheduler.Default);
|
||||
TaskCreationOptions.LongRunning,
|
||||
TaskScheduler.Default).Unwrap();
|
||||
}
|
||||
|
||||
private void StartSendingKeepAliveMessages(CancellationToken cancellationToken)
|
||||
@@ -509,15 +533,15 @@ namespace MQTTnet.Client
|
||||
() => SendKeepAliveMessagesAsync(cancellationToken),
|
||||
cancellationToken,
|
||||
TaskCreationOptions.LongRunning,
|
||||
TaskScheduler.Default);
|
||||
TaskScheduler.Default).Unwrap();
|
||||
}
|
||||
|
||||
private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket)
|
||||
{
|
||||
try
|
||||
{
|
||||
var applicationMessage = publishPacket.ToApplicationMessage();
|
||||
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(_options.ClientId, applicationMessage));
|
||||
var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
|
||||
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -550,5 +574,31 @@ namespace MQTTnet.Client
|
||||
{
|
||||
return Interlocked.CompareExchange(ref _disconnectGate, 1, 0) != 0;
|
||||
}
|
||||
|
||||
private MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options)
|
||||
{
|
||||
var packet = new MqttDisconnectPacket();
|
||||
|
||||
if (_adapter.PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.V500)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
packet.ReasonCode = MqttDisconnectReasonCode.NormalDisconnection;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet.ReasonCode = (MqttDisconnectReasonCode)options.ReasonCode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (options != null)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Reason codes for disconnect are only supported for MQTTv5.");
|
||||
}
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientConnectResult
|
||||
{
|
||||
public MqttClientConnectResult(bool isSessionPresent)
|
||||
{
|
||||
IsSessionPresent = isSessionPresent;
|
||||
}
|
||||
|
||||
public bool IsSessionPresent { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientConnectedEventArgs : EventArgs
|
||||
{
|
||||
public MqttClientConnectedEventArgs(bool isSessionPresent)
|
||||
{
|
||||
IsSessionPresent = isSessionPresent;
|
||||
}
|
||||
|
||||
public bool IsSessionPresent { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client.Subscribing;
|
||||
using MQTTnet.Client.Unsubscribing;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public static class MqttClientExtensions
|
||||
{
|
||||
public static Task<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, params TopicFilter[] topicFilters)
|
||||
public static Task DisconnectAsync(this IMqttClient client)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return client.DisconnectAsync(null);
|
||||
}
|
||||
|
||||
public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, params TopicFilter[] topicFilters)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||
@@ -16,7 +24,7 @@ namespace MQTTnet.Client
|
||||
return client.SubscribeAsync(topicFilters.ToList());
|
||||
}
|
||||
|
||||
public static Task<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
@@ -24,7 +32,7 @@ namespace MQTTnet.Client
|
||||
return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
|
||||
}
|
||||
|
||||
public static Task<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, string topic)
|
||||
public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, string topic)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
@@ -32,7 +40,7 @@ namespace MQTTnet.Client
|
||||
return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build());
|
||||
}
|
||||
|
||||
public static Task UnsubscribeAsync(this IMqttClient client, params string[] topicFilters)
|
||||
public static Task<MqttClientUnsubscribeResult> UnsubscribeAsync(this IMqttClient client, params string[] topicFilters)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttSubscribeResult
|
||||
{
|
||||
public MqttSubscribeResult(TopicFilter topicFilter, MqttSubscribeReturnCode returnCode)
|
||||
{
|
||||
TopicFilter = topicFilter;
|
||||
ReturnCode = returnCode;
|
||||
}
|
||||
|
||||
public TopicFilter TopicFilter { get; }
|
||||
|
||||
public MqttSubscribeReturnCode ReturnCode { get; }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public interface IMqttClientChannelOptions
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public interface IMqttClientCredentials
|
||||
{
|
||||
+12
-2
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public interface IMqttClientOptions
|
||||
{
|
||||
@@ -16,5 +16,15 @@ namespace MQTTnet.Client
|
||||
TimeSpan? KeepAliveSendInterval { get; }
|
||||
|
||||
MqttApplicationMessage WillMessage { get; }
|
||||
uint? WillDelayInterval { get; }
|
||||
|
||||
string AuthenticationMethod { get; }
|
||||
byte[] AuthenticationData { get; }
|
||||
uint? MaximumPacketSize { get; }
|
||||
ushort? ReceiveMaximum { get; }
|
||||
bool? RequestProblemInformation { get; }
|
||||
bool? RequestResponseInformation { get; }
|
||||
uint? SessionExpiryInterval { get; }
|
||||
ushort? TopicAliasMaximum { get; }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientCredentials : IMqttClientCredentials
|
||||
{
|
||||
+14
-3
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientOptions : IMqttClientOptions
|
||||
{
|
||||
@@ -9,12 +9,23 @@ namespace MQTTnet.Client
|
||||
public bool CleanSession { get; set; } = true;
|
||||
public IMqttClientCredentials Credentials { get; set; } = new MqttClientCredentials();
|
||||
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
|
||||
public IMqttClientChannelOptions ChannelOptions { get; set; }
|
||||
|
||||
public IMqttClientChannelOptions ChannelOptions { get; set; }
|
||||
public TimeSpan CommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
||||
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(15);
|
||||
public TimeSpan? KeepAliveSendInterval { get; set; }
|
||||
|
||||
public MqttApplicationMessage WillMessage { get; set; }
|
||||
public uint? WillDelayInterval { get; set; }
|
||||
|
||||
public string AuthenticationMethod { get; set; }
|
||||
public byte[] AuthenticationData { get; set; }
|
||||
|
||||
public uint? MaximumPacketSize { get; set; }
|
||||
public ushort? ReceiveMaximum { get; set; }
|
||||
public bool? RequestProblemInformation { get; set; }
|
||||
public bool? RequestResponseInformation { get; set; }
|
||||
public uint? SessionExpiryInterval { get; set; }
|
||||
public ushort? TopicAliasMaximum { get; set; }
|
||||
}
|
||||
}
|
||||
+55
-4
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientOptionsBuilder
|
||||
{
|
||||
@@ -55,6 +55,55 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithAuthentication(string method, byte[] data)
|
||||
{
|
||||
_options.AuthenticationMethod = method;
|
||||
_options.AuthenticationData = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillDelayInterval(uint? willDelayInterval)
|
||||
{
|
||||
_options.WillDelayInterval = willDelayInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTopicAliasMaximum(ushort? topicAliasMaximum)
|
||||
{
|
||||
_options.TopicAliasMaximum = topicAliasMaximum;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithMaximumPacketSize(uint? maximumPacketSize)
|
||||
{
|
||||
_options.MaximumPacketSize = maximumPacketSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithReceiveMaximum(ushort? receiveMaximum)
|
||||
{
|
||||
_options.ReceiveMaximum = receiveMaximum;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithRequestProblemInformation(bool? requestProblemInformation = true)
|
||||
{
|
||||
_options.RequestProblemInformation = requestProblemInformation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithRequestResponseInformation(bool? requestResponseInformation = true)
|
||||
{
|
||||
_options.RequestResponseInformation = requestResponseInformation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithSessionExpiryInterval(uint? sessionExpiryInterval)
|
||||
{
|
||||
_options.SessionExpiryInterval = sessionExpiryInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCredentials(string username, string password = null)
|
||||
{
|
||||
_options.Credentials = new MqttClientCredentials
|
||||
@@ -92,11 +141,13 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWebSocketServer(string uri)
|
||||
public MqttClientOptionsBuilder WithWebSocketServer(string uri, MqttClientOptionsBuilderWebSocketParameters parameters = null)
|
||||
{
|
||||
_webSocketOptions = new MqttClientWebSocketOptions
|
||||
{
|
||||
Uri = uri
|
||||
Uri = uri,
|
||||
RequestHeaders = parameters?.RequestHeaders,
|
||||
CookieContainer = parameters?.CookieContainer
|
||||
};
|
||||
|
||||
return this;
|
||||
+1
-1
@@ -4,7 +4,7 @@ using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientOptionsBuilderTlsParameters
|
||||
{
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientOptionsBuilderWebSocketParameters
|
||||
{
|
||||
public IDictionary<string, string> RequestHeaders { get; set; }
|
||||
|
||||
public CookieContainer CookieContainer { get; set; }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientTcpOptions : IMqttClientChannelOptions
|
||||
{
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public static class MqttClientTcpOptionsExtensions
|
||||
{
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientTlsOptions
|
||||
{
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientWebSocketOptions : IMqttClientChannelOptions
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientWebSocketProxyOptions
|
||||
{
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client.PacketDispatcher
|
||||
{
|
||||
public interface IMqttPacketAwaiter
|
||||
{
|
||||
void Complete(MqttBasePacket packet);
|
||||
|
||||
void Fail(Exception exception);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client.PacketDispatcher
|
||||
{
|
||||
public sealed class MqttPacketAwaiter<TPacket> : IMqttPacketAwaiter where TPacket : MqttBasePacket
|
||||
{
|
||||
private readonly TaskCompletionSource<MqttBasePacket> _packet = new TaskCompletionSource<MqttBasePacket>();
|
||||
|
||||
public async Task<TPacket> WaitOneAsync(TimeSpan timeout)
|
||||
{
|
||||
using (var timeoutToken = new CancellationTokenSource(timeout))
|
||||
{
|
||||
timeoutToken.Token.Register(() => _packet.TrySetCanceled());
|
||||
|
||||
var packet = await _packet.Task.ConfigureAwait(false);
|
||||
return (TPacket)packet;
|
||||
}
|
||||
}
|
||||
|
||||
public void Complete(MqttBasePacket packet)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
|
||||
_packet.TrySetResult(packet);
|
||||
}
|
||||
|
||||
public void Fail(Exception exception)
|
||||
{
|
||||
if (exception == null) throw new ArgumentNullException(nameof(exception));
|
||||
|
||||
_packet.TrySetException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-12
@@ -3,17 +3,17 @@ using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.PacketDispatcher
|
||||
{
|
||||
public class MqttPacketDispatcher
|
||||
{
|
||||
private readonly ConcurrentDictionary<Tuple<ushort, Type>, TaskCompletionSource<MqttBasePacket>> _awaiters = new ConcurrentDictionary<Tuple<ushort, Type>, TaskCompletionSource<MqttBasePacket>>();
|
||||
|
||||
private readonly ConcurrentDictionary<Tuple<ushort, Type>, IMqttPacketAwaiter> _awaiters = new ConcurrentDictionary<Tuple<ushort, Type>, IMqttPacketAwaiter>();
|
||||
|
||||
public void Dispatch(Exception exception)
|
||||
{
|
||||
foreach (var awaiter in _awaiters)
|
||||
{
|
||||
Task.Run(() => awaiter.Value.TrySetException(exception)); // Task.Run fixes a dead lock. Without this the client only receives one message.
|
||||
Task.Run(() => awaiter.Value.Fail(exception)); // Task.Run fixes a dead lock. Without this the client only receives one message.
|
||||
}
|
||||
|
||||
_awaiters.Clear();
|
||||
@@ -31,10 +31,10 @@ namespace MQTTnet.Client
|
||||
|
||||
var type = packet.GetType();
|
||||
var key = new Tuple<ushort, Type>(identifier, type);
|
||||
|
||||
|
||||
if (_awaiters.TryRemove(key, out var awaiter))
|
||||
{
|
||||
Task.Run(() => awaiter.TrySetResult(packet)); // Task.Run fixes a dead lock. Without this the client only receives one message.
|
||||
Task.Run(() => awaiter.Complete(packet)); // Task.Run fixes a dead lock. Without this the client only receives one message.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,22 +46,22 @@ namespace MQTTnet.Client
|
||||
_awaiters.Clear();
|
||||
}
|
||||
|
||||
public TaskCompletionSource<MqttBasePacket> AddPacketAwaiter<TResponsePacket>(ushort? identifier) where TResponsePacket : MqttBasePacket
|
||||
public MqttPacketAwaiter<TResponsePacket> AddPacketAwaiter<TResponsePacket>(ushort? identifier) where TResponsePacket : MqttBasePacket
|
||||
{
|
||||
var tcs = new TaskCompletionSource<MqttBasePacket>();
|
||||
var awaiter = new MqttPacketAwaiter<TResponsePacket>();
|
||||
|
||||
if (!identifier.HasValue)
|
||||
{
|
||||
identifier = 0;
|
||||
}
|
||||
|
||||
var key = new Tuple<ushort, Type>(identifier ?? 0, typeof(TResponsePacket));
|
||||
if (!_awaiters.TryAdd(key, tcs))
|
||||
|
||||
var key = new Tuple<ushort, Type>(identifier.Value, typeof(TResponsePacket));
|
||||
if (!_awaiters.TryAdd(key, awaiter))
|
||||
{
|
||||
throw new InvalidOperationException($"The packet dispatcher already has an awaiter for packet of type '{key.Item2.Name}' with identifier {key.Item1}.");
|
||||
}
|
||||
|
||||
return tcs;
|
||||
return awaiter;
|
||||
}
|
||||
|
||||
public void RemovePacketAwaiter<TResponsePacket>(ushort? identifier) where TResponsePacket : MqttBasePacket
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace MQTTnet.Client.Publishing
|
||||
{
|
||||
public enum MqttClientPublishReasonCode
|
||||
{
|
||||
Success = 0,
|
||||
NoMatchingSubscribers = 16,
|
||||
UnspecifiedError = 128,
|
||||
ImplementationSpecificError = 131,
|
||||
NotAuthorized = 135,
|
||||
TopicNameInvalid = 144,
|
||||
PacketIdentifierInUse = 145,
|
||||
QuotaExceeded = 151,
|
||||
PayloadFormatInvalid = 153
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
namespace MQTTnet.Client.Publishing
|
||||
{
|
||||
public class MqttClientPublishResult
|
||||
{
|
||||
public MqttClientPublishReasonCode ReasonCode { get; set; } = MqttClientPublishReasonCode.Success;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.Client.Subscribing
|
||||
{
|
||||
public class MqttClientSubscribeResult
|
||||
{
|
||||
public List<MqttClientSubscribeResultItem> Items { get; } = new List<MqttClientSubscribeResultItem>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace MQTTnet.Client.Subscribing
|
||||
{
|
||||
public enum MqttClientSubscribeResultCode
|
||||
{
|
||||
GrantedQoS0 = 0,
|
||||
GrantedQoS1 = 1,
|
||||
GrantedQoS2 = 2,
|
||||
UnspecifiedError = 128,
|
||||
ImplementationSpecificError = 131,
|
||||
NotAuthorized = 135,
|
||||
TopicFilterInvalid = 143,
|
||||
PacketIdentifierInUse = 145,
|
||||
QuotaExceeded = 151,
|
||||
SharedSubscriptionsNotSupported = 158,
|
||||
SubscriptionIdentifiersNotSupported = 161,
|
||||
WildcardSubscriptionsNotSupported = 162
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Client.Subscribing
|
||||
{
|
||||
public class MqttClientSubscribeResultItem
|
||||
{
|
||||
public MqttClientSubscribeResultItem(TopicFilter topicFilter, MqttClientSubscribeResultCode resultCode)
|
||||
{
|
||||
TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter));
|
||||
ResultCode = resultCode;
|
||||
}
|
||||
|
||||
public TopicFilter TopicFilter { get; }
|
||||
|
||||
public MqttClientSubscribeResultCode ResultCode { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.Client.Unsubscribing
|
||||
{
|
||||
public class MqttClientUnsubscribeResult
|
||||
{
|
||||
public List<MqttClientUnsubscribeResultItem> Items { get; } =new List<MqttClientUnsubscribeResultItem>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace MQTTnet.Client.Unsubscribing
|
||||
{
|
||||
public enum MqttClientUnsubscribeResultCode
|
||||
{
|
||||
Success = 0,
|
||||
NoSubscriptionExisted = 17,
|
||||
UnspecifiedError = 128,
|
||||
ImplementationSpecificError = 131,
|
||||
NotAuthorized = 135,
|
||||
TopicFilterInvalid = 143,
|
||||
PacketIdentifierInUse = 145
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Client.Unsubscribing
|
||||
{
|
||||
public class MqttClientUnsubscribeResultItem
|
||||
{
|
||||
public MqttClientUnsubscribeResultItem(string topicFilter, MqttClientUnsubscribeResultCode reasonCode)
|
||||
{
|
||||
TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter));
|
||||
ReasonCode = reasonCode;
|
||||
}
|
||||
|
||||
public string TopicFilter { get; }
|
||||
|
||||
public MqttClientUnsubscribeResultCode ReasonCode { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Client.Subscribing;
|
||||
using MQTTnet.Client.Unsubscribing;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public interface IMqttDataConverter
|
||||
{
|
||||
MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage);
|
||||
|
||||
MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket);
|
||||
|
||||
MqttClientConnectResult CreateClientConnectResult(MqttConnAckPacket connAckPacket);
|
||||
|
||||
MqttConnectPacket CreateConnectPacket(MqttApplicationMessage willApplicationMessage, IMqttClientOptions options);
|
||||
|
||||
MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket);
|
||||
|
||||
MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public interface IMqttPacketFormatter
|
||||
{
|
||||
IMqttDataConverter DataConverter { get; }
|
||||
|
||||
ArraySegment<byte> Encode(MqttBasePacket mqttPacket);
|
||||
|
||||
MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket);
|
||||
|
||||
void FreeBuffer();
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -1,15 +1,18 @@
|
||||
namespace MQTTnet.Serializer
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public struct MqttFixedHeader
|
||||
{
|
||||
public MqttFixedHeader(byte flags, int remainingLength)
|
||||
public MqttFixedHeader(byte flags, int remainingLength, int totalLength)
|
||||
{
|
||||
Flags = flags;
|
||||
RemainingLength = remainingLength;
|
||||
TotalLength = totalLength;
|
||||
}
|
||||
|
||||
public byte Flags { get; }
|
||||
|
||||
public int RemainingLength { get; }
|
||||
|
||||
public int TotalLength { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public class MqttPacketBodyReader
|
||||
{
|
||||
private readonly byte[] _buffer;
|
||||
private readonly ulong _initialOffset;
|
||||
private readonly ulong _length;
|
||||
|
||||
public MqttPacketBodyReader(byte[] buffer, int offset, int length)
|
||||
: this(buffer, (ulong)offset, (ulong)length)
|
||||
{
|
||||
}
|
||||
|
||||
public MqttPacketBodyReader(byte[] buffer, ulong offset, ulong length)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_initialOffset = offset;
|
||||
Offset = offset;
|
||||
_length = length;
|
||||
}
|
||||
|
||||
public ulong Offset { get; private set; }
|
||||
|
||||
public ulong Length => _length - Offset;
|
||||
|
||||
public bool EndOfStream => Offset == _length;
|
||||
|
||||
public void Seek(ulong position)
|
||||
{
|
||||
Offset = _initialOffset + position;
|
||||
}
|
||||
|
||||
public ArraySegment<byte> Read(uint length)
|
||||
{
|
||||
ValidateReceiveBuffer(length);
|
||||
|
||||
var buffer = new ArraySegment<byte>(_buffer, (int)Offset, (int)length);
|
||||
Offset += length;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
ValidateReceiveBuffer(1);
|
||||
return _buffer[Offset++];
|
||||
}
|
||||
|
||||
public bool ReadBoolean()
|
||||
{
|
||||
ValidateReceiveBuffer(1);
|
||||
var buffer = _buffer[Offset++];
|
||||
|
||||
if (buffer == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buffer == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new MqttProtocolViolationException("Boolean values can be 0 or 1 only.");
|
||||
}
|
||||
|
||||
public ArraySegment<byte> ReadRemainingData()
|
||||
{
|
||||
return new ArraySegment<byte>(_buffer, (int)Offset, (int)(_length - Offset));
|
||||
}
|
||||
|
||||
public ushort ReadTwoByteInteger()
|
||||
{
|
||||
ValidateReceiveBuffer(2);
|
||||
|
||||
var msb = _buffer[Offset++];
|
||||
var lsb = _buffer[Offset++];
|
||||
|
||||
return (ushort)(msb << 8 | lsb);
|
||||
}
|
||||
|
||||
public uint ReadFourByteInteger()
|
||||
{
|
||||
ValidateReceiveBuffer(4);
|
||||
|
||||
var byte0 = _buffer[Offset++];
|
||||
var byte1 = _buffer[Offset++];
|
||||
var byte2 = _buffer[Offset++];
|
||||
var byte3 = _buffer[Offset++];
|
||||
|
||||
return (uint)(byte0 << 24 | byte1 << 16 | byte2 << 8 | byte3);
|
||||
}
|
||||
|
||||
public ArraySegment<byte> ReadWithLengthPrefix()
|
||||
{
|
||||
var length = ReadTwoByteInteger();
|
||||
|
||||
ValidateReceiveBuffer(length);
|
||||
|
||||
var result = new ArraySegment<byte>(_buffer, (int)Offset, length);
|
||||
Offset += length;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ValidateReceiveBuffer(uint length)
|
||||
{
|
||||
if (_length < Offset + length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(_buffer), $"Expected at least {Offset + length} bytes but there are only {_length} bytes");
|
||||
}
|
||||
}
|
||||
|
||||
public string ReadStringWithLengthPrefix()
|
||||
{
|
||||
var buffer = ReadWithLengthPrefix();
|
||||
return Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter.V3;
|
||||
using MQTTnet.Formatter.V5;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public class MqttPacketFormatterAdapter
|
||||
{
|
||||
private IMqttPacketFormatter _formatter;
|
||||
|
||||
public MqttPacketFormatterAdapter()
|
||||
{
|
||||
}
|
||||
|
||||
public MqttPacketFormatterAdapter(MqttProtocolVersion protocolVersion)
|
||||
{
|
||||
UseProtocolVersion(protocolVersion);
|
||||
}
|
||||
|
||||
public MqttProtocolVersion? ProtocolVersion { get; private set; }
|
||||
|
||||
public IMqttDataConverter DataConverter
|
||||
{
|
||||
get
|
||||
{
|
||||
ThrowIfFormatterNotSet();
|
||||
return _formatter.DataConverter;
|
||||
}
|
||||
}
|
||||
|
||||
public ArraySegment<byte> Encode(MqttBasePacket packet)
|
||||
{
|
||||
ThrowIfFormatterNotSet();
|
||||
|
||||
return _formatter.Encode(packet);
|
||||
}
|
||||
|
||||
public MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket)
|
||||
{
|
||||
ThrowIfFormatterNotSet();
|
||||
|
||||
return _formatter.Decode(receivedMqttPacket);
|
||||
}
|
||||
|
||||
public void FreeBuffer()
|
||||
{
|
||||
_formatter?.FreeBuffer();
|
||||
}
|
||||
|
||||
public void DetectProtocolVersion(ReceivedMqttPacket receivedMqttPacket)
|
||||
{
|
||||
var protocolVersion = ParseProtocolVersion(receivedMqttPacket);
|
||||
|
||||
// Reset the position of the stream because the protocol version is part of
|
||||
// the regular CONNECT packet. So it will not properly deserialized if this
|
||||
// data is missing.
|
||||
receivedMqttPacket.Body.Seek(0);
|
||||
|
||||
UseProtocolVersion(protocolVersion);
|
||||
}
|
||||
|
||||
private void UseProtocolVersion(MqttProtocolVersion protocolVersion)
|
||||
{
|
||||
ProtocolVersion = protocolVersion;
|
||||
|
||||
switch (protocolVersion)
|
||||
{
|
||||
case MqttProtocolVersion.V500:
|
||||
{
|
||||
_formatter = new MqttV500PacketFormatter();
|
||||
break;
|
||||
}
|
||||
|
||||
case MqttProtocolVersion.V311:
|
||||
{
|
||||
_formatter = new MqttV311PacketFormatter();
|
||||
break;
|
||||
}
|
||||
|
||||
case MqttProtocolVersion.V310:
|
||||
{
|
||||
|
||||
_formatter = new MqttV310PacketFormatter();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MqttProtocolVersion ParseProtocolVersion(ReceivedMqttPacket receivedMqttPacket)
|
||||
{
|
||||
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
|
||||
|
||||
var protocolName = receivedMqttPacket.Body.ReadStringWithLengthPrefix();
|
||||
var protocolLevel = receivedMqttPacket.Body.ReadByte();
|
||||
|
||||
if (protocolName == "MQTT")
|
||||
{
|
||||
if (protocolLevel == 5)
|
||||
{
|
||||
return MqttProtocolVersion.V500;
|
||||
}
|
||||
|
||||
if (protocolLevel == 4)
|
||||
{
|
||||
return MqttProtocolVersion.V311;
|
||||
}
|
||||
|
||||
throw new MqttProtocolViolationException($"Protocol level '{protocolLevel}' not supported.");
|
||||
}
|
||||
|
||||
if (protocolName == "MQIsdp")
|
||||
{
|
||||
if (protocolLevel == 3)
|
||||
{
|
||||
return MqttProtocolVersion.V310;
|
||||
}
|
||||
|
||||
throw new MqttProtocolViolationException($"Protocol level '{protocolLevel}' not supported.");
|
||||
}
|
||||
|
||||
throw new MqttProtocolViolationException($"Protocol '{protocolName}' not supported.");
|
||||
}
|
||||
|
||||
private void ThrowIfFormatterNotSet()
|
||||
{
|
||||
if (_formatter == null)
|
||||
{
|
||||
throw new InvalidOperationException("Protocol version not set or detected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
-23
@@ -1,14 +1,24 @@
|
||||
using System.Threading;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Serializer
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public static class MqttPacketReader
|
||||
public class MqttPacketReader
|
||||
{
|
||||
public static async Task<MqttFixedHeader> ReadFixedHeaderAsync(IMqttChannel channel, byte[] fixedHeaderBuffer, byte[] singleByteBuffer, CancellationToken cancellationToken)
|
||||
private readonly byte[] _singleByteBuffer = new byte[1];
|
||||
|
||||
private readonly IMqttChannel _channel;
|
||||
|
||||
public MqttPacketReader(IMqttChannel channel)
|
||||
{
|
||||
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
|
||||
}
|
||||
|
||||
public async Task<MqttFixedHeader> ReadFixedHeaderAsync(byte[] fixedHeaderBuffer, CancellationToken cancellationToken)
|
||||
{
|
||||
// The MQTT fixed header contains 1 byte of flags and at least 1 byte for the remaining data length.
|
||||
// So in all cases at least 2 bytes must be read for a complete MQTT packet.
|
||||
@@ -19,7 +29,7 @@ namespace MQTTnet.Serializer
|
||||
|
||||
while (totalBytesRead < buffer.Length)
|
||||
{
|
||||
var bytesRead = await channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
|
||||
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ExceptionHelper.ThrowIfGracefulSocketClose(bytesRead);
|
||||
@@ -30,29 +40,30 @@ namespace MQTTnet.Serializer
|
||||
var hasRemainingLength = buffer[1] != 0;
|
||||
if (!hasRemainingLength)
|
||||
{
|
||||
return new MqttFixedHeader(buffer[0], 0);
|
||||
return new MqttFixedHeader(buffer[0], 0, totalBytesRead);
|
||||
}
|
||||
|
||||
#if WINDOWS_UWP
|
||||
// UWP will have a dead lock when calling this not async.
|
||||
var bodyLength = await ReadBodyLengthAsync(channel, buffer[1], singleByteBuffer, cancellationToken).ConfigureAwait(false);
|
||||
var bodyLength = await ReadBodyLengthAsync(buffer[1], cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
// Here the async/await pattern is not used becuase the overhead of context switches
|
||||
// Here the async/await pattern is not used because the overhead of context switches
|
||||
// is too big for reading 1 byte in a row. We expect that the remaining data was sent
|
||||
// directly after the initial bytes. If the client disconnects just in this moment we
|
||||
// will get an exception anyway.
|
||||
var bodyLength = ReadBodyLength(channel, buffer[1], singleByteBuffer, cancellationToken);
|
||||
var bodyLength = ReadBodyLength(buffer[1], cancellationToken);
|
||||
#endif
|
||||
|
||||
return new MqttFixedHeader(buffer[0], bodyLength);
|
||||
totalBytesRead += bodyLength;
|
||||
return new MqttFixedHeader(buffer[0], bodyLength, totalBytesRead);
|
||||
}
|
||||
|
||||
#if !WINDOWS_UWP
|
||||
private static int ReadBodyLength(IMqttChannel channel, byte initialEncodedByte, byte[] singleByteBuffer, CancellationToken cancellationToken)
|
||||
private int ReadBodyLength(byte initialEncodedByte, CancellationToken cancellationToken)
|
||||
{
|
||||
var offset = 0;
|
||||
var multiplier = 128;
|
||||
var value = initialEncodedByte & 127;
|
||||
var value = (initialEncodedByte & 127);
|
||||
int encodedByte = initialEncodedByte;
|
||||
|
||||
while ((encodedByte & 128) != 0)
|
||||
@@ -65,18 +76,18 @@ namespace MQTTnet.Serializer
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
encodedByte = ReadByte(channel, singleByteBuffer, cancellationToken);
|
||||
encodedByte = ReadByte(cancellationToken);
|
||||
|
||||
value += (byte)(encodedByte & 127) * multiplier;
|
||||
value += (encodedByte & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static byte ReadByte(IMqttChannel channel, byte[] singleByteBuffer, CancellationToken cancellationToken)
|
||||
private byte ReadByte(CancellationToken cancellationToken)
|
||||
{
|
||||
var readCount = channel.ReadAsync(singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
var readCount = _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -85,12 +96,12 @@ namespace MQTTnet.Serializer
|
||||
ExceptionHelper.ThrowGracefulSocketClose();
|
||||
}
|
||||
|
||||
return singleByteBuffer[0];
|
||||
return _singleByteBuffer[0];
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
private static async Task<int> ReadBodyLengthAsync(IMqttChannel channel, byte initialEncodedByte, byte[] singleByteBuffer, CancellationToken cancellationToken)
|
||||
private async Task<int> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken)
|
||||
{
|
||||
var offset = 0;
|
||||
var multiplier = 128;
|
||||
@@ -107,18 +118,18 @@ namespace MQTTnet.Serializer
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
encodedByte = await ReadByteAsync(channel, singleByteBuffer, cancellationToken).ConfigureAwait(false);
|
||||
encodedByte = await ReadByteAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
value += (byte)(encodedByte & 127) * multiplier;
|
||||
value += (encodedByte & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static async Task<byte> ReadByteAsync(IMqttChannel channel, byte[] singleByteBuffer, CancellationToken cancellationToken)
|
||||
private async Task<byte> ReadByteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var readCount = await channel.ReadAsync(singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
|
||||
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -127,7 +138,7 @@ namespace MQTTnet.Serializer
|
||||
ExceptionHelper.ThrowGracefulSocketClose();
|
||||
}
|
||||
|
||||
return singleByteBuffer[0];
|
||||
return _singleByteBuffer[0];
|
||||
}
|
||||
|
||||
#endif
|
||||
+50
-20
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Serializer
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a custom implementation of a memory stream which provides only MQTTnet relevant features.
|
||||
@@ -16,7 +17,7 @@ namespace MQTTnet.Serializer
|
||||
|
||||
private byte[] _buffer = new byte[128];
|
||||
|
||||
private int _position;
|
||||
private int _offset;
|
||||
|
||||
public int Length { get; private set; }
|
||||
|
||||
@@ -27,9 +28,9 @@ namespace MQTTnet.Serializer
|
||||
return (byte)fixedHeader;
|
||||
}
|
||||
|
||||
public static ArraySegment<byte> EncodeRemainingLength(int length)
|
||||
public static ArraySegment<byte> EncodeVariableByteInteger(uint value)
|
||||
{
|
||||
if (length <= 0)
|
||||
if (value <= 0)
|
||||
{
|
||||
return new ArraySegment<byte>(new byte[1], 0, 1);
|
||||
}
|
||||
@@ -37,7 +38,7 @@ namespace MQTTnet.Serializer
|
||||
var buffer = new byte[4];
|
||||
var bufferOffset = 0;
|
||||
|
||||
var x = length;
|
||||
var x = value;
|
||||
do
|
||||
{
|
||||
var encodedByte = x % 128;
|
||||
@@ -54,6 +55,11 @@ namespace MQTTnet.Serializer
|
||||
return new ArraySegment<byte>(buffer, 0, bufferOffset);
|
||||
}
|
||||
|
||||
public void WriteVariableLengthInteger(uint value)
|
||||
{
|
||||
Write(EncodeVariableByteInteger(value));
|
||||
}
|
||||
|
||||
public void WriteWithLengthPrefix(string value)
|
||||
{
|
||||
WriteWithLengthPrefix(Encoding.UTF8.GetBytes(value ?? string.Empty));
|
||||
@@ -61,17 +67,19 @@ namespace MQTTnet.Serializer
|
||||
|
||||
public void WriteWithLengthPrefix(byte[] value)
|
||||
{
|
||||
if (value == null) throw new ArgumentNullException(nameof(value));
|
||||
|
||||
EnsureAdditionalCapacity(value.Length + 2);
|
||||
|
||||
Write((ushort)value.Length);
|
||||
Write(value, 0, value.Length);
|
||||
}
|
||||
|
||||
|
||||
public void Write(byte @byte)
|
||||
{
|
||||
EnsureAdditionalCapacity(1);
|
||||
|
||||
_buffer[_position] = @byte;
|
||||
_buffer[_offset] = @byte;
|
||||
IncreasePostition(1);
|
||||
}
|
||||
|
||||
@@ -79,31 +87,50 @@ namespace MQTTnet.Serializer
|
||||
{
|
||||
EnsureAdditionalCapacity(2);
|
||||
|
||||
_buffer[_position] = (byte)(value >> 8);
|
||||
_buffer[_offset] = (byte)(value >> 8);
|
||||
IncreasePostition(1);
|
||||
_buffer[_position] = (byte)value;
|
||||
_buffer[_offset] = (byte)value;
|
||||
IncreasePostition(1);
|
||||
}
|
||||
|
||||
public void Write(byte[] array, int offset, int count)
|
||||
public void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (array == null) throw new ArgumentNullException(nameof(array));
|
||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
EnsureAdditionalCapacity(count);
|
||||
|
||||
Array.Copy(array, offset, _buffer, _position, count);
|
||||
Array.Copy(buffer, offset, _buffer, _offset, count);
|
||||
IncreasePostition(count);
|
||||
}
|
||||
|
||||
public void Write(ArraySegment<byte> buffer)
|
||||
{
|
||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
Write(buffer.Array, buffer.Offset, buffer.Count);
|
||||
}
|
||||
|
||||
public void Write(MqttPacketWriter propertyWriter)
|
||||
{
|
||||
if (propertyWriter == null) throw new ArgumentNullException(nameof(propertyWriter));
|
||||
|
||||
if (propertyWriter.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Write(propertyWriter._buffer, 0, propertyWriter.Length);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Length = 5;
|
||||
}
|
||||
|
||||
public void Seek(int offset)
|
||||
public void Seek(int position)
|
||||
{
|
||||
EnsureCapacity(offset);
|
||||
_position = offset;
|
||||
EnsureCapacity(position);
|
||||
_offset = position;
|
||||
}
|
||||
|
||||
public byte[] GetBuffer()
|
||||
@@ -127,9 +154,10 @@ namespace MQTTnet.Serializer
|
||||
Array.Resize(ref _buffer, MaxBufferSize);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void EnsureAdditionalCapacity(int additionalCapacity)
|
||||
{
|
||||
var freeSpace = _buffer.Length - _position;
|
||||
var freeSpace = _buffer.Length - _offset;
|
||||
if (freeSpace >= additionalCapacity)
|
||||
{
|
||||
return;
|
||||
@@ -138,6 +166,7 @@ namespace MQTTnet.Serializer
|
||||
EnsureCapacity(_buffer.Length + additionalCapacity - freeSpace);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void EnsureCapacity(int capacity)
|
||||
{
|
||||
var newBufferLength = _buffer.Length;
|
||||
@@ -146,7 +175,7 @@ namespace MQTTnet.Serializer
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
while (newBufferLength < capacity)
|
||||
{
|
||||
newBufferLength *= 2;
|
||||
@@ -155,13 +184,14 @@ namespace MQTTnet.Serializer
|
||||
Array.Resize(ref _buffer, newBufferLength);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void IncreasePostition(int length)
|
||||
{
|
||||
_position += length;
|
||||
_offset += length;
|
||||
|
||||
if (_position > Length)
|
||||
if (_offset > Length)
|
||||
{
|
||||
Length = _position;
|
||||
Length = _offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public enum MqttProtocolVersion
|
||||
{
|
||||
V310 = 3,
|
||||
V311 = 4,
|
||||
V500 = 5
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Client.Subscribing;
|
||||
using MQTTnet.Client.Unsubscribing;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Formatter.V3
|
||||
{
|
||||
public class MqttV310DataConverter : IMqttDataConverter
|
||||
{
|
||||
public MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
if (applicationMessage.UserProperties?.Any() == true)
|
||||
{
|
||||
throw new MqttProtocolViolationException("User properties are not supported in MQTT version 3.");
|
||||
}
|
||||
|
||||
return new MqttPublishPacket
|
||||
{
|
||||
Topic = applicationMessage.Topic,
|
||||
Payload = applicationMessage.Payload,
|
||||
QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel,
|
||||
Retain = applicationMessage.Retain,
|
||||
Dup = false
|
||||
};
|
||||
}
|
||||
|
||||
public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
||||
|
||||
return new MqttApplicationMessage
|
||||
{
|
||||
Topic = publishPacket.Topic,
|
||||
Payload = publishPacket.Payload,
|
||||
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
|
||||
Retain = publishPacket.Retain
|
||||
};
|
||||
}
|
||||
|
||||
public MqttClientConnectResult CreateClientConnectResult(MqttConnAckPacket connAckPacket)
|
||||
{
|
||||
if (connAckPacket == null) throw new ArgumentNullException(nameof(connAckPacket));
|
||||
|
||||
MqttClientConnectResultCode resultCode;
|
||||
switch (connAckPacket.ReturnCode.Value)
|
||||
{
|
||||
case MqttConnectReturnCode.ConnectionAccepted:
|
||||
{
|
||||
resultCode = MqttClientConnectResultCode.Success;
|
||||
break;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion:
|
||||
{
|
||||
resultCode = MqttClientConnectResultCode.UnsupportedProtocolVersion;
|
||||
break;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedNotAuthorized:
|
||||
{
|
||||
resultCode = MqttClientConnectResultCode.NotAuthorized;
|
||||
break;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword:
|
||||
{
|
||||
resultCode = MqttClientConnectResultCode.BadUserNameOrPassword;
|
||||
break;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedIdentifierRejected:
|
||||
{
|
||||
resultCode = MqttClientConnectResultCode.ClientIdentifierNotValid;
|
||||
break;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedServerUnavailable:
|
||||
{
|
||||
resultCode = MqttClientConnectResultCode.ServerUnavailable;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new MqttProtocolViolationException("Received unexpected return code.");
|
||||
}
|
||||
|
||||
return new MqttClientConnectResult
|
||||
{
|
||||
IsSessionPresent = connAckPacket.IsSessionPresent,
|
||||
ResultCode = resultCode
|
||||
};
|
||||
}
|
||||
|
||||
public MqttConnectPacket CreateConnectPacket(MqttApplicationMessage willApplicationMessage, IMqttClientOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
return new MqttConnectPacket
|
||||
{
|
||||
ClientId = options.ClientId,
|
||||
Username = options.Credentials?.Username,
|
||||
Password = options.Credentials?.Password,
|
||||
CleanSession = options.CleanSession,
|
||||
KeepAlivePeriod = (ushort)options.KeepAlivePeriod.TotalSeconds,
|
||||
WillMessage = willApplicationMessage
|
||||
};
|
||||
}
|
||||
|
||||
public MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket)
|
||||
{
|
||||
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
|
||||
if (subAckPacket == null) throw new ArgumentNullException(nameof(subAckPacket));
|
||||
|
||||
if (subAckPacket.ReturnCodes.Count != subscribePacket.TopicFilters.Count)
|
||||
{
|
||||
throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1].");
|
||||
}
|
||||
|
||||
var result = new MqttClientSubscribeResult();
|
||||
|
||||
result.Items.AddRange(subscribePacket.TopicFilters.Select((t, i) =>
|
||||
new MqttClientSubscribeResultItem(t, (MqttClientSubscribeResultCode)subAckPacket.ReturnCodes[i])));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket)
|
||||
{
|
||||
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
|
||||
if (unsubAckPacket == null) throw new ArgumentNullException(nameof(unsubAckPacket));
|
||||
|
||||
var result = new MqttClientUnsubscribeResult();
|
||||
|
||||
result.Items.AddRange(unsubscribePacket.TopicFilters.Select((t, i) =>
|
||||
new MqttClientUnsubscribeResultItem(t, MqttClientUnsubscribeResultCode.Success)));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
+97
-150
@@ -1,21 +1,22 @@
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Serializer
|
||||
namespace MQTTnet.Formatter.V3
|
||||
{
|
||||
public class MqttPacketSerializer : IMqttPacketSerializer
|
||||
public class MqttV310PacketFormatter : IMqttPacketFormatter
|
||||
{
|
||||
private const int FixedHeaderSize = 1;
|
||||
|
||||
private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
|
||||
|
||||
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
|
||||
public IMqttDataConverter DataConverter { get; } = new MqttV310DataConverter();
|
||||
|
||||
public ArraySegment<byte> Serialize(MqttBasePacket packet)
|
||||
public ArraySegment<byte> Encode(MqttBasePacket packet)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
|
||||
@@ -23,15 +24,15 @@ namespace MQTTnet.Serializer
|
||||
_packetWriter.Reset();
|
||||
_packetWriter.Seek(5);
|
||||
|
||||
var fixedHeader = SerializePacket(packet, _packetWriter);
|
||||
var remainingLength = _packetWriter.Length - 5;
|
||||
var fixedHeader = EncodePacket(packet, _packetWriter);
|
||||
var remainingLength = (uint)(_packetWriter.Length - 5);
|
||||
|
||||
var remainingLengthBuffer = MqttPacketWriter.EncodeRemainingLength(remainingLength);
|
||||
var remainingLengthBuffer = MqttPacketWriter.EncodeVariableByteInteger(remainingLength);
|
||||
|
||||
var headerSize = FixedHeaderSize + remainingLengthBuffer.Count;
|
||||
var headerOffset = 5 - headerSize;
|
||||
|
||||
// Position cursor on correct offset on beginining of array (has leading 0x0)
|
||||
// Position cursor on correct offset on beginning of array (has leading 0x0)
|
||||
_packetWriter.Seek(headerOffset);
|
||||
_packetWriter.Write(fixedHeader);
|
||||
_packetWriter.Write(remainingLengthBuffer.Array, remainingLengthBuffer.Offset, remainingLengthBuffer.Count);
|
||||
@@ -40,7 +41,7 @@ namespace MQTTnet.Serializer
|
||||
return new ArraySegment<byte>(buffer, headerOffset, _packetWriter.Length - headerOffset);
|
||||
}
|
||||
|
||||
public MqttBasePacket Deserialize(ReceivedMqttPacket receivedMqttPacket)
|
||||
public MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket)
|
||||
{
|
||||
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
|
||||
|
||||
@@ -52,20 +53,20 @@ namespace MQTTnet.Serializer
|
||||
|
||||
switch ((MqttControlPacketType)controlPacketType)
|
||||
{
|
||||
case MqttControlPacketType.Connect: return DeserializeConnect(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.ConnAck: return DeserializeConnAck(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Disconnect: return new MqttDisconnectPacket();
|
||||
case MqttControlPacketType.Publish: return DeserializePublish(receivedMqttPacket);
|
||||
case MqttControlPacketType.PubAck: return DeserializePubAck(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubRec: return DeserializePubRec(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubRel: return DeserializePubRel(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubComp: return DeserializePubComp(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Publish: return DecodePublish(receivedMqttPacket);
|
||||
case MqttControlPacketType.PubAck: return DecodePubAck(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubRec: return DecodePubRec(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubRel: return DecodePubRel(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubComp: return DecodePubComp(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PingReq: return new MqttPingReqPacket();
|
||||
case MqttControlPacketType.PingResp: return new MqttPingRespPacket();
|
||||
case MqttControlPacketType.Subscribe: return DeserializeSubscribe(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.SubAck: return DeserializeSubAck(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Unsubscibe: return DeserializeUnsubscribe(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.UnsubAck: return DeserializeUnsubAck(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Subscribe: return DecodeSubscribe(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.SubAck: return DecodeSubAck(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribe(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.UnsubAck: return DecodeUnsubAck(receivedMqttPacket.Body);
|
||||
|
||||
default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported.");
|
||||
}
|
||||
@@ -76,85 +77,86 @@ namespace MQTTnet.Serializer
|
||||
_packetWriter.FreeBuffer();
|
||||
}
|
||||
|
||||
private byte SerializePacket(MqttBasePacket packet, MqttPacketWriter packetWriter)
|
||||
private byte EncodePacket(MqttBasePacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
switch (packet)
|
||||
{
|
||||
case MqttConnectPacket connectPacket: return Serialize(connectPacket, packetWriter);
|
||||
case MqttConnAckPacket connAckPacket: return Serialize(connAckPacket, packetWriter);
|
||||
case MqttDisconnectPacket _: return SerializeEmptyPacket(MqttControlPacketType.Disconnect);
|
||||
case MqttPingReqPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingReq);
|
||||
case MqttPingRespPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingResp);
|
||||
case MqttPublishPacket publishPacket: return Serialize(publishPacket, packetWriter);
|
||||
case MqttPubAckPacket pubAckPacket: return Serialize(pubAckPacket, packetWriter);
|
||||
case MqttPubRecPacket pubRecPacket: return Serialize(pubRecPacket, packetWriter);
|
||||
case MqttPubRelPacket pubRelPacket: return Serialize(pubRelPacket, packetWriter);
|
||||
case MqttPubCompPacket pubCompPacket: return Serialize(pubCompPacket, packetWriter);
|
||||
case MqttSubscribePacket subscribePacket: return Serialize(subscribePacket, packetWriter);
|
||||
case MqttSubAckPacket subAckPacket: return Serialize(subAckPacket, packetWriter);
|
||||
case MqttUnsubscribePacket unsubscribePacket: return Serialize(unsubscribePacket, packetWriter);
|
||||
case MqttUnsubAckPacket unsubAckPacket: return Serialize(unsubAckPacket, packetWriter);
|
||||
case MqttConnectPacket connectPacket: return EncodeConnectPacket(connectPacket, packetWriter);
|
||||
case MqttConnAckPacket connAckPacket: return EncodeConnAckPacket(connAckPacket, packetWriter);
|
||||
case MqttDisconnectPacket _: return EncodeEmptyPacket(MqttControlPacketType.Disconnect);
|
||||
case MqttPingReqPacket _: return EncodeEmptyPacket(MqttControlPacketType.PingReq);
|
||||
case MqttPingRespPacket _: return EncodeEmptyPacket(MqttControlPacketType.PingResp);
|
||||
case MqttPublishPacket publishPacket: return EncodePublishPacket(publishPacket, packetWriter);
|
||||
case MqttPubAckPacket pubAckPacket: return EncodePubAckPacket(pubAckPacket, packetWriter);
|
||||
case MqttPubRecPacket pubRecPacket: return EncodePubRecPacket(pubRecPacket, packetWriter);
|
||||
case MqttPubRelPacket pubRelPacket: return EncodePubRelPacket(pubRelPacket, packetWriter);
|
||||
case MqttPubCompPacket pubCompPacket: return EncodePubCompPacket(pubCompPacket, packetWriter);
|
||||
case MqttSubscribePacket subscribePacket: return EncodeSubscribePacket(subscribePacket, packetWriter);
|
||||
case MqttSubAckPacket subAckPacket: return EncodeSubAckPacket(subAckPacket, packetWriter);
|
||||
case MqttUnsubscribePacket unsubscribePacket: return EncodeUnsubscribePacket(unsubscribePacket, packetWriter);
|
||||
case MqttUnsubAckPacket unsubAckPacket: return EncodeUnsubAckPacket(unsubAckPacket, packetWriter);
|
||||
|
||||
default: throw new MqttProtocolViolationException("Packet type invalid.");
|
||||
}
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializeUnsubAck(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeUnsubAck(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
return new MqttUnsubAckPacket
|
||||
{
|
||||
PacketIdentifier = body.ReadUInt16()
|
||||
PacketIdentifier = body.ReadTwoByteInteger()
|
||||
};
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializePubComp(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodePubComp(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
return new MqttPubCompPacket
|
||||
{
|
||||
PacketIdentifier = body.ReadUInt16()
|
||||
PacketIdentifier = body.ReadTwoByteInteger()
|
||||
};
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializePubRel(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodePubRel(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
return new MqttPubRelPacket
|
||||
{
|
||||
PacketIdentifier = body.ReadUInt16()
|
||||
PacketIdentifier = body.ReadTwoByteInteger()
|
||||
};
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializePubRec(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodePubRec(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
return new MqttPubRecPacket
|
||||
{
|
||||
PacketIdentifier = body.ReadUInt16()
|
||||
PacketIdentifier = body.ReadTwoByteInteger()
|
||||
};
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializePubAck(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodePubAck(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
return new MqttPubAckPacket
|
||||
{
|
||||
PacketIdentifier = body.ReadUInt16()
|
||||
PacketIdentifier = body.ReadTwoByteInteger()
|
||||
};
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializeUnsubscribe(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeUnsubscribe(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttUnsubscribePacket
|
||||
{
|
||||
PacketIdentifier = body.ReadUInt16(),
|
||||
PacketIdentifier = body.ReadTwoByteInteger(),
|
||||
};
|
||||
|
||||
while (!body.EndOfStream)
|
||||
@@ -165,26 +167,30 @@ namespace MQTTnet.Serializer
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializeSubscribe(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeSubscribe(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttSubscribePacket
|
||||
{
|
||||
PacketIdentifier = body.ReadUInt16()
|
||||
PacketIdentifier = body.ReadTwoByteInteger()
|
||||
};
|
||||
|
||||
while (!body.EndOfStream)
|
||||
{
|
||||
packet.TopicFilters.Add(new TopicFilter(
|
||||
body.ReadStringWithLengthPrefix(),
|
||||
(MqttQualityOfServiceLevel)body.ReadByte()));
|
||||
var topicFilter = new TopicFilter
|
||||
{
|
||||
Topic = body.ReadStringWithLengthPrefix(),
|
||||
QualityOfServiceLevel = (MqttQualityOfServiceLevel)body.ReadByte()
|
||||
};
|
||||
|
||||
packet.TopicFilters.Add(topicFilter);
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializePublish(ReceivedMqttPacket receivedMqttPacket)
|
||||
private static MqttBasePacket DecodePublish(ReceivedMqttPacket receivedMqttPacket)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(receivedMqttPacket.Body);
|
||||
|
||||
@@ -197,7 +203,7 @@ namespace MQTTnet.Serializer
|
||||
ushort? packetIdentifier = null;
|
||||
if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
||||
{
|
||||
packetIdentifier = receivedMqttPacket.Body.ReadUInt16();
|
||||
packetIdentifier = receivedMqttPacket.Body.ReadTwoByteInteger();
|
||||
}
|
||||
|
||||
var packet = new MqttPublishPacket
|
||||
@@ -213,37 +219,15 @@ namespace MQTTnet.Serializer
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializeConnect(MqttPacketBodyReader body)
|
||||
private MqttBasePacket DecodeConnectPacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var protocolName = body.ReadStringWithLengthPrefix();
|
||||
|
||||
MqttProtocolVersion protocolVersion;
|
||||
if (protocolName == "MQTT")
|
||||
var packet = new MqttConnectPacket
|
||||
{
|
||||
var protocolLevel = body.ReadByte();
|
||||
if (protocolLevel != 4)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"Protocol level ({protocolLevel}) not supported for MQTT 3.1.1.");
|
||||
}
|
||||
|
||||
protocolVersion = MqttProtocolVersion.V311;
|
||||
}
|
||||
else if (protocolName == "MQIsdp")
|
||||
{
|
||||
var protocolLevel = body.ReadByte();
|
||||
if (protocolLevel != 3)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"Protocol level ({protocolLevel}) not supported for MQTT 3.1.");
|
||||
}
|
||||
|
||||
protocolVersion = MqttProtocolVersion.V310;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new MqttProtocolViolationException($"Protocol name ({protocolName}) is not supported.");
|
||||
}
|
||||
ProtocolName = body.ReadStringWithLengthPrefix(),
|
||||
ProtocolLevel = body.ReadByte()
|
||||
};
|
||||
|
||||
var connectFlags = body.ReadByte();
|
||||
if ((connectFlags & 0x1) > 0)
|
||||
@@ -251,11 +235,7 @@ namespace MQTTnet.Serializer
|
||||
throw new MqttProtocolViolationException("The first bit of the Connect Flags must be set to 0.");
|
||||
}
|
||||
|
||||
var packet = new MqttConnectPacket
|
||||
{
|
||||
ProtocolVersion = protocolVersion,
|
||||
CleanSession = (connectFlags & 0x2) > 0
|
||||
};
|
||||
packet.CleanSession = (connectFlags & 0x2) > 0;
|
||||
|
||||
var willFlag = (connectFlags & 0x4) > 0;
|
||||
var willQoS = (connectFlags & 0x18) >> 3;
|
||||
@@ -263,7 +243,7 @@ namespace MQTTnet.Serializer
|
||||
var passwordFlag = (connectFlags & 0x40) > 0;
|
||||
var usernameFlag = (connectFlags & 0x80) > 0;
|
||||
|
||||
packet.KeepAlivePeriod = body.ReadUInt16();
|
||||
packet.KeepAlivePeriod = body.ReadTwoByteInteger();
|
||||
packet.ClientId = body.ReadStringWithLengthPrefix();
|
||||
|
||||
if (willFlag)
|
||||
@@ -291,42 +271,36 @@ namespace MQTTnet.Serializer
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializeSubAck(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeSubAck(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttSubAckPacket
|
||||
{
|
||||
PacketIdentifier = body.ReadUInt16()
|
||||
PacketIdentifier = body.ReadTwoByteInteger()
|
||||
};
|
||||
|
||||
while (!body.EndOfStream)
|
||||
{
|
||||
packet.SubscribeReturnCodes.Add((MqttSubscribeReturnCode)body.ReadByte());
|
||||
packet.ReturnCodes.Add((MqttSubscribeReturnCode)body.ReadByte());
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private MqttBasePacket DeserializeConnAck(MqttPacketBodyReader body)
|
||||
protected virtual MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttConnAckPacket();
|
||||
|
||||
var acknowledgeFlags = body.ReadByte();
|
||||
|
||||
if (ProtocolVersion == MqttProtocolVersion.V311)
|
||||
{
|
||||
packet.IsSessionPresent = (acknowledgeFlags & 0x1) > 0;
|
||||
}
|
||||
|
||||
packet.ConnectReturnCode = (MqttConnectReturnCode)body.ReadByte();
|
||||
body.ReadByte(); // Reserved.
|
||||
packet.ReturnCode = (MqttConnectReturnCode)body.ReadByte();
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static void ValidateConnectPacket(MqttConnectPacket packet)
|
||||
protected void ValidateConnectPacket(MqttConnectPacket packet)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
|
||||
@@ -345,21 +319,12 @@ namespace MQTTnet.Serializer
|
||||
}
|
||||
}
|
||||
|
||||
private byte Serialize(MqttConnectPacket packet, MqttPacketWriter packetWriter)
|
||||
protected virtual byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
ValidateConnectPacket(packet);
|
||||
|
||||
// Write variable header
|
||||
if (ProtocolVersion == MqttProtocolVersion.V311)
|
||||
{
|
||||
packetWriter.WriteWithLengthPrefix("MQTT");
|
||||
packetWriter.Write(4); // 3.1.2.2 Protocol Level 4
|
||||
}
|
||||
else
|
||||
{
|
||||
packetWriter.WriteWithLengthPrefix("MQIsdp");
|
||||
packetWriter.Write(3); // Protocol Level 3
|
||||
}
|
||||
packetWriter.WriteWithLengthPrefix("MQIsdp");
|
||||
packetWriter.Write(3); // Protocol Level 3
|
||||
|
||||
byte connectFlags = 0x0;
|
||||
if (packet.CleanSession)
|
||||
@@ -416,33 +381,15 @@ namespace MQTTnet.Serializer
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||
}
|
||||
|
||||
private byte Serialize(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
|
||||
protected virtual byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (ProtocolVersion == MqttProtocolVersion.V310)
|
||||
{
|
||||
packetWriter.Write(0);
|
||||
}
|
||||
else if (ProtocolVersion == MqttProtocolVersion.V311)
|
||||
{
|
||||
byte connectAcknowledgeFlags = 0x0;
|
||||
if (packet.IsSessionPresent)
|
||||
{
|
||||
connectAcknowledgeFlags |= 0x1;
|
||||
}
|
||||
|
||||
packetWriter.Write(connectAcknowledgeFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new MqttProtocolViolationException("Protocol version not supported.");
|
||||
}
|
||||
|
||||
packetWriter.Write((byte)packet.ConnectReturnCode);
|
||||
packetWriter.Write(0); // Reserved.
|
||||
packetWriter.Write((byte)packet.ReturnCode.Value);
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttPubRelPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePubRelPacket(MqttPubRelPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
@@ -454,7 +401,7 @@ namespace MQTTnet.Serializer
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttPublishPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePublishPacket(MqttPublishPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
ValidatePublishPacket(packet);
|
||||
|
||||
@@ -499,7 +446,7 @@ namespace MQTTnet.Serializer
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttPubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePubAckPacket(MqttPubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
@@ -511,7 +458,7 @@ namespace MQTTnet.Serializer
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttPubRecPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePubRecPacket(MqttPubRecPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
@@ -523,7 +470,7 @@ namespace MQTTnet.Serializer
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttPubCompPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePubCompPacket(MqttPubCompPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
@@ -535,7 +482,7 @@ namespace MQTTnet.Serializer
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttSubscribePacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeSubscribePacket(MqttSubscribePacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
|
||||
|
||||
@@ -558,7 +505,7 @@ namespace MQTTnet.Serializer
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttSubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeSubAckPacket(MqttSubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
@@ -567,9 +514,9 @@ namespace MQTTnet.Serializer
|
||||
|
||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
||||
|
||||
if (packet.SubscribeReturnCodes?.Any() == true)
|
||||
if (packet.ReturnCodes?.Any() == true)
|
||||
{
|
||||
foreach (var packetSubscribeReturnCode in packet.SubscribeReturnCodes)
|
||||
foreach (var packetSubscribeReturnCode in packet.ReturnCodes)
|
||||
{
|
||||
packetWriter.Write((byte)packetSubscribeReturnCode);
|
||||
}
|
||||
@@ -578,7 +525,7 @@ namespace MQTTnet.Serializer
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttUnsubscribePacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
|
||||
|
||||
@@ -600,7 +547,7 @@ namespace MQTTnet.Serializer
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttUnsubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
@@ -611,13 +558,13 @@ namespace MQTTnet.Serializer
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
|
||||
}
|
||||
|
||||
private static byte SerializeEmptyPacket(MqttControlPacketType type)
|
||||
private static byte EncodeEmptyPacket(MqttControlPacketType type)
|
||||
{
|
||||
return MqttPacketWriter.BuildFixedHeader(type);
|
||||
}
|
||||
|
||||
// ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local
|
||||
private static void ThrowIfBodyIsEmpty(MqttPacketBodyReader body)
|
||||
protected static void ThrowIfBodyIsEmpty(MqttPacketBodyReader body)
|
||||
{
|
||||
if (body == null || body.Length == 0)
|
||||
{
|
||||
@@ -0,0 +1,99 @@
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Formatter.V3
|
||||
{
|
||||
public class MqttV311PacketFormatter : MqttV310PacketFormatter
|
||||
{
|
||||
protected override byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
ValidateConnectPacket(packet);
|
||||
|
||||
packetWriter.WriteWithLengthPrefix("MQTT");
|
||||
packetWriter.Write(4); // 3.1.2.2 Protocol Level 4
|
||||
|
||||
byte connectFlags = 0x0;
|
||||
if (packet.CleanSession)
|
||||
{
|
||||
connectFlags |= 0x2;
|
||||
}
|
||||
|
||||
if (packet.WillMessage != null)
|
||||
{
|
||||
connectFlags |= 0x4;
|
||||
connectFlags |= (byte)((byte)packet.WillMessage.QualityOfServiceLevel << 3);
|
||||
|
||||
if (packet.WillMessage.Retain)
|
||||
{
|
||||
connectFlags |= 0x20;
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.Password != null && packet.Username == null)
|
||||
{
|
||||
throw new MqttProtocolViolationException("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].");
|
||||
}
|
||||
|
||||
if (packet.Password != null)
|
||||
{
|
||||
connectFlags |= 0x40;
|
||||
}
|
||||
|
||||
if (packet.Username != null)
|
||||
{
|
||||
connectFlags |= 0x80;
|
||||
}
|
||||
|
||||
packetWriter.Write(connectFlags);
|
||||
packetWriter.Write(packet.KeepAlivePeriod);
|
||||
packetWriter.WriteWithLengthPrefix(packet.ClientId);
|
||||
|
||||
if (packet.WillMessage != null)
|
||||
{
|
||||
packetWriter.WriteWithLengthPrefix(packet.WillMessage.Topic);
|
||||
packetWriter.WriteWithLengthPrefix(packet.WillMessage.Payload);
|
||||
}
|
||||
|
||||
if (packet.Username != null)
|
||||
{
|
||||
packetWriter.WriteWithLengthPrefix(packet.Username);
|
||||
}
|
||||
|
||||
if (packet.Password != null)
|
||||
{
|
||||
packetWriter.WriteWithLengthPrefix(packet.Password);
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||
}
|
||||
|
||||
protected override byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
byte connectAcknowledgeFlags = 0x0;
|
||||
if (packet.IsSessionPresent)
|
||||
{
|
||||
connectAcknowledgeFlags |= 0x1;
|
||||
}
|
||||
|
||||
packetWriter.Write(connectAcknowledgeFlags);
|
||||
packetWriter.Write((byte)packet.ReturnCode);
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
|
||||
}
|
||||
|
||||
protected override MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttConnAckPacket();
|
||||
|
||||
var acknowledgeFlags = body.ReadByte();
|
||||
|
||||
packet.IsSessionPresent = (acknowledgeFlags & 0x1) > 0;
|
||||
packet.ReturnCode = (MqttConnectReturnCode)body.ReadByte();
|
||||
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Client.Subscribing;
|
||||
using MQTTnet.Client.Unsubscribing;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Formatter.V5
|
||||
{
|
||||
public class MqttV500DataConverter : IMqttDataConverter
|
||||
{
|
||||
public MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
var packet = new MqttPublishPacket
|
||||
{
|
||||
Topic = applicationMessage.Topic,
|
||||
Payload = applicationMessage.Payload,
|
||||
QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel,
|
||||
Retain = applicationMessage.Retain,
|
||||
Dup = false,
|
||||
Properties = new MqttPublishPacketProperties
|
||||
{
|
||||
ContentType = applicationMessage.ContentType,
|
||||
CorrelationData = applicationMessage.CorrelationData,
|
||||
MessageExpiryInterval = applicationMessage.MessageExpiryInterval,
|
||||
PayloadFormatIndicator = applicationMessage.PayloadFormatIndicator,
|
||||
ResponseTopic = applicationMessage.ResponseTopic,
|
||||
SubscriptionIdentifier = applicationMessage.SubscriptionIdentifier,
|
||||
TopicAlias = applicationMessage.TopicAlias
|
||||
}
|
||||
};
|
||||
|
||||
if (applicationMessage.UserProperties != null)
|
||||
{
|
||||
packet.Properties.UserProperties.AddRange(applicationMessage.UserProperties);
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket)
|
||||
{
|
||||
return new MqttApplicationMessage
|
||||
{
|
||||
Topic = publishPacket.Topic,
|
||||
Payload = publishPacket.Payload,
|
||||
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
|
||||
Retain = publishPacket.Retain,
|
||||
UserProperties = publishPacket.Properties?.UserProperties ?? new List<MqttUserProperty>()
|
||||
};
|
||||
}
|
||||
|
||||
public MqttClientConnectResult CreateClientConnectResult(MqttConnAckPacket connAckPacket)
|
||||
{
|
||||
if (connAckPacket == null) throw new ArgumentNullException(nameof(connAckPacket));
|
||||
|
||||
return new MqttClientConnectResult
|
||||
{
|
||||
IsSessionPresent = connAckPacket.IsSessionPresent,
|
||||
ResultCode = (MqttClientConnectResultCode)connAckPacket.ReasonCode.Value
|
||||
};
|
||||
}
|
||||
|
||||
public MqttConnectPacket CreateConnectPacket(MqttApplicationMessage willApplicationMessage, IMqttClientOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
return new MqttConnectPacket
|
||||
{
|
||||
ClientId = options.ClientId,
|
||||
Username = options.Credentials?.Username,
|
||||
Password = options.Credentials?.Password,
|
||||
CleanSession = options.CleanSession,
|
||||
KeepAlivePeriod = (ushort)options.KeepAlivePeriod.TotalSeconds,
|
||||
WillMessage = willApplicationMessage,
|
||||
Properties = new MqttConnectPacketProperties
|
||||
{
|
||||
AuthenticationMethod = options.AuthenticationMethod,
|
||||
AuthenticationData = options.AuthenticationData,
|
||||
WillDelayInterval = options.WillDelayInterval,
|
||||
MaximumPacketSize = options.MaximumPacketSize,
|
||||
ReceiveMaximum = options.ReceiveMaximum,
|
||||
RequestProblemInformation = options.RequestProblemInformation,
|
||||
RequestResponseInformation = options.RequestResponseInformation,
|
||||
SessionExpiryInterval = options.SessionExpiryInterval,
|
||||
TopicAliasMaximum = options.TopicAliasMaximum
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket)
|
||||
{
|
||||
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
|
||||
if (subAckPacket == null) throw new ArgumentNullException(nameof(subAckPacket));
|
||||
|
||||
if (subAckPacket.ReasonCodes.Count != subscribePacket.TopicFilters.Count)
|
||||
{
|
||||
throw new MqttProtocolViolationException("The reason codes are not matching the topic filters [MQTT-3.9.3-1].");
|
||||
}
|
||||
|
||||
var result = new MqttClientSubscribeResult();
|
||||
|
||||
result.Items.AddRange(subscribePacket.TopicFilters.Select((t, i) =>
|
||||
new MqttClientSubscribeResultItem(t, (MqttClientSubscribeResultCode)subAckPacket.ReasonCodes[i])));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket)
|
||||
{
|
||||
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
|
||||
if (unsubAckPacket == null) throw new ArgumentNullException(nameof(unsubAckPacket));
|
||||
|
||||
if (unsubAckPacket.ReasonCodes.Count != unsubscribePacket.TopicFilters.Count)
|
||||
{
|
||||
throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1].");
|
||||
}
|
||||
|
||||
var result = new MqttClientUnsubscribeResult();
|
||||
|
||||
result.Items.AddRange(unsubscribePacket.TopicFilters.Select((t, i) =>
|
||||
new MqttClientUnsubscribeResultItem(t, (MqttClientUnsubscribeResultCode)unsubAckPacket.ReasonCodes[i])));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,717 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Formatter.V5
|
||||
{
|
||||
public class MqttV500PacketDecoder
|
||||
{
|
||||
public MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket)
|
||||
{
|
||||
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
|
||||
|
||||
var controlPacketType = receivedMqttPacket.FixedHeader >> 4;
|
||||
if (controlPacketType < 1 || controlPacketType > 15)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"The packet type is invalid ({controlPacketType}).");
|
||||
}
|
||||
|
||||
switch ((MqttControlPacketType)controlPacketType)
|
||||
{
|
||||
case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Disconnect: return DecodeDisconnectPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket.FixedHeader, receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PingReq: return DecodePingReqPacket();
|
||||
case MqttControlPacketType.PingResp: return DecodePingRespPacket();
|
||||
case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Auth: return DecodeAuthPacket(receivedMqttPacket.Body);
|
||||
|
||||
default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeConnectPacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttConnectPacket
|
||||
{
|
||||
Properties = new MqttConnectPacketProperties()
|
||||
};
|
||||
|
||||
var protocolName = body.ReadStringWithLengthPrefix();
|
||||
var protocolVersion = body.ReadByte();
|
||||
|
||||
if (protocolName != "MQTT" && protocolVersion != 5)
|
||||
{
|
||||
throw new MqttProtocolViolationException("MQTT protocol name and version do not match MQTT v5.");
|
||||
}
|
||||
|
||||
var connectFlags = body.ReadByte();
|
||||
|
||||
var cleanSessionFlag = (connectFlags & 0x02) > 0;
|
||||
var willMessageFlag = (connectFlags & 0x04) > 0;
|
||||
var willMessageQoS = (byte)(connectFlags >> 3 & 3);
|
||||
var willMessageRetainFlag = (connectFlags & 0x20) > 0;
|
||||
var passwordFlag = (connectFlags & 0x40) > 0;
|
||||
var usernameFlag = (connectFlags & 0x80) > 0;
|
||||
|
||||
packet.CleanSession = cleanSessionFlag;
|
||||
|
||||
if (willMessageFlag)
|
||||
{
|
||||
packet.WillMessage = new MqttApplicationMessage
|
||||
{
|
||||
QualityOfServiceLevel = (MqttQualityOfServiceLevel)willMessageQoS,
|
||||
Retain = willMessageRetainFlag
|
||||
};
|
||||
}
|
||||
|
||||
packet.KeepAlivePeriod = body.ReadTwoByteInteger();
|
||||
|
||||
var propertiesReader = new MqttV500PropertiesReader(body);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
if (propertiesReader.CurrentPropertyId == MqttPropertyId.SessionExpiryInterval)
|
||||
{
|
||||
packet.Properties.SessionExpiryInterval = propertiesReader.ReadSessionExpiryInterval();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationMethod)
|
||||
{
|
||||
packet.Properties.AuthenticationMethod = propertiesReader.ReadAuthenticationMethod();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.WillDelayInterval)
|
||||
{
|
||||
packet.Properties.WillDelayInterval = propertiesReader.ReadWillDelayInterval();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationData)
|
||||
{
|
||||
packet.Properties.AuthenticationData = propertiesReader.ReadAuthenticationData();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReceiveMaximum)
|
||||
{
|
||||
packet.Properties.ReceiveMaximum = propertiesReader.ReadReceiveMaximum();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.TopicAliasMaximum)
|
||||
{
|
||||
packet.Properties.TopicAliasMaximum = propertiesReader.ReadTopicAliasMaximum();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.MaximumPacketSize)
|
||||
{
|
||||
packet.Properties.MaximumPacketSize = propertiesReader.ReadMaximumPacketSize();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.RequestResponseInformation)
|
||||
{
|
||||
packet.Properties.RequestResponseInformation = propertiesReader.RequestResponseInformation();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.RequestProblemInformation)
|
||||
{
|
||||
packet.Properties.RequestProblemInformation = propertiesReader.RequestProblemInformation();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
|
||||
{
|
||||
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttConnAckPacket));
|
||||
}
|
||||
}
|
||||
|
||||
packet.ClientId = body.ReadStringWithLengthPrefix();
|
||||
|
||||
if (packet.WillMessage != null)
|
||||
{
|
||||
packet.WillMessage.Topic = body.ReadStringWithLengthPrefix();
|
||||
packet.WillMessage.Payload = body.ReadWithLengthPrefix().ToArray();
|
||||
}
|
||||
|
||||
if (usernameFlag)
|
||||
{
|
||||
packet.Username = body.ReadStringWithLengthPrefix();
|
||||
}
|
||||
|
||||
if (passwordFlag)
|
||||
{
|
||||
packet.Password = body.ReadStringWithLengthPrefix();
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var acknowledgeFlags = body.ReadByte();
|
||||
|
||||
var packet = new MqttConnAckPacket
|
||||
{
|
||||
IsSessionPresent = (acknowledgeFlags & 0x1) > 0,
|
||||
ReasonCode = (MqttConnectReasonCode)body.ReadByte(),
|
||||
Properties = new MqttConnAckPacketProperties()
|
||||
};
|
||||
|
||||
var propertiesReader = new MqttV500PropertiesReader(body);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
if (propertiesReader.CurrentPropertyId == MqttPropertyId.SessionExpiryInterval)
|
||||
{
|
||||
packet.Properties.SessionExpiryInterval = propertiesReader.ReadSessionExpiryInterval();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationMethod)
|
||||
{
|
||||
packet.Properties.AuthenticationMethod = propertiesReader.ReadAuthenticationMethod();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationData)
|
||||
{
|
||||
packet.Properties.AuthenticationData = propertiesReader.ReadAuthenticationData();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.RetainAvailable)
|
||||
{
|
||||
packet.Properties.RetainAvailable = propertiesReader.ReadRetainAvailable();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReceiveMaximum)
|
||||
{
|
||||
packet.Properties.ReceiveMaximum = propertiesReader.ReadReceiveMaximum();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AssignedClientIdentifer)
|
||||
{
|
||||
packet.Properties.AssignedClientIdentifier = propertiesReader.ReadAssignedClientIdentifier();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.TopicAliasMaximum)
|
||||
{
|
||||
packet.Properties.TopicAliasMaximum = propertiesReader.ReadTopicAliasMaximum();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
|
||||
{
|
||||
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.MaximumPacketSize)
|
||||
{
|
||||
packet.Properties.MaximumPacketSize = propertiesReader.ReadMaximumPacketSize();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.WildcardSubscriptionAvailable)
|
||||
{
|
||||
packet.Properties.WildcardSubscriptionAvailable = propertiesReader.ReadWildcardSubscriptionAvailable();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.SubscriptionIdentifiersAvailable)
|
||||
{
|
||||
packet.Properties.SubscriptionIdentifiersAvailable = propertiesReader.ReadSubscriptionIdentifiersAvailable();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.SharedSubscriptionAvailable)
|
||||
{
|
||||
packet.Properties.SharedSubscriptionAvailable = propertiesReader.ReadSharedSubscriptionAvailable();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ServerKeepAlive)
|
||||
{
|
||||
packet.Properties.ServerKeepAlive = propertiesReader.ReadServerKeepAlive();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ResponseInformation)
|
||||
{
|
||||
packet.Properties.ResponseInformation = propertiesReader.ReadResponseInformation();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ServerReference)
|
||||
{
|
||||
packet.Properties.ServerReference = propertiesReader.ReadServerReference();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
|
||||
{
|
||||
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttConnAckPacket));
|
||||
}
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeDisconnectPacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttDisconnectPacket
|
||||
{
|
||||
ReasonCode = (MqttDisconnectReasonCode)body.ReadByte(),
|
||||
Properties = new MqttDisconnectPacketProperties()
|
||||
};
|
||||
|
||||
var propertiesReader = new MqttV500PropertiesReader(body);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
if (propertiesReader.CurrentPropertyId == MqttPropertyId.SessionExpiryInterval)
|
||||
{
|
||||
packet.Properties.SessionExpiryInterval = propertiesReader.ReadSessionExpiryInterval();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
|
||||
{
|
||||
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ServerReference)
|
||||
{
|
||||
packet.Properties.ServerReference = propertiesReader.ReadServerReference();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
|
||||
{
|
||||
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttDisconnectPacket));
|
||||
}
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeSubscribePacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttSubscribePacket
|
||||
{
|
||||
PacketIdentifier = body.ReadTwoByteInteger(),
|
||||
Properties = new MqttSubscribePacketProperties()
|
||||
};
|
||||
|
||||
var propertiesReader = new MqttV500PropertiesReader(body);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
if (propertiesReader.CurrentPropertyId == MqttPropertyId.SubscriptionIdentifier)
|
||||
{
|
||||
packet.Properties.SubscriptionIdentifier = propertiesReader.ReadSubscriptionIdentifier();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
|
||||
{
|
||||
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttSubscribePacket));
|
||||
}
|
||||
}
|
||||
|
||||
while (!body.EndOfStream)
|
||||
{
|
||||
var topic = body.ReadStringWithLengthPrefix();
|
||||
var options = body.ReadByte();
|
||||
|
||||
var qos = (MqttQualityOfServiceLevel)(options & 3);
|
||||
var noLocal = (options & (1 << 2)) > 0;
|
||||
var retainAsPublished = (options & (1 << 3)) > 0;
|
||||
var retainHandling = (MqttRetainHandling)((options >> 4) & 3);
|
||||
|
||||
packet.TopicFilters.Add(new TopicFilter
|
||||
{
|
||||
Topic = topic,
|
||||
QualityOfServiceLevel = qos,
|
||||
NoLocal = noLocal,
|
||||
RetainAsPublished = retainAsPublished,
|
||||
RetainHandling = retainHandling
|
||||
});
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeSubAckPacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttSubAckPacket
|
||||
{
|
||||
PacketIdentifier = body.ReadTwoByteInteger(),
|
||||
Properties = new MqttSubAckPacketProperties()
|
||||
};
|
||||
|
||||
var propertiesReader = new MqttV500PropertiesReader(body);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
|
||||
{
|
||||
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
|
||||
{
|
||||
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttSubAckPacket));
|
||||
}
|
||||
}
|
||||
|
||||
while (!body.EndOfStream)
|
||||
{
|
||||
var reasonCode = (MqttSubscribeReasonCode)body.ReadByte();
|
||||
packet.ReasonCodes.Add(reasonCode);
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeUnsubscribePacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttUnsubscribePacket
|
||||
{
|
||||
PacketIdentifier = body.ReadTwoByteInteger(),
|
||||
Properties = new MqttUnsubscribePacketProperties()
|
||||
};
|
||||
|
||||
var propertiesReader = new MqttV500PropertiesReader(body);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
|
||||
{
|
||||
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttUnsubscribePacket));
|
||||
}
|
||||
}
|
||||
|
||||
while (!body.EndOfStream)
|
||||
{
|
||||
packet.TopicFilters.Add(body.ReadStringWithLengthPrefix());
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeUnsubAckPacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttUnsubAckPacket
|
||||
{
|
||||
PacketIdentifier = body.ReadTwoByteInteger(),
|
||||
Properties = new MqttUnsubAckPacketProperties()
|
||||
};
|
||||
|
||||
var propertiesReader = new MqttV500PropertiesReader(body);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
|
||||
{
|
||||
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
|
||||
{
|
||||
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttUnsubAckPacket));
|
||||
}
|
||||
}
|
||||
|
||||
while (!body.EndOfStream)
|
||||
{
|
||||
var reasonCode = (MqttUnsubscribeReasonCode)body.ReadByte();
|
||||
packet.ReasonCodes.Add(reasonCode);
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePingReqPacket()
|
||||
{
|
||||
return new MqttPingReqPacket();
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePingRespPacket()
|
||||
{
|
||||
return new MqttPingRespPacket();
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePublishPacket(byte header, MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var retain = (header & 1) > 0;
|
||||
var qos = (MqttQualityOfServiceLevel)(header >> 1 & 3);
|
||||
var dup = (header >> 3 & 1) > 0;
|
||||
|
||||
var packet = new MqttPublishPacket
|
||||
{
|
||||
Topic = body.ReadStringWithLengthPrefix(),
|
||||
Retain = retain,
|
||||
QualityOfServiceLevel = qos,
|
||||
Dup = dup,
|
||||
Properties = new MqttPublishPacketProperties()
|
||||
};
|
||||
|
||||
if (qos > 0)
|
||||
{
|
||||
packet.PacketIdentifier = body.ReadTwoByteInteger();
|
||||
}
|
||||
|
||||
var propertiesReader = new MqttV500PropertiesReader(body);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
if (propertiesReader.CurrentPropertyId == MqttPropertyId.PayloadFormatIndicator)
|
||||
{
|
||||
packet.Properties.PayloadFormatIndicator = propertiesReader.ReadPayloadFormatIndicator();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.MessageExpiryInterval)
|
||||
{
|
||||
packet.Properties.MessageExpiryInterval = propertiesReader.ReadMessageExpiryInterval();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.TopicAlias)
|
||||
{
|
||||
packet.Properties.TopicAlias = propertiesReader.ReadTopicAlias();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ResponseTopic)
|
||||
{
|
||||
packet.Properties.ResponseTopic = propertiesReader.ReadResponseTopic();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.CorrelationData)
|
||||
{
|
||||
packet.Properties.CorrelationData = propertiesReader.ReadCorrelationData();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.SubscriptionIdentifier)
|
||||
{
|
||||
packet.Properties.SubscriptionIdentifier = propertiesReader.ReadSubscriptionIdentifier();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ContentType)
|
||||
{
|
||||
packet.Properties.ContentType = propertiesReader.ReadContentType();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
|
||||
{
|
||||
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPublishPacket));
|
||||
}
|
||||
}
|
||||
|
||||
packet.Payload = body.ReadRemainingData().ToArray();
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePubAckPacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttPubAckPacket
|
||||
{
|
||||
PacketIdentifier = body.ReadTwoByteInteger(),
|
||||
Properties = new MqttPubAckPacketProperties()
|
||||
};
|
||||
|
||||
if (body.EndOfStream)
|
||||
{
|
||||
packet.ReasonCode = MqttPubAckReasonCode.Success;
|
||||
return packet;
|
||||
}
|
||||
|
||||
packet.ReasonCode = (MqttPubAckReasonCode)body.ReadByte();
|
||||
|
||||
var propertiesReader = new MqttV500PropertiesReader(body);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
|
||||
{
|
||||
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
|
||||
{
|
||||
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPubAckPacket));
|
||||
}
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePubRecPacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttPubRecPacket
|
||||
{
|
||||
PacketIdentifier = body.ReadTwoByteInteger(),
|
||||
Properties = new MqttPubRecPacketProperties()
|
||||
};
|
||||
|
||||
if (body.EndOfStream)
|
||||
{
|
||||
packet.ReasonCode = MqttPubRecReasonCode.Success;
|
||||
return packet;
|
||||
}
|
||||
|
||||
packet.ReasonCode = (MqttPubRecReasonCode)body.ReadByte();
|
||||
|
||||
var propertiesReader = new MqttV500PropertiesReader(body);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
|
||||
{
|
||||
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
|
||||
{
|
||||
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPubRecPacket));
|
||||
}
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePubRelPacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttPubRelPacket
|
||||
{
|
||||
PacketIdentifier = body.ReadTwoByteInteger(),
|
||||
Properties = new MqttPubRelPacketProperties()
|
||||
};
|
||||
|
||||
if (body.EndOfStream)
|
||||
{
|
||||
packet.ReasonCode = MqttPubRelReasonCode.Success;
|
||||
return packet;
|
||||
}
|
||||
|
||||
packet.ReasonCode = (MqttPubRelReasonCode)body.ReadByte();
|
||||
|
||||
var propertiesReader = new MqttV500PropertiesReader(body);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
|
||||
{
|
||||
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
|
||||
{
|
||||
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPubRelPacket));
|
||||
}
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePubCompPacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttPubCompPacket
|
||||
{
|
||||
PacketIdentifier = body.ReadTwoByteInteger(),
|
||||
Properties = new MqttPubCompPacketProperties()
|
||||
};
|
||||
|
||||
if (body.EndOfStream)
|
||||
{
|
||||
packet.ReasonCode = MqttPubCompReasonCode.Success;
|
||||
return packet;
|
||||
}
|
||||
|
||||
packet.ReasonCode = (MqttPubCompReasonCode)body.ReadByte();
|
||||
|
||||
var propertiesReader = new MqttV500PropertiesReader(body);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
|
||||
{
|
||||
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
|
||||
{
|
||||
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPubCompPacket));
|
||||
}
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeAuthPacket(MqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttAuthPacket
|
||||
{
|
||||
Properties = new MqttAuthPacketProperties()
|
||||
};
|
||||
|
||||
if (body.EndOfStream)
|
||||
{
|
||||
packet.ReasonCode = MqttAuthenticateReasonCode.Success;
|
||||
return packet;
|
||||
}
|
||||
|
||||
packet.ReasonCode = (MqttAuthenticateReasonCode)body.ReadByte();
|
||||
|
||||
var propertiesReader = new MqttV500PropertiesReader(body);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationMethod)
|
||||
{
|
||||
packet.Properties.AuthenticationMethod = propertiesReader.ReadAuthenticationMethod();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationData)
|
||||
{
|
||||
packet.Properties.AuthenticationData = propertiesReader.ReadAuthenticationData();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
|
||||
{
|
||||
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
|
||||
}
|
||||
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
|
||||
{
|
||||
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttAuthPacket));
|
||||
}
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
// ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local
|
||||
private static void ThrowIfBodyIsEmpty(MqttPacketBodyReader body)
|
||||
{
|
||||
if (body == null || body.Length == 0)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Data from the body is required but not present.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,572 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Formatter.V5
|
||||
{
|
||||
public class MqttV500PacketEncoder
|
||||
{
|
||||
private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
|
||||
|
||||
public ArraySegment<byte> Encode(MqttBasePacket packet)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
|
||||
// Leave enough head space for max header size (fixed + 4 variable remaining length = 5 bytes)
|
||||
_packetWriter.Reset();
|
||||
_packetWriter.Seek(5);
|
||||
|
||||
var fixedHeader = EncodePacket(packet, _packetWriter);
|
||||
var remainingLength = (uint)(_packetWriter.Length - 5);
|
||||
|
||||
var remainingLengthBuffer = MqttPacketWriter.EncodeVariableByteInteger(remainingLength);
|
||||
|
||||
var headerSize = 1 + remainingLengthBuffer.Count;
|
||||
var headerOffset = 5 - headerSize;
|
||||
|
||||
// Position cursor on correct offset on beginning of array (has leading 0x0)
|
||||
_packetWriter.Seek(headerOffset);
|
||||
_packetWriter.Write(fixedHeader);
|
||||
_packetWriter.Write(remainingLengthBuffer.Array, remainingLengthBuffer.Offset, remainingLengthBuffer.Count);
|
||||
|
||||
var buffer = _packetWriter.GetBuffer();
|
||||
return new ArraySegment<byte>(buffer, headerOffset, _packetWriter.Length - headerOffset);
|
||||
}
|
||||
|
||||
public void FreeBuffer()
|
||||
{
|
||||
_packetWriter.FreeBuffer();
|
||||
}
|
||||
|
||||
private static byte EncodePacket(MqttBasePacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
switch (packet)
|
||||
{
|
||||
case MqttConnectPacket connectPacket: return EncodeConnectPacket(connectPacket, packetWriter);
|
||||
case MqttConnAckPacket connAckPacket: return EncodeConnAckPacket(connAckPacket, packetWriter);
|
||||
case MqttDisconnectPacket disconnectPacket: return EncodeDisconnectPacket(disconnectPacket, packetWriter);
|
||||
case MqttPingReqPacket _: return EncodePingReqPacket();
|
||||
case MqttPingRespPacket _: return EncodePingRespPacket();
|
||||
case MqttPublishPacket publishPacket: return EncodePublishPacket(publishPacket, packetWriter);
|
||||
case MqttPubAckPacket pubAckPacket: return EncodePubAckPacket(pubAckPacket, packetWriter);
|
||||
case MqttPubRecPacket pubRecPacket: return EncodePubRecPacket(pubRecPacket, packetWriter);
|
||||
case MqttPubRelPacket pubRelPacket: return EncodePubRelPacket(pubRelPacket, packetWriter);
|
||||
case MqttPubCompPacket pubCompPacket: return EncodePubCompPacket(pubCompPacket, packetWriter);
|
||||
case MqttSubscribePacket subscribePacket: return EncodeSubscribePacket(subscribePacket, packetWriter);
|
||||
case MqttSubAckPacket subAckPacket: return EncodeSubAckPacket(subAckPacket, packetWriter);
|
||||
case MqttUnsubscribePacket unsubscribePacket: return EncodeUnsubscribePacket(unsubscribePacket, packetWriter);
|
||||
case MqttUnsubAckPacket unsubAckPacket: return EncodeUnsubAckPacket(unsubAckPacket, packetWriter);
|
||||
case MqttAuthPacket authPacket: return EncodeAuthPacket(authPacket, packetWriter);
|
||||
|
||||
default: throw new MqttProtocolViolationException("Packet type invalid.");
|
||||
}
|
||||
}
|
||||
|
||||
private static byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||
|
||||
if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession)
|
||||
{
|
||||
throw new MqttProtocolViolationException("CleanSession must be set if ClientId is empty [MQTT-3.1.3-7].");
|
||||
}
|
||||
|
||||
packetWriter.WriteWithLengthPrefix("MQTT");
|
||||
packetWriter.Write(5); // [3.1.2.2 Protocol Version]
|
||||
|
||||
byte connectFlags = 0x0;
|
||||
if (packet.CleanSession)
|
||||
{
|
||||
connectFlags |= 0x2;
|
||||
}
|
||||
|
||||
if (packet.WillMessage != null)
|
||||
{
|
||||
connectFlags |= 0x4;
|
||||
connectFlags |= (byte)((byte)packet.WillMessage.QualityOfServiceLevel << 3);
|
||||
|
||||
if (packet.WillMessage.Retain)
|
||||
{
|
||||
connectFlags |= 0x20;
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.Password != null && packet.Username == null)
|
||||
{
|
||||
throw new MqttProtocolViolationException("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].");
|
||||
}
|
||||
|
||||
if (packet.Password != null)
|
||||
{
|
||||
connectFlags |= 0x40;
|
||||
}
|
||||
|
||||
if (packet.Username != null)
|
||||
{
|
||||
connectFlags |= 0x80;
|
||||
}
|
||||
|
||||
packetWriter.Write(connectFlags);
|
||||
packetWriter.Write(packet.KeepAlivePeriod);
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
if (packet.Properties != null)
|
||||
{
|
||||
propertiesWriter.WriteWillDelayInterval(packet.Properties.WillDelayInterval);
|
||||
propertiesWriter.WriteSessionExpiryInterval(packet.Properties.SessionExpiryInterval);
|
||||
propertiesWriter.WriteAuthenticationMethod(packet.Properties.AuthenticationMethod);
|
||||
propertiesWriter.WriteAuthenticationData(packet.Properties.AuthenticationData);
|
||||
propertiesWriter.WriteRequestProblemInformation(packet.Properties.RequestProblemInformation);
|
||||
propertiesWriter.WriteRequestResponseInformation(packet.Properties.RequestResponseInformation);
|
||||
propertiesWriter.WriteReceiveMaximum(packet.Properties.ReceiveMaximum);
|
||||
propertiesWriter.WriteTopicAlias(packet.Properties.TopicAliasMaximum);
|
||||
propertiesWriter.WriteMaximumPacketSize(packet.Properties.MaximumPacketSize);
|
||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
|
||||
propertiesWriter.WriteToPacket(packetWriter);
|
||||
|
||||
packetWriter.WriteWithLengthPrefix(packet.ClientId);
|
||||
|
||||
if (packet.WillMessage != null)
|
||||
{
|
||||
packetWriter.WriteWithLengthPrefix(packet.WillMessage.Topic);
|
||||
packetWriter.WriteWithLengthPrefix(packet.WillMessage.Payload);
|
||||
}
|
||||
|
||||
if (packet.Username != null)
|
||||
{
|
||||
packetWriter.WriteWithLengthPrefix(packet.Username);
|
||||
}
|
||||
|
||||
if (packet.Password != null)
|
||||
{
|
||||
packetWriter.WriteWithLengthPrefix(packet.Password);
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||
}
|
||||
|
||||
private static byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||
|
||||
if (!packet.ReasonCode.HasValue)
|
||||
{
|
||||
ThrowReasonCodeNotSetException();
|
||||
}
|
||||
|
||||
byte connectAcknowledgeFlags = 0x0;
|
||||
if (packet.IsSessionPresent)
|
||||
{
|
||||
connectAcknowledgeFlags |= 0x1;
|
||||
}
|
||||
|
||||
packetWriter.Write(connectAcknowledgeFlags);
|
||||
packetWriter.Write((byte)packet.ReasonCode.Value);
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
if (packet.Properties != null)
|
||||
{
|
||||
propertiesWriter.WriteSessionExpiryInterval(packet.Properties.SessionExpiryInterval);
|
||||
propertiesWriter.WriteAuthenticationMethod(packet.Properties.AuthenticationMethod);
|
||||
propertiesWriter.WriteAuthenticationData(packet.Properties.AuthenticationData);
|
||||
propertiesWriter.WriteRetainAvailable(packet.Properties.RetainAvailable);
|
||||
propertiesWriter.WriteReceiveMaximum(packet.Properties.ReceiveMaximum);
|
||||
propertiesWriter.WriteAssignedClientIdentifier(packet.Properties.AssignedClientIdentifier);
|
||||
propertiesWriter.WriteTopicAliasMaximum(packet.Properties.TopicAliasMaximum);
|
||||
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
|
||||
propertiesWriter.WriteMaximumPacketSize(packet.Properties.MaximumPacketSize);
|
||||
propertiesWriter.WriteWildcardSubscriptionAvailable(packet.Properties.WildcardSubscriptionAvailable);
|
||||
propertiesWriter.WriteSubscriptionIdentifiersAvailable(packet.Properties.SubscriptionIdentifiersAvailable);
|
||||
propertiesWriter.WriteSharedSubscriptionAvailable(packet.Properties.SharedSubscriptionAvailable);
|
||||
propertiesWriter.WriteServerKeepAlive(packet.Properties.ServerKeepAlive);
|
||||
propertiesWriter.WriteResponseInformation(packet.Properties.ResponseInformation);
|
||||
propertiesWriter.WriteServerReference(packet.Properties.ServerReference);
|
||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
|
||||
propertiesWriter.WriteToPacket(packetWriter);
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
|
||||
}
|
||||
|
||||
private static byte EncodePublishPacket(MqttPublishPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||
|
||||
if (packet.QualityOfServiceLevel == 0 && packet.Dup)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Dup flag must be false for QoS 0 packets [MQTT-3.3.1-2].");
|
||||
}
|
||||
|
||||
packetWriter.WriteWithLengthPrefix(packet.Topic);
|
||||
|
||||
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Publish packet has no packet identifier.");
|
||||
}
|
||||
|
||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (packet.PacketIdentifier > 0)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Packet identifier must be 0 if QoS == 0 [MQTT-2.3.1-5].");
|
||||
}
|
||||
}
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
if (packet.Properties != null)
|
||||
{
|
||||
propertiesWriter.WritePayloadFormatIndicator(packet.Properties.PayloadFormatIndicator);
|
||||
propertiesWriter.WriteMessageExpiryInterval(packet.Properties.MessageExpiryInterval);
|
||||
propertiesWriter.WriteTopicAlias(packet.Properties.TopicAlias);
|
||||
propertiesWriter.WriteResponseTopic(packet.Properties.ResponseTopic);
|
||||
propertiesWriter.WriteCorrelationData(packet.Properties.CorrelationData);
|
||||
propertiesWriter.WriteSubscriptionIdentifier(packet.Properties.SubscriptionIdentifier);
|
||||
propertiesWriter.WriteContentType(packet.Properties.ContentType);
|
||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
|
||||
propertiesWriter.WriteToPacket(packetWriter);
|
||||
|
||||
if (packet.Payload?.Length > 0)
|
||||
{
|
||||
packetWriter.Write(packet.Payload, 0, packet.Payload.Length);
|
||||
}
|
||||
|
||||
byte fixedHeader = 0;
|
||||
|
||||
if (packet.Retain)
|
||||
{
|
||||
fixedHeader |= 0x01;
|
||||
}
|
||||
|
||||
fixedHeader |= (byte)((byte)packet.QualityOfServiceLevel << 1);
|
||||
|
||||
if (packet.Dup)
|
||||
{
|
||||
fixedHeader |= 0x08;
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader);
|
||||
}
|
||||
|
||||
private static byte EncodePubAckPacket(MqttPubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
throw new MqttProtocolViolationException("PubAck packet has no packet identifier.");
|
||||
}
|
||||
|
||||
if (!packet.ReasonCode.HasValue)
|
||||
{
|
||||
throw new MqttProtocolViolationException("PubAck packet must contain a connect reason.");
|
||||
}
|
||||
|
||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
if (packet.Properties != null)
|
||||
{
|
||||
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
|
||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
|
||||
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubAckReasonCode.Success)
|
||||
{
|
||||
packetWriter.Write((byte)packet.ReasonCode.Value);
|
||||
propertiesWriter.WriteToPacket(packetWriter);
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
|
||||
}
|
||||
|
||||
private static byte EncodePubRecPacket(MqttPubRecPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
if (!packet.ReasonCode.HasValue)
|
||||
{
|
||||
ThrowReasonCodeNotSetException();
|
||||
}
|
||||
|
||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
||||
packetWriter.Write((byte)packet.ReasonCode.Value);
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
if (packet.Properties != null)
|
||||
{
|
||||
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
|
||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
|
||||
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRecReasonCode.Success)
|
||||
{
|
||||
packetWriter.Write((byte)packet.ReasonCode.Value);
|
||||
propertiesWriter.WriteToPacket(packetWriter);
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
|
||||
}
|
||||
|
||||
private static byte EncodePubRelPacket(MqttPubRelPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
if (!packet.ReasonCode.HasValue)
|
||||
{
|
||||
ThrowReasonCodeNotSetException();
|
||||
}
|
||||
|
||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
||||
packetWriter.Write((byte)packet.ReasonCode.Value);
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
if (packet.Properties != null)
|
||||
{
|
||||
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
|
||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
|
||||
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRelReasonCode.Success)
|
||||
{
|
||||
packetWriter.Write((byte)packet.ReasonCode.Value);
|
||||
propertiesWriter.WriteToPacket(packetWriter);
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
|
||||
}
|
||||
|
||||
private static byte EncodePubCompPacket(MqttPubCompPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
if (!packet.ReasonCode.HasValue)
|
||||
{
|
||||
ThrowReasonCodeNotSetException();
|
||||
}
|
||||
|
||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
||||
packetWriter.Write((byte)packet.ReasonCode.Value);
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
if (packet.Properties != null)
|
||||
{
|
||||
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
|
||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
|
||||
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubCompReasonCode.Success)
|
||||
{
|
||||
packetWriter.Write((byte)packet.ReasonCode.Value);
|
||||
propertiesWriter.WriteToPacket(packetWriter);
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
|
||||
}
|
||||
|
||||
private static byte EncodeSubscribePacket(MqttSubscribePacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
|
||||
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
if (packet.Properties != null)
|
||||
{
|
||||
propertiesWriter.WriteSubscriptionIdentifier(packet.Properties.SubscriptionIdentifier);
|
||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
|
||||
propertiesWriter.WriteToPacket(packetWriter);
|
||||
|
||||
if (packet.TopicFilters?.Count > 0)
|
||||
{
|
||||
foreach (var topicFilter in packet.TopicFilters)
|
||||
{
|
||||
packetWriter.WriteWithLengthPrefix(topicFilter.Topic);
|
||||
|
||||
var options = (byte)topicFilter.QualityOfServiceLevel;
|
||||
|
||||
if (topicFilter.NoLocal == true)
|
||||
{
|
||||
options |= 1 << 2;
|
||||
}
|
||||
|
||||
if (topicFilter.RetainAsPublished == true)
|
||||
{
|
||||
options |= 1 << 3;
|
||||
}
|
||||
|
||||
if (topicFilter.RetainHandling.HasValue)
|
||||
{
|
||||
options |= (byte)((byte)topicFilter.RetainHandling.Value << 4);
|
||||
}
|
||||
|
||||
packetWriter.Write(options);
|
||||
}
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02);
|
||||
}
|
||||
|
||||
private static byte EncodeSubAckPacket(MqttSubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
if (packet.ReasonCodes == null || !packet.ReasonCodes.Any())
|
||||
{
|
||||
throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
|
||||
}
|
||||
|
||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
if (packet.Properties != null)
|
||||
{
|
||||
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
|
||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
|
||||
propertiesWriter.WriteToPacket(packetWriter);
|
||||
|
||||
foreach (var reasonCode in packet.ReasonCodes)
|
||||
{
|
||||
packetWriter.Write((byte)reasonCode);
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck);
|
||||
}
|
||||
|
||||
private static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
|
||||
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
if (packet.Properties != null)
|
||||
{
|
||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
|
||||
propertiesWriter.WriteToPacket(packetWriter);
|
||||
|
||||
foreach (var topicFilter in packet.TopicFilters)
|
||||
{
|
||||
packetWriter.WriteWithLengthPrefix(topicFilter);
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
|
||||
}
|
||||
|
||||
private static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
if (packet.ReasonCodes == null || !packet.ReasonCodes.Any())
|
||||
{
|
||||
throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
|
||||
}
|
||||
|
||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
if (packet.Properties != null)
|
||||
{
|
||||
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
|
||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
|
||||
propertiesWriter.WriteToPacket(packetWriter);
|
||||
|
||||
foreach (var reasonCode in packet.ReasonCodes)
|
||||
{
|
||||
packetWriter.Write((byte)reasonCode);
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
|
||||
}
|
||||
|
||||
private static byte EncodeDisconnectPacket(MqttDisconnectPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.ReasonCode.HasValue)
|
||||
{
|
||||
ThrowReasonCodeNotSetException();
|
||||
}
|
||||
|
||||
packetWriter.Write((byte)packet.ReasonCode.Value);
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
if (packet.Properties != null)
|
||||
{
|
||||
propertiesWriter.WriteServerReference(packet.Properties.ServerReference);
|
||||
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
|
||||
propertiesWriter.WriteSessionExpiryInterval(packet.Properties.SessionExpiryInterval);
|
||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
|
||||
propertiesWriter.WriteToPacket(packetWriter);
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Disconnect);
|
||||
}
|
||||
|
||||
private static byte EncodePingReqPacket()
|
||||
{
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingReq);
|
||||
}
|
||||
|
||||
private static byte EncodePingRespPacket()
|
||||
{
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingResp);
|
||||
}
|
||||
|
||||
private static byte EncodeAuthPacket(MqttAuthPacket packet, MqttPacketWriter packetWriter)
|
||||
{
|
||||
packetWriter.Write((byte)packet.ReasonCode);
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
if (packet.Properties != null)
|
||||
{
|
||||
propertiesWriter.WriteAuthenticationMethod(packet.Properties.AuthenticationMethod);
|
||||
propertiesWriter.WriteAuthenticationData(packet.Properties.AuthenticationData);
|
||||
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
|
||||
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
|
||||
}
|
||||
|
||||
propertiesWriter.WriteToPacket(packetWriter);
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Auth);
|
||||
}
|
||||
|
||||
private static void ThrowReasonCodeNotSetException()
|
||||
{
|
||||
throw new MqttProtocolViolationException("The ReasonCode must be set for MQTT version 5.");
|
||||
}
|
||||
|
||||
private static void ThrowIfPacketIdentifierIsInvalid(IMqttPacketWithIdentifier packet)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"Packet identifier is not set for {packet.GetType().Name}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Formatter.V5
|
||||
{
|
||||
public class MqttV500PacketFormatter : IMqttPacketFormatter
|
||||
{
|
||||
private readonly MqttV500PacketEncoder _encoder = new MqttV500PacketEncoder();
|
||||
private readonly MqttV500PacketDecoder _decoder = new MqttV500PacketDecoder();
|
||||
|
||||
public IMqttDataConverter DataConverter { get; } = new MqttV500DataConverter();
|
||||
|
||||
public ArraySegment<byte> Encode(MqttBasePacket mqttPacket)
|
||||
{
|
||||
if (mqttPacket == null) throw new ArgumentNullException(nameof(mqttPacket));
|
||||
|
||||
return _encoder.Encode(mqttPacket);
|
||||
}
|
||||
|
||||
public MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket)
|
||||
{
|
||||
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
|
||||
|
||||
return _decoder.Decode(receivedMqttPacket);
|
||||
}
|
||||
|
||||
public void FreeBuffer()
|
||||
{
|
||||
_encoder.FreeBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Formatter.V5
|
||||
{
|
||||
public class MqttV500PropertiesReader
|
||||
{
|
||||
private readonly MqttPacketBodyReader _body;
|
||||
private readonly uint _length;
|
||||
private readonly ulong _targetOffset;
|
||||
|
||||
public MqttV500PropertiesReader(MqttPacketBodyReader body)
|
||||
{
|
||||
_body = body ?? throw new ArgumentNullException(nameof(body));
|
||||
|
||||
if (!body.EndOfStream)
|
||||
{
|
||||
_length = body.ReadVariableLengthInteger();
|
||||
}
|
||||
|
||||
_targetOffset = body.Offset + _length;
|
||||
}
|
||||
|
||||
public MqttPropertyId CurrentPropertyId { get; private set; }
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (_length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_body.Offset >= _targetOffset)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CurrentPropertyId = (MqttPropertyId)_body.ReadByte();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void FillUserProperties(List<MqttUserProperty> userProperties)
|
||||
{
|
||||
if (userProperties == null) throw new ArgumentNullException(nameof(userProperties));
|
||||
|
||||
var userPropertiesLength = _body.ReadVariableLengthInteger();
|
||||
if (userPropertiesLength == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var targetPosition = _body.Offset + userPropertiesLength;
|
||||
while (_body.Offset < targetPosition)
|
||||
{
|
||||
var name = _body.ReadStringWithLengthPrefix();
|
||||
var value = _body.ReadStringWithLengthPrefix();
|
||||
|
||||
userProperties.Add(new MqttUserProperty(name, value));
|
||||
}
|
||||
}
|
||||
|
||||
public string ReadReasonString()
|
||||
{
|
||||
return _body.ReadStringWithLengthPrefix();
|
||||
}
|
||||
|
||||
public string ReadAuthenticationMethod()
|
||||
{
|
||||
return _body.ReadStringWithLengthPrefix();
|
||||
}
|
||||
|
||||
public byte[] ReadAuthenticationData()
|
||||
{
|
||||
return _body.ReadWithLengthPrefix().ToArray();
|
||||
}
|
||||
|
||||
public bool? ReadRetainAvailable()
|
||||
{
|
||||
return _body.ReadBoolean();
|
||||
}
|
||||
|
||||
public uint? ReadSessionExpiryInterval()
|
||||
{
|
||||
return _body.ReadFourByteInteger();
|
||||
}
|
||||
|
||||
public ushort? ReadReceiveMaximum()
|
||||
{
|
||||
return _body.ReadTwoByteInteger();
|
||||
}
|
||||
|
||||
public string ReadAssignedClientIdentifier()
|
||||
{
|
||||
return _body.ReadStringWithLengthPrefix();
|
||||
}
|
||||
|
||||
public string ReadServerReference()
|
||||
{
|
||||
return _body.ReadStringWithLengthPrefix();
|
||||
}
|
||||
|
||||
public ushort? ReadTopicAliasMaximum()
|
||||
{
|
||||
return _body.ReadTwoByteInteger();
|
||||
}
|
||||
|
||||
public uint? ReadMaximumPacketSize()
|
||||
{
|
||||
return _body.ReadFourByteInteger();
|
||||
}
|
||||
|
||||
public ushort? ReadServerKeepAlive()
|
||||
{
|
||||
return _body.ReadTwoByteInteger();
|
||||
}
|
||||
|
||||
public string ReadResponseInformation()
|
||||
{
|
||||
return _body.ReadStringWithLengthPrefix();
|
||||
}
|
||||
|
||||
public bool? ReadSharedSubscriptionAvailable()
|
||||
{
|
||||
return _body.ReadBoolean();
|
||||
}
|
||||
|
||||
public bool? ReadSubscriptionIdentifiersAvailable()
|
||||
{
|
||||
return _body.ReadBoolean();
|
||||
}
|
||||
|
||||
public bool? ReadWildcardSubscriptionAvailable()
|
||||
{
|
||||
return _body.ReadBoolean();
|
||||
}
|
||||
|
||||
public uint? ReadSubscriptionIdentifier()
|
||||
{
|
||||
return _body.ReadVariableLengthInteger();
|
||||
}
|
||||
|
||||
public MqttPayloadFormatIndicator? ReadPayloadFormatIndicator()
|
||||
{
|
||||
return (MqttPayloadFormatIndicator)_body.ReadByte();
|
||||
}
|
||||
|
||||
public uint? ReadMessageExpiryInterval()
|
||||
{
|
||||
return _body.ReadFourByteInteger();
|
||||
}
|
||||
|
||||
public ushort? ReadTopicAlias()
|
||||
{
|
||||
return _body.ReadTwoByteInteger();
|
||||
}
|
||||
|
||||
public string ReadResponseTopic()
|
||||
{
|
||||
return _body.ReadStringWithLengthPrefix();
|
||||
}
|
||||
|
||||
public byte[] ReadCorrelationData()
|
||||
{
|
||||
return _body.ReadWithLengthPrefix().ToArray();
|
||||
}
|
||||
|
||||
public string ReadContentType()
|
||||
{
|
||||
return _body.ReadStringWithLengthPrefix();
|
||||
}
|
||||
|
||||
public uint? ReadWillDelayInterval()
|
||||
{
|
||||
return _body.ReadFourByteInteger();
|
||||
}
|
||||
|
||||
public bool? RequestResponseInformation()
|
||||
{
|
||||
return _body.ReadBoolean();
|
||||
}
|
||||
|
||||
public bool? RequestProblemInformation()
|
||||
{
|
||||
return _body.ReadBoolean();
|
||||
}
|
||||
|
||||
public void ThrowInvalidPropertyIdException(Type type)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"Property ID '{CurrentPropertyId}' is not supported for package type '{type.Name}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Formatter.V5
|
||||
{
|
||||
public class MqttV500PropertiesWriter
|
||||
{
|
||||
private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
|
||||
|
||||
public void WriteUserProperties(List<MqttUserProperty> userProperties)
|
||||
{
|
||||
if (userProperties == null || userProperties.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var propertyWriter = new MqttPacketWriter();
|
||||
foreach (var property in userProperties)
|
||||
{
|
||||
propertyWriter.WriteWithLengthPrefix(property.Name);
|
||||
propertyWriter.WriteWithLengthPrefix(property.Value);
|
||||
}
|
||||
|
||||
_packetWriter.Write((byte)MqttPropertyId.UserProperty);
|
||||
_packetWriter.WriteVariableLengthInteger((uint)propertyWriter.Length);
|
||||
_packetWriter.Write(propertyWriter);
|
||||
}
|
||||
|
||||
public void WriteCorrelationData(byte[] value)
|
||||
{
|
||||
Write(MqttPropertyId.CorrelationData, value);
|
||||
}
|
||||
|
||||
public void WriteAuthenticationData(byte[] value)
|
||||
{
|
||||
Write(MqttPropertyId.AuthenticationData, value);
|
||||
}
|
||||
|
||||
public void WriteReasonString(string value)
|
||||
{
|
||||
Write(MqttPropertyId.ReasonString, value);
|
||||
}
|
||||
|
||||
public void WriteResponseTopic(string value)
|
||||
{
|
||||
Write(MqttPropertyId.ResponseTopic, value);
|
||||
}
|
||||
|
||||
public void WriteContentType(string value)
|
||||
{
|
||||
Write(MqttPropertyId.ContentType, value);
|
||||
}
|
||||
|
||||
public void WriteServerReference(string value)
|
||||
{
|
||||
Write(MqttPropertyId.ServerReference, value);
|
||||
}
|
||||
|
||||
public void WriteAuthenticationMethod(string value)
|
||||
{
|
||||
Write(MqttPropertyId.AuthenticationMethod, value);
|
||||
}
|
||||
|
||||
public void WriteToPacket(MqttPacketWriter packetWriter)
|
||||
{
|
||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||
|
||||
packetWriter.WriteVariableLengthInteger((uint)_packetWriter.Length);
|
||||
packetWriter.Write(_packetWriter);
|
||||
}
|
||||
|
||||
public void WriteSessionExpiryInterval(uint? value)
|
||||
{
|
||||
WriteAsFourByteInteger(MqttPropertyId.SessionExpiryInterval, value);
|
||||
}
|
||||
|
||||
public void WriteSubscriptionIdentifier(uint? value)
|
||||
{
|
||||
WriteAsVariableLengthInteger(MqttPropertyId.SubscriptionIdentifier, value);
|
||||
}
|
||||
|
||||
public void WriteTopicAlias(ushort? value)
|
||||
{
|
||||
Write(MqttPropertyId.TopicAlias, value);
|
||||
}
|
||||
|
||||
public void WriteMessageExpiryInterval(uint? value)
|
||||
{
|
||||
WriteAsFourByteInteger(MqttPropertyId.MessageExpiryInterval, value);
|
||||
}
|
||||
|
||||
public void WritePayloadFormatIndicator(MqttPayloadFormatIndicator? value)
|
||||
{
|
||||
if (!value.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Write(MqttPropertyId.PayloadFormatIndicator, (byte)value.Value);
|
||||
}
|
||||
|
||||
public void WriteWillDelayInterval(uint? value)
|
||||
{
|
||||
WriteAsFourByteInteger(MqttPropertyId.WillDelayInterval, value);
|
||||
}
|
||||
|
||||
public void WriteRequestProblemInformation(bool? value)
|
||||
{
|
||||
Write(MqttPropertyId.RequestProblemInformation, value);
|
||||
}
|
||||
|
||||
public void WriteRequestResponseInformation(bool? value)
|
||||
{
|
||||
Write(MqttPropertyId.RequestResponseInformation, value);
|
||||
}
|
||||
|
||||
public void WriteReceiveMaximum(ushort? value)
|
||||
{
|
||||
Write(MqttPropertyId.RequestResponseInformation, value);
|
||||
}
|
||||
|
||||
public void WriteMaximumPacketSize(uint? value)
|
||||
{
|
||||
WriteAsFourByteInteger(MqttPropertyId.MaximumPacketSize, value);
|
||||
}
|
||||
|
||||
public void WriteRetainAvailable(bool? value)
|
||||
{
|
||||
Write(MqttPropertyId.RetainAvailable, value);
|
||||
}
|
||||
|
||||
public void WriteAssignedClientIdentifier(string value)
|
||||
{
|
||||
Write(MqttPropertyId.AssignedClientIdentifer, value);
|
||||
}
|
||||
|
||||
public void WriteTopicAliasMaximum(ushort? value)
|
||||
{
|
||||
Write(MqttPropertyId.TopicAliasMaximum, value);
|
||||
}
|
||||
|
||||
public void WriteWildcardSubscriptionAvailable(bool? value)
|
||||
{
|
||||
Write(MqttPropertyId.WildcardSubscriptionAvailable, value);
|
||||
}
|
||||
|
||||
public void WriteSubscriptionIdentifiersAvailable(bool? value)
|
||||
{
|
||||
Write(MqttPropertyId.SubscriptionIdentifiersAvailable, value);
|
||||
}
|
||||
|
||||
public void WriteSharedSubscriptionAvailable(bool? value)
|
||||
{
|
||||
Write(MqttPropertyId.SharedSubscriptionAvailable, value);
|
||||
}
|
||||
|
||||
public void WriteServerKeepAlive(ushort? value)
|
||||
{
|
||||
Write(MqttPropertyId.ServerKeepAlive, value);
|
||||
}
|
||||
|
||||
public void WriteResponseInformation(string value)
|
||||
{
|
||||
Write(MqttPropertyId.ResponseInformation, value);
|
||||
}
|
||||
|
||||
private void Write(MqttPropertyId id, bool? value)
|
||||
{
|
||||
if (!value.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_packetWriter.Write((byte)id);
|
||||
_packetWriter.Write(value.Value ? (byte)0x1 : (byte)0x0);
|
||||
}
|
||||
|
||||
private void Write(MqttPropertyId id, ushort? value)
|
||||
{
|
||||
if (!value.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_packetWriter.Write((byte)id);
|
||||
_packetWriter.Write(value.Value);
|
||||
}
|
||||
|
||||
private void WriteAsVariableLengthInteger(MqttPropertyId id, uint? value)
|
||||
{
|
||||
if (!value.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_packetWriter.Write((byte)id);
|
||||
_packetWriter.WriteVariableLengthInteger(value.Value);
|
||||
}
|
||||
|
||||
private void WriteAsFourByteInteger(MqttPropertyId id, uint? value)
|
||||
{
|
||||
if (!value.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_packetWriter.Write((byte)id);
|
||||
_packetWriter.Write((byte)(value.Value >> 24));
|
||||
_packetWriter.Write((byte)(value.Value >> 16));
|
||||
_packetWriter.Write((byte)(value.Value >> 8));
|
||||
_packetWriter.Write((byte)value.Value);
|
||||
}
|
||||
|
||||
private void Write(MqttPropertyId id, string value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_packetWriter.Write((byte)id);
|
||||
_packetWriter.WriteWithLengthPrefix(value);
|
||||
}
|
||||
|
||||
private void Write(MqttPropertyId id, byte[] value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_packetWriter.Write((byte)id);
|
||||
_packetWriter.WriteWithLengthPrefix(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client.Publishing;
|
||||
|
||||
namespace MQTTnet
|
||||
{
|
||||
public interface IApplicationMessagePublisher
|
||||
{
|
||||
Task PublishAsync(MqttApplicationMessage applicationMessage);
|
||||
Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
@@ -11,19 +12,17 @@ namespace MQTTnet.Implementations
|
||||
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
var serializer = new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion };
|
||||
|
||||
|
||||
switch (options.ChannelOptions)
|
||||
{
|
||||
case MqttClientTcpOptions _:
|
||||
{
|
||||
return new MqttChannelAdapter(new MqttTcpChannel(options), serializer, logger);
|
||||
return new MqttChannelAdapter(new MqttTcpChannel(options), new MqttPacketFormatterAdapter(options.ProtocolVersion), logger);
|
||||
}
|
||||
|
||||
case MqttClientWebSocketOptions webSocketOptions:
|
||||
{
|
||||
return new MqttChannelAdapter(new MqttWebSocketChannel(webSocketOptions), serializer, logger);
|
||||
return new MqttChannelAdapter(new MqttWebSocketChannel(webSocketOptions), new MqttPacketFormatterAdapter(options.ProtocolVersion), logger);
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -10,7 +10,7 @@ using Windows.Networking;
|
||||
using Windows.Networking.Sockets;
|
||||
using Windows.Security.Cryptography.Certificates;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
using System;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
@@ -17,7 +16,7 @@ namespace MQTTnet.Implementations
|
||||
{
|
||||
private readonly IMqttClientOptions _clientOptions;
|
||||
private readonly MqttClientTcpOptions _options;
|
||||
|
||||
|
||||
private Socket _socket;
|
||||
private Stream _stream;
|
||||
|
||||
@@ -87,8 +86,9 @@ namespace MQTTnet.Implementations
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Cleanup(ref _stream, (s) => s.Dispose());
|
||||
Cleanup(ref _socket, (s) => {
|
||||
Cleanup(ref _stream, s => s.Dispose());
|
||||
Cleanup(ref _socket, s =>
|
||||
{
|
||||
if (s.Connected)
|
||||
{
|
||||
s.Shutdown(SocketShutdown.Both);
|
||||
@@ -102,7 +102,7 @@ namespace MQTTnet.Implementations
|
||||
// Try the instance callback.
|
||||
if (_options.TlsOptions.CertificateValidationCallback != null)
|
||||
{
|
||||
return _options.TlsOptions.CertificateValidationCallback(x509Certificate, chain, sslPolicyErrors,_clientOptions);
|
||||
return _options.TlsOptions.CertificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _clientOptions);
|
||||
}
|
||||
|
||||
// Try static callback.
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Threading.Tasks;
|
||||
using Windows.Networking.Sockets;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
@@ -72,7 +72,7 @@ namespace MQTTnet.Implementations
|
||||
{
|
||||
try
|
||||
{
|
||||
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, _options), new MqttPacketSerializer(), _logger);
|
||||
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, _options), new MqttPacketFormatterAdapter(), _logger);
|
||||
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
|
||||
}
|
||||
catch (Exception exception)
|
||||
|
||||
@@ -9,7 +9,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
@@ -20,8 +20,8 @@ namespace MQTTnet.Implementations
|
||||
private readonly CancellationToken _cancellationToken;
|
||||
private readonly AddressFamily _addressFamily;
|
||||
private readonly MqttServerTcpEndpointBaseOptions _options;
|
||||
private readonly MqttServerTlsTcpEndpointOptions _tlsOptions;
|
||||
private readonly X509Certificate2 _tlsCertificate;
|
||||
|
||||
private Socket _socket;
|
||||
|
||||
public MqttTcpServerListener(
|
||||
@@ -36,6 +36,11 @@ namespace MQTTnet.Implementations
|
||||
_tlsCertificate = tlsCertificate;
|
||||
_cancellationToken = cancellationToken;
|
||||
_logger = logger.CreateChildLogger(nameof(MqttTcpServerListener));
|
||||
|
||||
if (_options is MqttServerTlsTcpEndpointOptions tlsOptions)
|
||||
{
|
||||
_tlsOptions = tlsOptions;
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
||||
@@ -75,7 +80,7 @@ namespace MQTTnet.Implementations
|
||||
if (_tlsCertificate != null)
|
||||
{
|
||||
sslStream = new SslStream(new NetworkStream(clientSocket), false);
|
||||
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false);
|
||||
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, _tlsOptions.SslProtocol, false).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_logger.Verbose("Client '{0}' accepted by TCP listener '{1}, {2}'.",
|
||||
@@ -83,7 +88,7 @@ namespace MQTTnet.Implementations
|
||||
_socket.LocalEndPoint,
|
||||
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
|
||||
|
||||
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer(), _logger);
|
||||
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketFormatterAdapter(), _logger);
|
||||
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
@@ -155,9 +155,7 @@ namespace MQTTnet.Implementations
|
||||
|
||||
if (!string.IsNullOrEmpty(_options.ProxyOptions.Username) && !string.IsNullOrEmpty(_options.ProxyOptions.Password))
|
||||
{
|
||||
var credentials =
|
||||
new NetworkCredential(_options.ProxyOptions.Username, _options.ProxyOptions.Password, _options.ProxyOptions.Domain);
|
||||
|
||||
var credentials = new NetworkCredential(_options.ProxyOptions.Username, _options.ProxyOptions.Password, _options.ProxyOptions.Domain);
|
||||
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList, credentials);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Serializer
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
public static class Extensions
|
||||
public static class ArraySegmentExtensions
|
||||
{
|
||||
public static byte[] ToArray(this ArraySegment<byte> source)
|
||||
{
|
||||
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
public class BlockingQueue<TItem>
|
||||
{
|
||||
private readonly object _syncRoot = new object();
|
||||
private readonly LinkedList<TItem> _items = new LinkedList<TItem>();
|
||||
private readonly ManualResetEvent _gate = new ManualResetEvent(false);
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
return _items.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Enqueue(TItem item)
|
||||
{
|
||||
if (item == null) throw new ArgumentNullException(nameof(item));
|
||||
|
||||
lock (_syncRoot)
|
||||
{
|
||||
_items.AddLast(item);
|
||||
_gate.Set();
|
||||
}
|
||||
}
|
||||
|
||||
public TItem Dequeue()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
if (_items.Count > 0)
|
||||
{
|
||||
var item = _items.First.Value;
|
||||
_items.RemoveFirst();
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
if (_items.Count == 0)
|
||||
{
|
||||
_gate.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
_gate.WaitOne();
|
||||
}
|
||||
}
|
||||
|
||||
public TItem PeekAndWait()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
if (_items.Count > 0)
|
||||
{
|
||||
return _items.First.Value;
|
||||
}
|
||||
|
||||
if (_items.Count == 0)
|
||||
{
|
||||
_gate.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
_gate.WaitOne();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveFirst(Predicate<TItem> match)
|
||||
{
|
||||
if (match == null) throw new ArgumentNullException(nameof(match));
|
||||
|
||||
lock (_syncRoot)
|
||||
{
|
||||
if (_items.Count > 0 && match(_items.First.Value))
|
||||
{
|
||||
_items.RemoveFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TItem RemoveFirst()
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
var item = _items.First;
|
||||
_items.RemoveFirst();
|
||||
|
||||
return item.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
_items.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
public static class MqttApplicationMessageExtensions
|
||||
{
|
||||
public static MqttApplicationMessage ToApplicationMessage(this MqttPublishPacket publishPacket)
|
||||
{
|
||||
return new MqttApplicationMessage
|
||||
{
|
||||
Topic = publishPacket.Topic,
|
||||
Payload = publishPacket.Payload,
|
||||
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
|
||||
Retain = publishPacket.Retain
|
||||
};
|
||||
}
|
||||
|
||||
public static MqttPublishPacket ToPublishPacket(this MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new MqttPublishPacket
|
||||
{
|
||||
Topic = applicationMessage.Topic,
|
||||
Payload = applicationMessage.Payload,
|
||||
QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel,
|
||||
Retain = applicationMessage.Retain,
|
||||
Dup = false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='uap10.0'">
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.1.5" />
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.1.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net452'">
|
||||
@@ -63,4 +63,20 @@
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net461'">
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="NewFolder\**" />
|
||||
<EmbeddedResource Remove="NewFolder\**" />
|
||||
<None Remove="NewFolder\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Net.Requests">
|
||||
<HintPath>C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Net.Requests.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,4 +1,6 @@
|
||||
using MQTTnet.Protocol;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet
|
||||
{
|
||||
@@ -11,5 +13,21 @@ namespace MQTTnet
|
||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
|
||||
|
||||
public bool Retain { get; set; }
|
||||
|
||||
public List<MqttUserProperty> UserProperties { get; set; } = new List<MqttUserProperty>();
|
||||
|
||||
public string ContentType { get; set; }
|
||||
|
||||
public string ResponseTopic { get; set; }
|
||||
|
||||
public MqttPayloadFormatIndicator? PayloadFormatIndicator { get; set; }
|
||||
|
||||
public uint? MessageExpiryInterval { get; set; }
|
||||
|
||||
public ushort? TopicAlias { get; set; }
|
||||
|
||||
public byte[] CorrelationData { get; set; }
|
||||
|
||||
public uint? SubscriptionIdentifier { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,26 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet
|
||||
{
|
||||
public class MqttApplicationMessageBuilder
|
||||
{
|
||||
private readonly List<MqttUserProperty> _userProperties = new List<MqttUserProperty>();
|
||||
|
||||
private MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
|
||||
private string _topic;
|
||||
private byte[] _payload;
|
||||
private bool _retain;
|
||||
private string _contentType;
|
||||
private string _responseTopic;
|
||||
private byte[] _correlationData;
|
||||
private ushort? _topicAlias;
|
||||
private uint? _subscriptionIdentifier;
|
||||
private uint? _messageExpiryInterval;
|
||||
private MqttPayloadFormatIndicator? _payloadFormatIndicator;
|
||||
|
||||
public MqttApplicationMessageBuilder WithTopic(string topic)
|
||||
{
|
||||
@@ -100,6 +110,78 @@ namespace MQTTnet
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is only supported when using MQTTv5.
|
||||
/// </summary>
|
||||
public MqttApplicationMessageBuilder WithUserProperty(string name, string value)
|
||||
{
|
||||
_userProperties.Add(new MqttUserProperty(name, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is only supported when using MQTTv5.
|
||||
/// </summary>
|
||||
public MqttApplicationMessageBuilder WithContentType(string contentType)
|
||||
{
|
||||
_contentType = contentType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is only supported when using MQTTv5.
|
||||
/// </summary>
|
||||
public MqttApplicationMessageBuilder WithResponseTopic(string responseTopic)
|
||||
{
|
||||
_responseTopic = responseTopic;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is only supported when using MQTTv5.
|
||||
/// </summary>
|
||||
public MqttApplicationMessageBuilder WithCorrelationData(byte[] correlationData)
|
||||
{
|
||||
_correlationData = correlationData;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is only supported when using MQTTv5.
|
||||
/// </summary>
|
||||
public MqttApplicationMessageBuilder WithTopicAlias(ushort topicAlias)
|
||||
{
|
||||
_topicAlias = topicAlias;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is only supported when using MQTTv5.
|
||||
/// </summary>
|
||||
public MqttApplicationMessageBuilder WithSubscriptionIdentifier(uint subscriptionIdentifier)
|
||||
{
|
||||
_subscriptionIdentifier = subscriptionIdentifier;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is only supported when using MQTTv5.
|
||||
/// </summary>
|
||||
public MqttApplicationMessageBuilder WithMessageExpiryInterval(uint messageExpiryInterval)
|
||||
{
|
||||
_messageExpiryInterval = messageExpiryInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is only supported when using MQTTv5.
|
||||
/// </summary>
|
||||
public MqttApplicationMessageBuilder WithPayloadFormatIndicator(MqttPayloadFormatIndicator payloadFormatIndicator)
|
||||
{
|
||||
_payloadFormatIndicator = payloadFormatIndicator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttApplicationMessage Build()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_topic))
|
||||
@@ -107,13 +189,24 @@ namespace MQTTnet
|
||||
throw new MqttProtocolViolationException("Topic is not set.");
|
||||
}
|
||||
|
||||
return new MqttApplicationMessage
|
||||
var applicationMessage = new MqttApplicationMessage
|
||||
{
|
||||
Topic = _topic,
|
||||
Payload = _payload ?? new byte[0],
|
||||
QualityOfServiceLevel = _qualityOfServiceLevel,
|
||||
Retain = _retain
|
||||
Retain = _retain,
|
||||
ContentType = _contentType,
|
||||
ResponseTopic = _responseTopic,
|
||||
CorrelationData = _correlationData,
|
||||
TopicAlias = _topicAlias,
|
||||
SubscriptionIdentifier = _subscriptionIdentifier,
|
||||
MessageExpiryInterval = _messageExpiryInterval,
|
||||
PayloadFormatIndicator = _payloadFormatIndicator
|
||||
};
|
||||
|
||||
applicationMessage.UserProperties.AddRange(_userProperties);
|
||||
|
||||
return applicationMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Packets
|
||||
{
|
||||
/// <summary>
|
||||
/// Added in MQTTv5.0.0.
|
||||
/// </summary>
|
||||
public class MqttAuthPacket : MqttBasePacket
|
||||
{
|
||||
public MqttAuthenticateReasonCode ReasonCode { get; set; }
|
||||
|
||||
public MqttAuthPacketProperties Properties { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.Packets
|
||||
{
|
||||
public class MqttAuthPacketProperties
|
||||
{
|
||||
public string AuthenticationMethod { get; set; }
|
||||
|
||||
public byte[] AuthenticationData { get; set; }
|
||||
|
||||
public string ReasonString { get; set; }
|
||||
|
||||
public List<MqttUserProperty> UserProperties { get; } = new List<MqttUserProperty>();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user