mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-16 01:03:01 +08:00
Compare commits
179
Commits
v2.8.0
...
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 | ||
|
|
ffc4f1408a | ||
|
|
c8b3066883 | ||
|
|
fb4f89b412 | ||
|
|
6bee6a0469 | ||
|
|
0c2ab9b231 | ||
|
|
03bef4c15d | ||
|
|
ba62ca7306 | ||
|
|
56a1530cee | ||
|
|
699cd5fc23 | ||
|
|
381c59d1e5 | ||
|
|
70f4dc9e10 | ||
|
|
0367ad31f1 | ||
|
|
5c6bcdbca6 | ||
|
|
400c0f05ad | ||
|
|
00df7c9b7b | ||
|
|
a272ed7daa | ||
|
|
b2e7a2e370 | ||
|
|
39bc70cbf0 | ||
|
|
682298b69f | ||
|
|
028a39ec25 | ||
|
|
3b104049b4 | ||
|
|
dd83a360b4 | ||
|
|
976c62d9c2 | ||
|
|
4cc9f12a03 | ||
|
|
0b5a751c56 | ||
|
|
5a59cd278c | ||
|
|
cdb05e5894 | ||
|
|
6c08b5caf3 | ||
|
|
d5b53f3712 | ||
|
|
80fbb2f0b2 | ||
|
|
b4f90decc7 | ||
|
|
61b324043a | ||
|
|
2ced921008 | ||
|
|
1e2fee0f33 | ||
|
|
ac36c9c406 | ||
|
|
f70c79aaf9 | ||
|
|
0839fa5757 | ||
|
|
60bc4ebcee | ||
|
|
65e239652d | ||
|
|
6b9015a928 | ||
|
|
a740c38ee6 | ||
|
|
603639126e | ||
|
|
043cfdb3f9 | ||
|
|
b0da4b2b2d | ||
|
|
5402859a30 | ||
|
|
2b10200101 | ||
|
|
55e8927ab7 | ||
|
|
d30fe61264 | ||
|
|
f7ea2d29e1 | ||
|
|
e6cfef5295 | ||
|
|
0dea9e6a2b | ||
|
|
ae80de26ec | ||
|
|
53bd2a0960 | ||
|
|
b55f5ff298 | ||
|
|
21048b51ae | ||
|
|
773fd09166 | ||
|
|
57532a290a | ||
|
|
740a35b018 | ||
|
|
6e995230a7 | ||
|
|
65a8b091b9 | ||
|
|
fa34908d82 | ||
|
|
b09eb1213c | ||
|
|
23ae8ab3b5 | ||
|
|
0bdb7e9e44 | ||
|
|
54e3cd7cb7 | ||
|
|
16ad07cfc9 | ||
|
|
dee0adcd8c | ||
|
|
acbf1226ab | ||
|
|
f002744d87 | ||
|
|
9efee7af21 | ||
|
|
9814b10496 | ||
|
|
978694b3c0 | ||
|
|
bc6f155f12 | ||
|
|
d494c2104b | ||
|
|
8035c6859f | ||
|
|
2f10295022 | ||
|
|
e5f32730a7 | ||
|
|
cd257b4d25 | ||
|
|
ce534baa75 | ||
|
|
2b420b8e66 | ||
|
|
35300abfb5 | ||
|
|
3d0ecc05ff | ||
|
|
bfb6ec8856 | ||
|
|
bf1d682a85 | ||
|
|
7d9326a147 | ||
|
|
7fd09009e7 | ||
|
|
9fa5e2dc1f |
-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>* Updated to MQTTnet 2.8.0.
|
||||
</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.0" />
|
||||
<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>* Updated to MQTTnet 2.8.0.
|
||||
</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.0" />
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
@@ -33,6 +32,9 @@
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\"/>
|
||||
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net462\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net470\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net471\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net472\"/>
|
||||
</files>
|
||||
</package>
|
||||
@@ -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>* Updated to MQTTnet 2.8.0.
|
||||
</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.0" />
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
@@ -34,5 +33,8 @@
|
||||
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net462\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net470\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net471\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net472\"/>
|
||||
</files>
|
||||
</package>
|
||||
+11
-29
@@ -10,33 +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> ** MQTTnet is now available at Open Collective for donations (https://opencollective.com/mqttnet). **
|
||||
* [Core] Performance optimizations.
|
||||
* [Core] Due to performance reasons the timestamp of log messages is now in UTC format.
|
||||
* [Core] Added several packet validations.
|
||||
* [Core] Log messages now contain the complete source path including parent components.
|
||||
* [Core] The adapter now has an _Endpoint_ definition as string containing remote IP and port.
|
||||
* [Client] Received messages are now processed completely in the worker thread without creating new Tasks.
|
||||
* [Client] Fixed wrong calculation for sending keep alive packets (thanks to @cstichlberger)
|
||||
* [Client] A clean disconnect (via DisconnectAsync) will no longer throw an exception.
|
||||
* [Client] Added new overloads for quick message publishing.
|
||||
* [ManagedClient] The managed client is moved to a separate nuget package.
|
||||
* [ManagedClient] Added an own message format with extended properties like ID (BREAKING CHANGE).
|
||||
* [ManagedClient] Fixed a loading issue of stored application messages (thanks to @JTrotta).
|
||||
* [ManagedClient] Added a new event which is fired when a synchronization of the subscriptions has failed.
|
||||
* [ManagedClient] Added a new event which is fired when a connection attempt has failed.
|
||||
* [ManagedClient] Exposed a new property which provides the count of not published messages (pending messages count).
|
||||
* [Server] Added support for other WebSocket sub protocol formats like mqttv-3.1.1 (thanks to @israellot).
|
||||
* [Server] The takeover of an existing client sessions is now treated as a _clean_ disconnect of the previous client.
|
||||
* [Server] The pending messages queue per client is now limited to 250 messages. Overflow strategy and count can be changed via options (thanks to @VladimirAkopyan)
|
||||
* [Server] Keep alive checking is now suspended while large packages are being received (and thus the client is connected). Keep alive checking continues after a large packet is received completely.
|
||||
* [Server] Rewritten the _ConnectedClients_ API and added new features for disconnecting and Endpoint information (IP etc.).
|
||||
* [Server] Added settings for disabling persistent sessions and defining a max pending messages queue size per session.
|
||||
* [Server] Persistent sessions are disabled by default (BREAKING CHANGE!).
|
||||
* [Server] Added a new interceptor which is invoked before a new message is added to the client queue.
|
||||
* [Server] Added support for Linux servers by dividing IPv4 and IPv6 support and adding new options (BREAKING CHANGE!).
|
||||
* [Server] Gracefully closed connections are no longer reported as warnings.
|
||||
* [Server] Added new overloads for initializing the ASP.NET Core integration.
|
||||
<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>
|
||||
@@ -45,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" />
|
||||
@@ -54,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>
|
||||
@@ -77,6 +56,9 @@
|
||||
<file src="..\Source\MQTTnet\bin\Release\net452\MQTTnet.*" target="lib\net452\"/>
|
||||
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net462\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net470\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net471\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net472\"/>
|
||||
</files>
|
||||
</package>
|
||||
+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
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<p align="center">
|
||||
<img src="https://github.com/chkr1011/MQTTnet/blob/master/Images/Logo_128x128.png?raw=true" width="128">
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
[](https://www.nuget.org/packages/MQTTnet)
|
||||
[](https://ci.appveyor.com/project/chkr1011/mqttnet/branch/develop)
|
||||
[](https://ci.appveyor.com/project/chkr1011/mqttnet)
|
||||
[](https://bettercodehub.com/)
|
||||
[](https://opencollective.com/mqttnet)
|
||||
[](https://opencollective.com/mqttnet)
|
||||
@@ -18,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_.
|
||||
|
||||
@@ -46,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
|
||||
|
||||
@@ -60,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
|
||||
|
||||
@@ -83,15 +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))
|
||||
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
|
||||
* 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,6 +3,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Linq;
|
||||
using MQTTnet.Server;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
@@ -23,10 +24,7 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
|
||||
{
|
||||
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
|
||||
subProtocol = requestedSubProtocolValues
|
||||
.OrderByDescending(p => p.Length)
|
||||
.FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
|
||||
subProtocol = SelectSubProtocol(requestedSubProtocolValues);
|
||||
}
|
||||
|
||||
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
|
||||
@@ -40,6 +38,14 @@ namespace MQTTnet.AspNetCore
|
||||
return app;
|
||||
}
|
||||
|
||||
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
|
||||
{
|
||||
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
|
||||
return requestedSubProtocolValues
|
||||
.OrderByDescending(p => p.Length)
|
||||
.FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
|
||||
}
|
||||
|
||||
public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action<IMqttServer> configure)
|
||||
{
|
||||
var server = app.ApplicationServices.GetRequiredService<IMqttServer>();
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using MQTTnet.Server;
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class AspNetMqttServerOptionsBuilder : MqttServerOptionsBuilder
|
||||
{
|
||||
public AspNetMqttServerOptionsBuilder(IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
}
|
||||
}
|
||||
@@ -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,20 +1,27 @@
|
||||
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)
|
||||
{
|
||||
var serializer = new MqttPacketSerializer();
|
||||
using (var adapter = new MqttConnectionContext(serializer, connection))
|
||||
// required for websocket transport to work
|
||||
var transferFormatFeature = connection.Features.Get<ITransferFormatFeature>();
|
||||
if (transferFormatFeature != null)
|
||||
{
|
||||
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
|
||||
}
|
||||
|
||||
using (var adapter = new MqttConnectionContext(new MqttPacketFormatterAdapter(), connection))
|
||||
{
|
||||
var args = new MqttServerAdapterClientAcceptedEventArgs(adapter);
|
||||
ClientAccepted?.Invoke(this, args);
|
||||
@@ -32,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;
|
||||
@@ -32,7 +35,8 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
|
||||
var bodySlice = copy.Slice(0, bodyLength);
|
||||
packet = serializer.Deserialize(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(bodySlice.GetArray(), 0)));
|
||||
var buffer = bodySlice.GetArray();
|
||||
packet = formatter.Decode(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(buffer, 0, buffer.Length), buffer.Length + 2));
|
||||
consumed = bodySlice.End;
|
||||
observed = bodySlice.End;
|
||||
return true;
|
||||
|
||||
@@ -20,14 +20,27 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
|
||||
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action<MqttServerOptionsBuilder> configure)
|
||||
{
|
||||
var builder = new MqttServerOptionsBuilder();
|
||||
services.AddSingleton<IMqttServerOptions>(s => {
|
||||
var builder = new MqttServerOptionsBuilder();
|
||||
configure(builder);
|
||||
return builder.Build();
|
||||
});
|
||||
|
||||
configure(builder);
|
||||
|
||||
services.AddSingleton<IMqttServerOptions>(builder.Build());
|
||||
services.AddHostedMqttServer();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
|
||||
{
|
||||
services.AddSingleton<IMqttServerOptions>(s => {
|
||||
var builder = new AspNetMqttServerOptionsBuilder(s);
|
||||
configure(builder);
|
||||
return builder.Build();
|
||||
});
|
||||
|
||||
services.AddHostedMqttServer();
|
||||
|
||||
@@ -44,7 +57,7 @@ namespace MQTTnet.AspNetCore
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
|
||||
private static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
|
||||
{
|
||||
var logger = new MqttNetLogger();
|
||||
var childLogger = logger.CreateChildLogger();
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -9,6 +11,12 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
TimeSpan AutoReconnectDelay { get; }
|
||||
|
||||
TimeSpan ConnectionCheckInterval { get; }
|
||||
|
||||
IManagedMqttClientStorage Storage { get; }
|
||||
|
||||
int MaxPendingMessages { get; }
|
||||
|
||||
MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461;uap10.0</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 />
|
||||
<Authors />
|
||||
|
||||
@@ -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(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
||||
await Task.Delay(Options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -226,9 +262,17 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
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();
|
||||
@@ -306,15 +370,15 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
try
|
||||
{
|
||||
if (subscriptions.Any())
|
||||
{
|
||||
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (unsubscriptions.Any())
|
||||
{
|
||||
await _mqttClient.UnsubscribeAsync(unsubscriptions).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (subscriptions.Any())
|
||||
{
|
||||
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -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)
|
||||
@@ -367,7 +431,6 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
_publishingCancellationToken = cts;
|
||||
|
||||
Task.Factory.StartNew(() => PublishQueuedMessages(cts.Token), cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
@@ -9,6 +11,12 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public TimeSpan AutoReconnectDelay { get; set; } = TimeSpan.FromSeconds(5);
|
||||
|
||||
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,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461;uap10.0</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 />
|
||||
<Authors />
|
||||
|
||||
@@ -3,7 +3,7 @@ _mqttClient.subscribe("MQTTnet.RPC/+/ping");
|
||||
_mqttClient.subscribe("MQTTnet.RPC/+/do_something");
|
||||
|
||||
// It is not allowed to change the structure of the topic. Otherwise RPC will not work. So method names can be separated using
|
||||
// an _ or . but no +, # or . If it is required to distinguish between devices own rules can be defined like the following.
|
||||
// an _ or . but no +, # or /. If it is required to distinguish between devices own rules can be defined like the following.
|
||||
_mqttClient.subscribe("MQTTnet.RPC/+/deviceA.ping");
|
||||
_mqttClient.subscribe("MQTTnet.RPC/+/deviceB.ping");
|
||||
_mqttClient.subscribe("MQTTnet.RPC/+/deviceC.getTemperature");
|
||||
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
using MQTTnet.Client;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Security.Authentication;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Serializer;
|
||||
using WebSocket4Net;
|
||||
|
||||
namespace MQTTnet.TestApp.NetCore
|
||||
{
|
||||
public class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
|
||||
{
|
||||
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
if (!(options.ChannelOptions is MqttClientWebSocketOptions))
|
||||
{
|
||||
throw new NotSupportedException("Only WebSocket connections are supported.");
|
||||
}
|
||||
|
||||
return new MqttChannelAdapter(new WebSocket4NetMqttChannel(options), new MqttPacketSerializer(), logger);
|
||||
}
|
||||
|
||||
private class WebSocket4NetMqttChannel : IMqttChannel
|
||||
{
|
||||
private readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
|
||||
|
||||
private readonly IMqttClientOptions _clientOptions;
|
||||
private WebSocket4Net.WebSocket _webSocket;
|
||||
|
||||
public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions)
|
||||
{
|
||||
_clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions));
|
||||
}
|
||||
|
||||
public string Endpoint { get; } = "";
|
||||
|
||||
public Task ConnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var channelOptions = (MqttClientWebSocketOptions)_clientOptions.ChannelOptions;
|
||||
|
||||
var uri = "ws://" + channelOptions.Uri;
|
||||
var sslProtocols = SslProtocols.None;
|
||||
|
||||
if (channelOptions.TlsOptions.UseTls)
|
||||
{
|
||||
uri = "wss://" + channelOptions.Uri;
|
||||
sslProtocols = SslProtocols.Tls12;
|
||||
}
|
||||
|
||||
var subProtocol = channelOptions.SubProtocols.FirstOrDefault() ?? string.Empty;
|
||||
|
||||
_webSocket = new WebSocket4Net.WebSocket(uri, subProtocol, sslProtocols: sslProtocols);
|
||||
_webSocket.DataReceived += OnDataReceived;
|
||||
_webSocket.Open();
|
||||
SpinWait.SpinUntil(() => _webSocket.State == WebSocketState.Open, _clientOptions.CommunicationTimeout);
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task DisconnectAsync()
|
||||
{
|
||||
if (_webSocket != null)
|
||||
{
|
||||
_webSocket.DataReceived -= OnDataReceived;
|
||||
_webSocket.Close();
|
||||
SpinWait.SpinUntil(() => _webSocket.State == WebSocketState.Closed, _clientOptions.CommunicationTimeout);
|
||||
}
|
||||
|
||||
_webSocket = null;
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
var readBytes = 0;
|
||||
while (count > 0 && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
byte @byte;
|
||||
if (readBytes == 0)
|
||||
{
|
||||
// Block until at lease one byte was received.
|
||||
@byte = _receiveBuffer.Take(cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_receiveBuffer.TryTake(out @byte))
|
||||
{
|
||||
return Task.FromResult(readBytes);
|
||||
}
|
||||
}
|
||||
|
||||
buffer[offset] = @byte;
|
||||
offset++;
|
||||
count--;
|
||||
readBytes++;
|
||||
}
|
||||
|
||||
return Task.FromResult(readBytes);
|
||||
}
|
||||
|
||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
_webSocket.Send(buffer, offset, count);
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_webSocket != null)
|
||||
{
|
||||
_webSocket.DataReceived -= OnDataReceived;
|
||||
_webSocket.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDataReceived(object sender, WebSocket4Net.DataReceivedEventArgs e)
|
||||
{
|
||||
foreach (var @byte in e.Data)
|
||||
{
|
||||
_receiveBuffer.Add(@byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,19 +92,17 @@ namespace MQTTnet.Adapter
|
||||
WrapException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
_logger.Verbose("TX >>> {0}", packet);
|
||||
|
||||
var packetData = PacketSerializer.Serialize(packet);
|
||||
|
||||
var packetData = PacketFormatterAdapter.Encode(packet);
|
||||
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
|
||||
PacketFormatterAdapter.FreeBuffer();
|
||||
|
||||
PacketSerializer.FreeBuffer();
|
||||
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -130,11 +129,11 @@ namespace MQTTnet.Adapter
|
||||
|
||||
if (timeout > TimeSpan.Zero)
|
||||
{
|
||||
receivedMqttPacket = await Internal.TaskExtensions.TimeoutAfterAsync(ct => ReceiveAsync(_channel, ct), timeout, cancellationToken).ConfigureAwait(false);
|
||||
receivedMqttPacket = await Internal.TaskExtensions.TimeoutAfterAsync(ReceiveAsync, timeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
receivedMqttPacket = await ReceiveAsync(_channel, cancellationToken).ConfigureAwait(false);
|
||||
receivedMqttPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (receivedMqttPacket == null || cancellationToken.IsCancellationRequested)
|
||||
@@ -142,16 +141,24 @@ 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;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (IsWrappedException(exception))
|
||||
@@ -165,9 +172,9 @@ namespace MQTTnet.Adapter
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<ReceivedMqttPacket> ReceiveAsync(IMqttChannel channel, CancellationToken cancellationToken)
|
||||
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
|
||||
{
|
||||
@@ -175,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];
|
||||
@@ -190,18 +197,22 @@ namespace MQTTnet.Adapter
|
||||
chunkSize = bytesLeft;
|
||||
}
|
||||
|
||||
// async/await is not used to avoid the overhead of context switches. We assume that the reamining data
|
||||
#if WINDOWS_UWP
|
||||
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 remaining data
|
||||
// has been sent from the sender directly after the initial bytes.
|
||||
var readBytes = channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).GetAwaiter().GetResult();
|
||||
if (readBytes <= 0)
|
||||
{
|
||||
ExceptionHelper.ThrowGracefulSocketClose();
|
||||
}
|
||||
var readBytes = _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
#endif
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ExceptionHelper.ThrowIfGracefulSocketClose(readBytes);
|
||||
|
||||
bodyOffset += readBytes;
|
||||
} while (bodyOffset < body.Length);
|
||||
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, new MqttPacketBodyReader(body, 0));
|
||||
var bodyReader = new MqttPacketBodyReader(body, 0, body.Length);
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, bodyReader, fixedHeader.TotalLength);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -236,7 +247,8 @@ namespace MQTTnet.Adapter
|
||||
{
|
||||
if (exception is IOException && exception.InnerException is SocketException socketException)
|
||||
{
|
||||
if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
|
||||
if (socketException.SocketErrorCode == SocketError.ConnectionAborted ||
|
||||
socketException.SocketErrorCode == SocketError.OperationAborted)
|
||||
{
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -7,5 +8,9 @@ namespace MQTTnet.Client
|
||||
IMqttClient CreateMqttClient();
|
||||
|
||||
IMqttClient CreateMqttClient(IMqttNetLogger logger);
|
||||
|
||||
IMqttClient CreateMqttClient(IMqttClientAdapterFactory adapterFactory);
|
||||
|
||||
IMqttClient CreateMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory adapterFactory);
|
||||
}
|
||||
}
|
||||
@@ -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,12 +30,12 @@ namespace MQTTnet.Client
|
||||
private readonly IMqttClientAdapterFactory _adapterFactory;
|
||||
private readonly IMqttNetChildLogger _logger;
|
||||
|
||||
private IMqttClientOptions _options;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private Task _packetReceiverTask;
|
||||
private Task _keepAliveMessageSenderTask;
|
||||
internal Task _packetReceiverTask;
|
||||
internal Task _keepAliveMessageSenderTask;
|
||||
private IMqttChannelAdapter _adapter;
|
||||
private bool _cleanDisconnectInitiated;
|
||||
private long _disconnectGate;
|
||||
|
||||
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
|
||||
{
|
||||
@@ -43,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)
|
||||
{
|
||||
@@ -53,45 +61,52 @@ namespace MQTTnet.Client
|
||||
|
||||
try
|
||||
{
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_options = options;
|
||||
Options = options;
|
||||
|
||||
_packetIdentifierProvider.Reset();
|
||||
_packetDispatcher.Reset();
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_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)
|
||||
{
|
||||
_logger.Error(exception, "Error while connecting with server.");
|
||||
await DisconnectInternalAsync(null, exception).ConfigureAwait(false);
|
||||
|
||||
if (!DisconnectIsPending())
|
||||
{
|
||||
await DisconnectInternalAsync(null, exception).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync()
|
||||
public async Task DisconnectAsync(MqttClientDisconnectOptions options)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -99,16 +114,20 @@ 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
|
||||
{
|
||||
await DisconnectInternalAsync(null, null).ConfigureAwait(false);
|
||||
if (!DisconnectIsPending())
|
||||
{
|
||||
await DisconnectInternalAsync(null, null).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
@@ -116,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));
|
||||
|
||||
@@ -138,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:
|
||||
{
|
||||
@@ -176,36 +203,35 @@ namespace MQTTnet.Client
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel(false);
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
|
||||
_adapter?.Dispose();
|
||||
_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)
|
||||
@@ -215,21 +241,22 @@ namespace MQTTnet.Client
|
||||
|
||||
private async Task DisconnectInternalAsync(Task sender, Exception exception)
|
||||
{
|
||||
var clientWasConnected = IsConnected;
|
||||
|
||||
InitiateDisconnect();
|
||||
|
||||
var clientWasConnected = IsConnected;
|
||||
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)
|
||||
@@ -238,10 +265,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
finally
|
||||
{
|
||||
_adapter?.Dispose();
|
||||
_adapter = null;
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
Dispose();
|
||||
_cleanDisconnectInitiated = false;
|
||||
|
||||
_logger.Info("Disconnected.");
|
||||
@@ -255,16 +279,16 @@ namespace MQTTnet.Client
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_cancellationTokenSource == null || _cancellationTokenSource.IsCancellationRequested)
|
||||
if (_cancellationTokenSource?.IsCancellationRequested == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_cancellationTokenSource.Cancel(false);
|
||||
_cancellationTokenSource?.Cancel(false);
|
||||
}
|
||||
catch (Exception adapterException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Warning(adapterException, "Error while initiating disconnect.");
|
||||
_logger.Warning(exception, "Error while initiating disconnect.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,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)
|
||||
{
|
||||
@@ -311,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;
|
||||
@@ -352,7 +376,10 @@ namespace MQTTnet.Client
|
||||
_logger.Error(exception, "Unhandled exception while sending/receiving keep alive packets.");
|
||||
}
|
||||
|
||||
await DisconnectInternalAsync(_keepAliveMessageSenderTask, exception).ConfigureAwait(false);
|
||||
if (!DisconnectIsPending())
|
||||
{
|
||||
await DisconnectInternalAsync(_keepAliveMessageSenderTask, exception).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -362,14 +389,15 @@ 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);
|
||||
if (packet != null)
|
||||
|
||||
if (packet != null && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
await ProcessReceivedPacketAsync(packet, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
@@ -395,7 +423,11 @@ namespace MQTTnet.Client
|
||||
}
|
||||
|
||||
_packetDispatcher.Dispatch(exception);
|
||||
await DisconnectInternalAsync(_packetReceiverTask, exception).ConfigureAwait(false);
|
||||
|
||||
if (!DisconnectIsPending())
|
||||
{
|
||||
await DisconnectInternalAsync(_packetReceiverTask, exception).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -417,7 +449,7 @@ namespace MQTTnet.Client
|
||||
|
||||
if (packet is MqttDisconnectPacket)
|
||||
{
|
||||
return DisconnectAsync();
|
||||
return DisconnectAsync(null);
|
||||
}
|
||||
|
||||
if (packet is MqttPubRelPacket pubRelPacket)
|
||||
@@ -440,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.");
|
||||
@@ -457,45 +489,59 @@ 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)
|
||||
{
|
||||
_packetReceiverTask = Task.Run(
|
||||
_packetReceiverTask = Task.Factory.StartNew(
|
||||
() => ReceivePacketsAsync(cancellationToken),
|
||||
cancellationToken);
|
||||
cancellationToken,
|
||||
TaskCreationOptions.LongRunning,
|
||||
TaskScheduler.Default).Unwrap();
|
||||
}
|
||||
|
||||
private void StartSendingKeepAliveMessages(CancellationToken cancellationToken)
|
||||
{
|
||||
_keepAliveMessageSenderTask = Task.Run(
|
||||
_keepAliveMessageSenderTask = Task.Factory.StartNew(
|
||||
() => SendKeepAliveMessagesAsync(cancellationToken),
|
||||
cancellationToken);
|
||||
cancellationToken,
|
||||
TaskCreationOptions.LongRunning,
|
||||
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)
|
||||
{
|
||||
@@ -523,5 +569,36 @@ namespace MQTTnet.Client
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private bool DisconnectIsPending()
|
||||
{
|
||||
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,141 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MQTTnet.Serializer;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientOptionsBuilder
|
||||
{
|
||||
private readonly MqttClientOptions _options = new MqttClientOptions();
|
||||
private MqttClientTcpOptions _tcpOptions;
|
||||
private MqttClientWebSocketOptions _webSocketOptions;
|
||||
|
||||
private MqttClientTlsOptions _tlsOptions;
|
||||
|
||||
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
|
||||
{
|
||||
_options.ProtocolVersion = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCommunicationTimeout(TimeSpan value)
|
||||
{
|
||||
_options.CommunicationTimeout = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCleanSession(bool value = true)
|
||||
{
|
||||
_options.CleanSession = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithKeepAlivePeriod(TimeSpan value)
|
||||
{
|
||||
_options.KeepAlivePeriod = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithKeepAliveSendInterval(TimeSpan value)
|
||||
{
|
||||
_options.KeepAliveSendInterval = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithClientId(string value)
|
||||
{
|
||||
_options.ClientId = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillMessage(MqttApplicationMessage value)
|
||||
{
|
||||
_options.WillMessage = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCredentials(string username, string password = null)
|
||||
{
|
||||
_options.Credentials = new MqttClientCredentials
|
||||
{
|
||||
Username = username,
|
||||
Password = password
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTcpServer(string server, int? port = null)
|
||||
{
|
||||
_tcpOptions = new MqttClientTcpOptions
|
||||
{
|
||||
Server = server,
|
||||
Port = port
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWebSocketServer(string uri)
|
||||
{
|
||||
_webSocketOptions = new MqttClientWebSocketOptions
|
||||
{
|
||||
Uri = uri
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTls(
|
||||
bool allowUntrustedCertificates = false,
|
||||
bool ignoreCertificateChainErrors = false,
|
||||
bool ignoreCertificateRevocationErrors = false,
|
||||
params byte[][] certificates)
|
||||
{
|
||||
_tlsOptions = new MqttClientTlsOptions
|
||||
{
|
||||
UseTls = true,
|
||||
AllowUntrustedCertificates = allowUntrustedCertificates,
|
||||
IgnoreCertificateChainErrors = ignoreCertificateChainErrors,
|
||||
IgnoreCertificateRevocationErrors = ignoreCertificateRevocationErrors,
|
||||
Certificates = certificates?.ToList()
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTls()
|
||||
{
|
||||
_tlsOptions = new MqttClientTlsOptions
|
||||
{
|
||||
UseTls = true
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IMqttClientOptions Build()
|
||||
{
|
||||
if (_tlsOptions != null)
|
||||
{
|
||||
if (_tcpOptions == null && _webSocketOptions == null)
|
||||
{
|
||||
throw new InvalidOperationException("A channel (TCP or WebSocket) must be set if TLS is configured.");
|
||||
}
|
||||
|
||||
if (_tcpOptions != null)
|
||||
{
|
||||
_tcpOptions.TlsOptions = _tlsOptions;
|
||||
}
|
||||
else if (_webSocketOptions != null)
|
||||
{
|
||||
_webSocketOptions.TlsOptions = _tlsOptions;
|
||||
}
|
||||
}
|
||||
|
||||
_options.ChannelOptions = (IMqttClientChannelOptions)_tcpOptions ?? _webSocketOptions;
|
||||
|
||||
return _options;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientOptionsBuilder
|
||||
{
|
||||
private readonly MqttClientOptions _options = new MqttClientOptions();
|
||||
|
||||
private MqttClientTcpOptions _tcpOptions;
|
||||
private MqttClientWebSocketOptions _webSocketOptions;
|
||||
private MqttClientOptionsBuilderTlsParameters _tlsParameters;
|
||||
private MqttClientWebSocketProxyOptions _proxyOptions;
|
||||
|
||||
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
|
||||
{
|
||||
_options.ProtocolVersion = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCommunicationTimeout(TimeSpan value)
|
||||
{
|
||||
_options.CommunicationTimeout = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCleanSession(bool value = true)
|
||||
{
|
||||
_options.CleanSession = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithKeepAlivePeriod(TimeSpan value)
|
||||
{
|
||||
_options.KeepAlivePeriod = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithKeepAliveSendInterval(TimeSpan value)
|
||||
{
|
||||
_options.KeepAliveSendInterval = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithClientId(string value)
|
||||
{
|
||||
_options.ClientId = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillMessage(MqttApplicationMessage value)
|
||||
{
|
||||
_options.WillMessage = value;
|
||||
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
|
||||
{
|
||||
Username = username,
|
||||
Password = password
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTcpServer(string server, int? port = null)
|
||||
{
|
||||
_tcpOptions = new MqttClientTcpOptions
|
||||
{
|
||||
Server = server,
|
||||
Port = port
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithProxy(string address, string username = null, string password = null, string domain = null, bool bypassOnLocal = false, string[] bypassList = null)
|
||||
{
|
||||
_proxyOptions = new MqttClientWebSocketProxyOptions
|
||||
{
|
||||
Address = address,
|
||||
Username = username,
|
||||
Password = password,
|
||||
Domain = domain,
|
||||
BypassOnLocal = bypassOnLocal,
|
||||
BypassList = bypassList
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWebSocketServer(string uri, MqttClientOptionsBuilderWebSocketParameters parameters = null)
|
||||
{
|
||||
_webSocketOptions = new MqttClientWebSocketOptions
|
||||
{
|
||||
Uri = uri,
|
||||
RequestHeaders = parameters?.RequestHeaders,
|
||||
CookieContainer = parameters?.CookieContainer
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTls(MqttClientOptionsBuilderTlsParameters parameters)
|
||||
{
|
||||
_tlsParameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTls()
|
||||
{
|
||||
return WithTls(new MqttClientOptionsBuilderTlsParameters { UseTls = true });
|
||||
}
|
||||
|
||||
[Obsolete("Use method _WithTls_ which accepts the _MqttClientOptionsBuilderTlsParameters_.")]
|
||||
public MqttClientOptionsBuilder WithTls(
|
||||
bool allowUntrustedCertificates = false,
|
||||
bool ignoreCertificateChainErrors = false,
|
||||
bool ignoreCertificateRevocationErrors = false,
|
||||
params byte[][] certificates)
|
||||
{
|
||||
_tlsParameters = new MqttClientOptionsBuilderTlsParameters
|
||||
{
|
||||
UseTls = true,
|
||||
AllowUntrustedCertificates = allowUntrustedCertificates,
|
||||
IgnoreCertificateChainErrors = ignoreCertificateChainErrors,
|
||||
IgnoreCertificateRevocationErrors = ignoreCertificateRevocationErrors,
|
||||
Certificates = certificates?.ToList()
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IMqttClientOptions Build()
|
||||
{
|
||||
if (_tcpOptions == null && _webSocketOptions == null)
|
||||
{
|
||||
throw new InvalidOperationException("A channel must be set.");
|
||||
}
|
||||
|
||||
if (_tlsParameters != null)
|
||||
{
|
||||
if (_tlsParameters?.UseTls == true)
|
||||
{
|
||||
var tlsOptions = new MqttClientTlsOptions
|
||||
{
|
||||
UseTls = true,
|
||||
SslProtocol = _tlsParameters.SslProtocol,
|
||||
AllowUntrustedCertificates = _tlsParameters.AllowUntrustedCertificates,
|
||||
Certificates = _tlsParameters.Certificates?.Select(c => c.ToArray()).ToList(),
|
||||
CertificateValidationCallback = _tlsParameters.CertificateValidationCallback,
|
||||
IgnoreCertificateChainErrors = _tlsParameters.IgnoreCertificateChainErrors,
|
||||
IgnoreCertificateRevocationErrors = _tlsParameters.IgnoreCertificateRevocationErrors
|
||||
};
|
||||
|
||||
if (_tcpOptions != null)
|
||||
{
|
||||
_tcpOptions.TlsOptions = tlsOptions;
|
||||
}
|
||||
else if (_webSocketOptions != null)
|
||||
{
|
||||
_webSocketOptions.TlsOptions = tlsOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_proxyOptions != null)
|
||||
{
|
||||
if (_webSocketOptions == null)
|
||||
{
|
||||
throw new InvalidOperationException("Proxies are only supported for WebSocket connections.");
|
||||
}
|
||||
|
||||
_webSocketOptions.ProxyOptions = _proxyOptions;
|
||||
}
|
||||
|
||||
_options.ChannelOptions = (IMqttClientChannelOptions)_tcpOptions ?? _webSocketOptions;
|
||||
|
||||
return _options;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientOptionsBuilderTlsParameters
|
||||
{
|
||||
public bool UseTls { get; set; }
|
||||
|
||||
public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
|
||||
|
||||
public IEnumerable<IEnumerable<byte>> Certificates { get; set; }
|
||||
|
||||
public bool AllowUntrustedCertificates { get; set; }
|
||||
|
||||
public bool IgnoreCertificateChainErrors { get; set; }
|
||||
|
||||
public bool IgnoreCertificateRevocationErrors { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
+4
-1
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientTlsOptions
|
||||
{
|
||||
@@ -17,6 +18,8 @@ namespace MQTTnet.Client
|
||||
|
||||
public List<byte[]> Certificates { get; set; }
|
||||
|
||||
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
|
||||
|
||||
public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback { get; set; }
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientWebSocketOptions : IMqttClientChannelOptions
|
||||
{
|
||||
@@ -13,6 +13,8 @@ namespace MQTTnet.Client
|
||||
|
||||
public CookieContainer CookieContainer { get; set; }
|
||||
|
||||
public MqttClientWebSocketProxyOptions ProxyOptions { get; set; }
|
||||
|
||||
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
||||
|
||||
public override string ToString()
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientWebSocketProxyOptions
|
||||
{
|
||||
public string Address { get; set; }
|
||||
|
||||
public string Username { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public string Domain { get; set; }
|
||||
|
||||
public bool BypassOnLocal { get; set; }
|
||||
|
||||
public string[] BypassList { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public class MqttPacketReader
|
||||
{
|
||||
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.
|
||||
// async/await is used here because the next packet is received in a couple of minutes so the performance
|
||||
// impact is acceptable according to a useless waiting thread.
|
||||
var buffer = fixedHeaderBuffer;
|
||||
var totalBytesRead = 0;
|
||||
|
||||
while (totalBytesRead < buffer.Length)
|
||||
{
|
||||
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ExceptionHelper.ThrowIfGracefulSocketClose(bytesRead);
|
||||
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
|
||||
var hasRemainingLength = buffer[1] != 0;
|
||||
if (!hasRemainingLength)
|
||||
{
|
||||
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(buffer[1], cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
// 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(buffer[1], cancellationToken);
|
||||
#endif
|
||||
|
||||
totalBytesRead += bodyLength;
|
||||
return new MqttFixedHeader(buffer[0], bodyLength, totalBytesRead);
|
||||
}
|
||||
|
||||
#if !WINDOWS_UWP
|
||||
private int ReadBodyLength(byte initialEncodedByte, CancellationToken cancellationToken)
|
||||
{
|
||||
var offset = 0;
|
||||
var multiplier = 128;
|
||||
var value = (initialEncodedByte & 127);
|
||||
int encodedByte = initialEncodedByte;
|
||||
|
||||
while ((encodedByte & 128) != 0)
|
||||
{
|
||||
offset++;
|
||||
if (offset > 3)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Remaining length is invalid.");
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
encodedByte = ReadByte(cancellationToken);
|
||||
|
||||
value += (encodedByte & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private byte ReadByte(CancellationToken cancellationToken)
|
||||
{
|
||||
var readCount = _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (readCount <= 0)
|
||||
{
|
||||
ExceptionHelper.ThrowGracefulSocketClose();
|
||||
}
|
||||
|
||||
return _singleByteBuffer[0];
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
private async Task<int> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken)
|
||||
{
|
||||
var offset = 0;
|
||||
var multiplier = 128;
|
||||
var value = initialEncodedByte & 127;
|
||||
int encodedByte = initialEncodedByte;
|
||||
|
||||
while ((encodedByte & 128) != 0)
|
||||
{
|
||||
offset++;
|
||||
if (offset > 3)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Remaining length is invalid.");
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
encodedByte = await ReadByteAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
value += (encodedByte & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private async Task<byte> ReadByteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (readCount <= 0)
|
||||
{
|
||||
ExceptionHelper.ThrowGracefulSocketClose();
|
||||
}
|
||||
|
||||
return _singleByteBuffer[0];
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+50
-22
@@ -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,10 +28,9 @@ namespace MQTTnet.Serializer
|
||||
return (byte)fixedHeader;
|
||||
}
|
||||
|
||||
public static ArraySegment<byte> EncodeRemainingLength(int length)
|
||||
public static ArraySegment<byte> EncodeVariableByteInteger(uint value)
|
||||
{
|
||||
// write the encoded remaining length right aligned on the 4 byte buffer
|
||||
if (length <= 0)
|
||||
if (value <= 0)
|
||||
{
|
||||
return new ArraySegment<byte>(new byte[1], 0, 1);
|
||||
}
|
||||
@@ -38,8 +38,7 @@ namespace MQTTnet.Serializer
|
||||
var buffer = new byte[4];
|
||||
var bufferOffset = 0;
|
||||
|
||||
// Algorithm taken from http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html.
|
||||
var x = length;
|
||||
var x = value;
|
||||
do
|
||||
{
|
||||
var encodedByte = x % 128;
|
||||
@@ -56,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));
|
||||
@@ -63,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);
|
||||
}
|
||||
|
||||
@@ -81,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()
|
||||
@@ -129,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;
|
||||
@@ -140,6 +166,7 @@ namespace MQTTnet.Serializer
|
||||
EnsureCapacity(_buffer.Length + additionalCapacity - freeSpace);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void EnsureCapacity(int capacity)
|
||||
{
|
||||
var newBufferLength = _buffer.Length;
|
||||
@@ -148,7 +175,7 @@ namespace MQTTnet.Serializer
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
while (newBufferLength < capacity)
|
||||
{
|
||||
newBufferLength *= 2;
|
||||
@@ -157,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
|
||||
@@ -154,7 +154,11 @@ namespace MQTTnet.Implementations
|
||||
|
||||
private void CreateStreams()
|
||||
{
|
||||
_readStream = _socket.InputStream.AsStreamForRead(_bufferSize);
|
||||
// Attention! Do not set the buffer for the read method. This will
|
||||
// limit the internal buffer and the read operation will hang forever
|
||||
// if more data than the buffer size was received.
|
||||
_readStream = _socket.InputStream.AsStreamForRead();
|
||||
|
||||
_writeStream = _socket.OutputStream.AsStreamForWrite(_bufferSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -63,7 +62,7 @@ namespace MQTTnet.Implementations
|
||||
if (_options.TlsOptions.UseTls)
|
||||
{
|
||||
sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback);
|
||||
await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
||||
await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), _options.TlsOptions.SslProtocol, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
CreateStream(sslStream);
|
||||
@@ -87,8 +86,15 @@ namespace MQTTnet.Implementations
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
TryDispose(_stream, () => _stream = null);
|
||||
TryDispose(_socket, () => _socket = null);
|
||||
Cleanup(ref _stream, s => s.Dispose());
|
||||
Cleanup(ref _socket, s =>
|
||||
{
|
||||
if (s.Connected)
|
||||
{
|
||||
s.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
s.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
private bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
@@ -96,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.
|
||||
@@ -110,7 +116,7 @@ namespace MQTTnet.Implementations
|
||||
return true;
|
||||
}
|
||||
|
||||
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.RevocationStatusUnknown))
|
||||
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.OfflineRevocation))
|
||||
{
|
||||
if (!_options.TlsOptions.IgnoreCertificateRevocationErrors)
|
||||
{
|
||||
@@ -157,11 +163,16 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryDispose(IDisposable disposable, Action afterDispose)
|
||||
private static void Cleanup<T>(ref T item, Action<T> handler) where T : class
|
||||
{
|
||||
var temp = item;
|
||||
item = null;
|
||||
try
|
||||
{
|
||||
disposable?.Dispose();
|
||||
if (temp != null)
|
||||
{
|
||||
handler(temp);
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
@@ -169,10 +180,6 @@ namespace MQTTnet.Implementations
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
afterDispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#if !WINDOWS_UWP
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
@@ -79,27 +80,33 @@ namespace MQTTnet.Implementations
|
||||
|
||||
private void RegisterListeners(MqttServerTcpEndpointBaseOptions options, X509Certificate2 tlsCertificate)
|
||||
{
|
||||
var listenerV4 = new MqttTcpServerListener(
|
||||
AddressFamily.InterNetwork,
|
||||
options,
|
||||
tlsCertificate,
|
||||
_cancellationTokenSource.Token,
|
||||
_logger);
|
||||
if (!options.BoundInterNetworkAddress.Equals(IPAddress.None))
|
||||
{
|
||||
var listenerV4 = new MqttTcpServerListener(
|
||||
AddressFamily.InterNetwork,
|
||||
options,
|
||||
tlsCertificate,
|
||||
_cancellationTokenSource.Token,
|
||||
_logger);
|
||||
|
||||
listenerV4.ClientAccepted += OnClientAccepted;
|
||||
listenerV4.Start();
|
||||
_listeners.Add(listenerV4);
|
||||
listenerV4.ClientAccepted += OnClientAccepted;
|
||||
listenerV4.Start();
|
||||
_listeners.Add(listenerV4);
|
||||
}
|
||||
|
||||
var listenerV6 = new MqttTcpServerListener(
|
||||
AddressFamily.InterNetworkV6,
|
||||
options,
|
||||
tlsCertificate,
|
||||
_cancellationTokenSource.Token,
|
||||
_logger);
|
||||
if (!options.BoundInterNetworkV6Address.Equals(IPAddress.None))
|
||||
{
|
||||
var listenerV6 = new MqttTcpServerListener(
|
||||
AddressFamily.InterNetworkV6,
|
||||
options,
|
||||
tlsCertificate,
|
||||
_cancellationTokenSource.Token,
|
||||
_logger);
|
||||
|
||||
listenerV6.ClientAccepted += OnClientAccepted;
|
||||
listenerV6.Start();
|
||||
_listeners.Add(listenerV6);
|
||||
listenerV6.ClientAccepted += OnClientAccepted;
|
||||
listenerV6.Start();
|
||||
_listeners.Add(listenerV6);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClientAccepted(object sender, MqttServerAdapterClientAcceptedEventArgs e)
|
||||
|
||||
@@ -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,13 +80,15 @@ 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);
|
||||
}
|
||||
|
||||
var protocol = _addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6";
|
||||
_logger.Verbose($"Client '{clientSocket.RemoteEndPoint}' accepted by TCP listener '{_socket.LocalEndPoint}, {protocol}'.");
|
||||
_logger.Verbose("Client '{0}' accepted by TCP listener '{1}, {2}'.",
|
||||
clientSocket.RemoteEndPoint,
|
||||
_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)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.WebSockets;
|
||||
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
|
||||
{
|
||||
@@ -14,7 +15,7 @@ namespace MQTTnet.Implementations
|
||||
private readonly MqttClientWebSocketOptions _options;
|
||||
|
||||
private WebSocket _webSocket;
|
||||
|
||||
|
||||
public MqttWebSocketChannel(MqttClientWebSocketOptions options)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
@@ -45,6 +46,11 @@ namespace MQTTnet.Implementations
|
||||
|
||||
var clientWebSocket = new ClientWebSocket();
|
||||
|
||||
if (_options.ProxyOptions != null)
|
||||
{
|
||||
clientWebSocket.Options.Proxy = CreateProxy();
|
||||
}
|
||||
|
||||
if (_options.RequestHeaders != null)
|
||||
{
|
||||
foreach (var requestHeader in _options.RequestHeaders)
|
||||
@@ -132,5 +138,29 @@ namespace MQTTnet.Implementations
|
||||
_webSocket = null;
|
||||
}
|
||||
}
|
||||
|
||||
private IWebProxy CreateProxy()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_options.ProxyOptions?.Address))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#if WINDOWS_UWP
|
||||
throw new NotSupportedException("Proxies are not supported in UWP.");
|
||||
#elif NETSTANDARD1_3
|
||||
throw new NotSupportedException("Proxies are not supported in netstandard 1.3.");
|
||||
#else
|
||||
var proxyUri = new Uri(_options.ProxyOptions.Address);
|
||||
|
||||
if (!string.IsNullOrEmpty(_options.ProxyOptions.Username) && !string.IsNullOrEmpty(_options.ProxyOptions.Password))
|
||||
{
|
||||
var credentials = new NetworkCredential(_options.ProxyOptions.Username, _options.ProxyOptions.Password, _options.ProxyOptions.Domain);
|
||||
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList, credentials);
|
||||
}
|
||||
|
||||
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
+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)
|
||||
{
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user