Compare commits

...
93 Commits
Author SHA1 Message Date
Christian 38f2453c92 Update README.md 2018-12-22 20:25:53 +01:00
Christian Kratky 058bb65d31 Merge from develop. 2018-12-22 09:40:47 +01:00
Christian Kratky 38b215e9b5 Merge branch 'develop' into feature/MQTTv5 2018-12-22 09:38:12 +01:00
Christian Kratky 45df33c9a5 Update nuspec. 2018-12-22 09:33:44 +01:00
Christian Kratky 89becaa191 Fix build script. 2018-12-22 09:28:51 +01:00
Christian Kratky 6c87e4e039 Replace nuget version with placeholder. 2018-12-22 09:26:52 +01:00
Christian Kratky cbe06d6b87 Merge from master. 2018-12-21 00:21:42 +01:00
Christian Kratky 74e4fd8b3f Update test certificate. 2018-12-20 20:54:37 +01:00
Christian Kratky cc113a6c34 Refactor namespaces. 2018-12-20 20:53:29 +01:00
Christian fca9876111 Delete .travis.yml 2018-12-19 00:10:10 +01:00
Christian ac9f6a36f8 Update appveyor.yml 2018-12-19 00:06:51 +01:00
Christian 929b60e044 Update appveyor.yml 2018-12-18 21:32:46 +01:00
Christian acb433dd4c Update appveyor.yml 2018-12-18 20:23:18 +01:00
Christian c4e8bf7f39 Update appveyor.yml 2018-12-18 20:12:09 +01:00
Christian ba4fdfb51c Merge pull request #497 from cquesnel/feature/add-websocket-parameters
Add custom WebSocket parameters
2018-12-14 21:37:04 +01:00
Charles Quesnel dfda916283 Use default parameter instead of overload 2018-12-13 14:54:31 -05:00
Charles Quesnel 40326df9a9 Fix typo in 'Cookie' 2018-12-13 14:08:53 -05:00
Charles Quesnel a94444098a Remove unused usings 2018-12-13 13:01:34 -05:00
Charles Quesnel 0c13e7a504 Add overload to allow passing custom request headers and cookies when connecting with WS 2018-12-13 12:56:47 -05:00
Christian Kratky 0dba57fe75 Cleanup code. 2018-12-09 21:04:14 +01:00
Christian Kratky 6483f896c1 Merge from develop. 2018-12-09 20:48:46 +01:00
Christian Kratky 8fa9943c3d Merge branch 'master' into develop 2018-12-09 19:44:17 +01:00
Christian Kratky 195357835b Merge branch 'master' of https://github.com/chkr1011/MQTTnet 2018-12-09 19:42:03 +01:00
Christian Kratky 28b2562112 Fix client session disposal in server. 2018-12-09 17:35:46 +01:00
Christian Kratky ab24368de1 Update docs. 2018-12-08 13:46:40 +01:00
Christian Kratky 9b51404b4d Add Unit Tests. 2018-12-06 21:35:42 +01:00
Christian Kratky 619eaf284d Add readonly property for client options. 2018-12-03 21:20:11 +01:00
Christian Kratky 86c348df58 Stop existing sessions before takeover from another client. 2018-12-02 21:50:16 +01:00
Christian Kratky fdb2ec897c Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-12-01 11:00:09 +01:00
Christian Kratky b091fee554 Reset the keep alive monitor if a session is being resumed from a different (or same) client. 2018-12-01 11:00:00 +01:00
Christian 14ec6ee793 Merge pull request #484 from fogzot/feature-msbuild-conditions
Feature msbuild conditions
2018-12-01 10:50:39 +01:00
Federico Di Gregorio 0cfef0649a Fixed appveyor configuration to use full solution 2018-11-30 09:38:10 +01:00
Federico Di Gregorio 7292af7db4 Added solution that does not include UWP 2018-11-30 09:26:22 +01:00
Federico Di Gregorio bc1a197c4f Align conditions in dependent project with conditions in MQTTnet 2018-11-30 09:26:02 +01:00
Christian Kratky ccc275902f Fix wrong retain flag when distributing messages. 2018-11-28 21:35:50 +01:00
Christian Kratky 0f5fb8aa3a Add more pacakges. 2018-11-28 21:11:23 +01:00
Christian Kratky 83eca982e9 Update nugets. 2018-11-24 20:21:05 +01:00
Christian Kratky 017f17a6b1 Fix UnitTests. 2018-11-24 20:19:16 +01:00
Christian Kratky b3564e01a4 Update docs. 2018-11-23 22:57:34 +01:00
Christian Kratky 43105f71d8 Refactor removal from blocking queue. 2018-11-23 22:56:10 +01:00
Christian Kratky 40c408a9c0 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-11-23 22:51:07 +01:00
Christian Kratky 7baff7c080 Refactor property definition for packets. 2018-11-23 22:50:44 +01:00
Christian 91a918d30b Merge pull request #479 from PaulFake/patch-1
Fix for two issues with the managed message queue cap
2018-11-23 22:48:40 +01:00
Paul Fake 2a93fc18e4 Made comparison function static 2018-11-21 21:44:26 -05:00
Paul Fake 31de6ee186 Delegate comparison function 2018-11-21 21:39:42 -05:00
Paul Fake 74d59673a8 Delegate for comparisons 2018-11-21 21:38:06 -05:00
Paul Fake d1c3d9b453 Removed unnecessary code 2018-11-21 21:37:01 -05:00
Paul Fake baa9241216 Revert "Changes to this file no longer needed"
This reverts commit 2c6c3ac6a4.
2018-11-21 21:35:15 -05:00
Paul Fake 2c6c3ac6a4 Changes to this file no longer needed 2018-11-21 20:47:12 -05:00
Paul Fake b2c31331b6 Peek-and-publish model
We had been seeing an issue in which the queue could grow larger than the configured cap.  I examined the code and saw that this could happen if _mqttClient.PublishAsync() throws an exception, in which case a message can be re-enqueued without honoring the cap.  Furthermore, I saw that it was possible for the DropOldestQueuedMessage strategy to drop messages that were not actually the oldest ones, because when re-enqueueing the messages in the queue are no longer ordered by the original time they entered the queue.  It made sense to us to peek at the message when publishing rather than dequeue it, so that when re-enqueueing after an exception 1) the cap is still honored and 2) the order of queued messages isn't altered.  It's ok if another thread removes the message that's currently being published from the queue due to the cap, because all we have to do then is check if it's already been removed before removing it ourselves.
2018-11-19 21:20:50 -05:00
Paul Fake 9ff39c1fd9 New functions
This is to support the peek-and-publish model for the managed client
2018-11-19 21:11:47 -05:00
Paul Fake 210d57b254 IEquatable for speed
IEquatable for speed in using EqualityComparer<TItem>.Default.Equals
2018-11-19 21:09:36 -05:00
Christian Kratky 7292cedd2f Encode ConnAck packet. 2018-11-19 00:17:19 +01:00
Christian Kratky 48d5afa799 Refactor namings of serializers etc. 2018-11-19 00:14:15 +01:00
Christian Kratky 3db22eea51 Remove dedicated V3 messages. 2018-11-18 22:04:39 +01:00
Christian Kratky 2fbb44ec66 Add more property implementations. 2018-11-18 21:54:40 +01:00
Christian Kratky d7b98080f5 Refactor unit test for server events. 2018-11-18 11:20:36 +01:00
Christian Kratky 3e7b41e712 Refactor code. 2018-11-17 20:24:23 +01:00
Christian Kratky 5f18f35e02 Refactor task usage in client. 2018-11-17 20:23:52 +01:00
Christian Kratky af39e87703 Refactor code. 2018-11-17 20:23:20 +01:00
Christian Kratky ace25af0e8 Added package properties. 2018-11-16 23:11:26 +01:00
Christian Kratky 8d78455414 Fix breaking changes. 2018-11-16 22:18:59 +01:00
Christian Kratky fed16d7710 Merge from develop. 2018-11-16 22:16:21 +01:00
Christian Kratky fcff3a2c8d Update docs. 2018-11-16 22:12:29 +01:00
Christian 803dd6f64e Merge pull request #476 from PaulFake/patch-1
Check for pending disconnect in ThrowIfNotConnected
2018-11-16 22:08:49 +01:00
Paul Fake a1e258eb24 Check for pending disconnect in ThrowIfNotConnected
This might be a little controversial, but it worked for us to correct a problem in which messages get stuck in the managed client storage queue (and are thrown out of the regular message queue without being published!) in the case of a failed connection.  What we were seeing was that ManagedMqttClient.TryPublishQueuedMessage() was discarding a dequeued message without ever removing it from the storage queue because of an OperationCancelledException thrown by MqttClient.PublishAsync().  Tracing the code back, we found that when the connection is interrupted, after the timeout period MqttClient.InitiateDisconnect() would set the cancellation token, and the managed client would continue to try to publish, eventually calling through to MqttClient.SendAndReceiveAsync(), which would throw because the cancellation token is set.  Looking back over the code, we saw that MqttClient.PublishAsync() has a call to ThrowIfNotConnected() at the top, which told us that the intent was to not allow this function to be called after a disconnect.  But the disconnect was still pending, and the function wasn't behaving correctly in this state, so we reasoned that it's best to throw if the disconnect is pending.
2018-11-14 11:25:12 -05:00
Christian Kratky e9df698a1f Update nuget versions. 2018-11-05 22:40:48 +01:00
Christian Kratky bddcbf6999 Refactor code. 2018-11-05 22:39:56 +01:00
Christian be22e2551d Merge pull request #471 from JanEggers/develop
updated nuget spec according to csproj
2018-11-05 22:24:31 +01:00
Jan Eggers 4e01c7f13d updated nuget spec according to csproj 2018-11-05 06:45:40 +01:00
Christian Kratky 7181813f12 Fix pending messages overflow strategy issue. 2018-11-04 20:33:44 +01:00
Christian Kratky 6fc9f12c34 Refactor task await. 2018-11-04 13:18:42 +01:00
Christian cc4e37fa40 Merge pull request #469 from JanEggers/fixedConcurrentWrites
fixed parallel writes
2018-11-04 13:16:18 +01:00
JanEggers ccadd4e41a updated release notes 2018-11-04 11:44:25 +01:00
JanEggers a5caab62f8 fixed parallel writes 2018-11-04 11:39:12 +01:00
Christian Kratky 2bff218806 Update docs. 2018-10-29 21:49:01 +01:00
Christian Kratky 95c30a2843 Update docs. 2018-10-29 21:48:12 +01:00
Christian Kratky 28efbcdb3d Add max pending messages options for ManagedClient. 2018-10-29 21:45:01 +01:00
Christian Kratky ca7952ab33 Remove wrong setter. 2018-10-29 20:35:22 +01:00
Christian Kratky 62693af196 Merge from master. 2018-10-20 00:34:28 +02:00
Christian Kratky 5abee2512f Update nuget packages. 2018-10-20 00:32:53 +02:00
Christian Kratky c9a6155834 Update docs. 2018-10-19 23:57:09 +02:00
Christian Kratky aad3ab903f Expose retained messages to server interface. 2018-10-19 23:56:32 +02:00
Christian Kratky 4c76a898b6 Add new UnitTests. 2018-10-19 23:55:02 +02:00
Christian d273148bc5 Update README.md
Reorder references alphabetically.
2018-10-14 12:05:08 +02:00
Christian 5314f5c67a Merge pull request #444 from keesschollaart81/patch-1
Referencing the MQTT Bindings for Azure Functions
2018-10-14 12:03:07 +02:00
Kees Schollaart 22aab44d49 Referencing my MQTT Bindings for Azure Functions
Hi, if you're interested, hereby a reference to my work on MQTT Bindings for Azure Functions where I used this library. Thanks for the excellent work. More background [over here](http://case.schollaart.net/2018/09/22/mqtt-and-azure-functions.html)
2018-10-14 11:46:32 +02:00
Christian b819746165 Merge pull request #430 from JanEggers/fix#421
fixed #421
2018-10-07 15:52:17 +02:00
JanEggers 73d33e2200 fixed #421 2018-10-07 15:20:13 +02:00
Christian 740bc3aa30 Merge pull request #429 from JanEggers/supportTlsProtolOptionOnServer
added ability to specify tls protocol version
2018-10-07 15:03:17 +02:00
JanEggers 583b32f17a updated release notes 2018-10-07 13:24:07 +02:00
JanEggers b7ab29473b added ability to specify tls protocol version 2018-10-07 12:10:26 +02:00
Christian Kratky 5a59cd278c Add serializer adapter. 2018-08-06 20:36:15 +02:00
210 changed files with 5988 additions and 1192 deletions
-10
View File
@@ -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
+6 -6
View File
@@ -10,15 +10,15 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is a support library to integrate MQTTnet into AspNetCore.</description>
<releaseNotes>* For more release notes please check the MQTTnet release notes.
</releaseNotes>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2018</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<dependency id="MQTTnet" version="2.8.4" />
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.0" />
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.0.1" />
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.0.1" />
<dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.3" />
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.1.1" />
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.1.1" />
</dependencies>
</metadata>
@@ -10,12 +10,11 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description>
<releaseNotes>* For more release notes please check the MQTTnet release notes.
</releaseNotes>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2018</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<dependency id="MQTTnet" version="2.8.4" />
<dependency id="MQTTnet" version="$nugetVersion" />
</dependencies>
</metadata>
+2 -3
View File
@@ -10,12 +10,11 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
<releaseNotes>* For more release notes please check the MQTTnet release notes.
</releaseNotes>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2018</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<dependency id="MQTTnet" version="2.8.4" />
<dependency id="MQTTnet" version="$nugetVersion" />
</dependencies>
</metadata>
+8 -3
View File
@@ -10,7 +10,12 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
<releaseNotes>* [Client] Fixed a deadlock when an exception is fired while connecting (thanks to @malibVB).
<releaseNotes> * [Core] Added support for MQTTv5 packages.
* [Client] Added new MQTTv5 features to options builder.
* [Client] Added uniform API across all supported MQTT versions (BREAKING CHANGE!)
* [Server] Added support for MQTTv5 clients. The server will still return _success_ for all cases at the moment even if more granular codes are available.
* [Note] Due to MQTTv5 a lot of new classes were introduced. This required adding new namespaces as well. Most classes are backward compatible but new namespaces must be added.
</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2018</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
@@ -19,7 +24,7 @@
<dependency id="NETStandard.Library" version="1.3.0" />
<dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.1" />
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
<group targetFramework="netstandard2.0">
<dependency id="NETStandard.Library" version="2.0.0" />
@@ -28,7 +33,7 @@
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
<group targetFramework="uap10.0">
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.4" />
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.9" />
</group>
<group targetFramework="net452">
</group>
+5 -1
View File
@@ -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

+222
View File
@@ -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
View File
@@ -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
+8 -5
View File
@@ -20,12 +20,13 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
* Async support
* TLS 1.2 support for client and server (but not UWP servers)
* Extensible communication channels (i.e. In-Memory, TCP, TCP+TLS, WS)
* Extensible communication channels (e.g. In-Memory, TCP, TCP+TLS, WS)
* Lightweight (only the low level implementation of MQTT, no overhead)
* Performance optimized (processing ~70.000 messages / second)*
* Uniform API across all supported versions of the MQTT protocol
* Interfaces included for mocking and testing
* Access to internal trace messages
* Unit tested (~90 tests)
* Unit tested (~120 tests)
\* Tested on local machine (Intel i7 8700K) with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetCore_.
@@ -48,6 +49,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
* WebSockets supported (via ASP.NET Core 2.0, separate nuget)
* A custom message interceptor can be added which allows transforming or extending every received application message
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
* Connect clients with different protocol versions at the same time.
## Supported frameworks
@@ -62,7 +64,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
## Supported MQTT versions
* 5.0.0 (planned)
* 5.0.0
* 3.1.1
* 3.1.0
@@ -85,16 +87,17 @@ This project also listed at Open Collective (https://opencollective.com/mqttnet)
This library is used in the following projects:
* Azure Functions MQTT Bindings, [CaseOnline.Azure.WebJobs.Extensions.Mqtt](https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt/)
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
* MQTT Client Rx (Wrapper for Reactive Extensions, <https://github.com/1iveowl/MQTTClient.rx>)
* MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8))
* Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>)
If you use this library and want to see your project here please let me know.
If you use this library and want to see your project here please create a pull request.
## MIT License
Copyright (c) 2017-2018 Christian Kratky
Copyright (c) 2017-2019 Christian Kratky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -3,8 +3,9 @@ using System.Net;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Formatter;
namespace MQTTnet.AspNetCore.Client
{
@@ -14,15 +15,13 @@ namespace MQTTnet.AspNetCore.Client
{
if (options == null) throw new ArgumentNullException(nameof(options));
var serializer = new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion };
switch (options.ChannelOptions)
{
case MqttClientTcpOptions tcpOptions:
{
var endpoint = new DnsEndPoint(tcpOptions.Server, tcpOptions.GetPort());
var tcpConnection = new TcpConnection(endpoint);
return new MqttConnectionContext(serializer, tcpConnection);
return new MqttConnectionContext(new MqttPacketFormatterAdapter(options.ProtocolVersion), tcpConnection);
}
default:
{
@@ -14,9 +14,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.3" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.1" />
</ItemGroup>
<ItemGroup>
@@ -1,31 +1,32 @@
using Microsoft.AspNetCore.Connections;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Serializer;
using System;
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Formatter;
namespace MQTTnet.AspNetCore
{
public class MqttConnectionContext : IMqttChannelAdapter
{
public MqttConnectionContext(
IMqttPacketSerializer packetSerializer,
ConnectionContext connection)
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
{
PacketSerializer = packetSerializer;
Connection = connection;
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
Connection = connection ?? throw new ArgumentNullException(nameof(connection));
}
public string Endpoint => Connection.ConnectionId;
public ConnectionContext Connection { get; }
public IMqttPacketSerializer PacketSerializer { get; }
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
public event EventHandler ReadingPacketStarted;
public event EventHandler ReadingPacketCompleted;
private readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1);
public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
if (Connection is TcpConnection tcp && !tcp.IsConnected)
@@ -59,7 +60,7 @@ namespace MQTTnet.AspNetCore
}
else
{
readResult = await readTask;
readResult = await readTask.ConfigureAwait(false);
}
var buffer = readResult.Buffer;
@@ -71,7 +72,7 @@ namespace MQTTnet.AspNetCore
{
if (!buffer.IsEmpty)
{
if (PacketSerializer.TryDeserialize(buffer, out var packet, out consumed, out observed))
if (PacketFormatterAdapter.TryDecode(buffer, out var packet, out consumed, out observed))
{
return packet;
}
@@ -83,7 +84,7 @@ namespace MQTTnet.AspNetCore
}
else if (readResult.IsCompleted)
{
break;
throw new MqttCommunicationException("Connection Aborted");
}
}
finally
@@ -104,10 +105,20 @@ namespace MQTTnet.AspNetCore
return null;
}
public Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
var buffer = PacketSerializer.Serialize(packet);
return Connection.Transport.Output.WriteAsync(buffer.AsMemory(), cancellationToken).AsTask();
var buffer = PacketFormatterAdapter.Encode(packet).AsMemory();
var output = Connection.Transport.Output;
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
await output.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
}
finally
{
_writerSemaphore.Release();
}
}
public void Dispose()
@@ -1,15 +1,15 @@
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using MQTTnet.Adapter;
using MQTTnet.Serializer;
using MQTTnet.Server;
using System;
using System.Threading.Tasks;
using MQTTnet.Formatter;
namespace MQTTnet.AspNetCore
{
public class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
{
{
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
public override async Task OnConnectedAsync(ConnectionContext connection)
@@ -21,9 +21,7 @@ namespace MQTTnet.AspNetCore
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
}
var serializer = new MqttPacketSerializer();
using (var adapter = new MqttConnectionContext(serializer, connection))
using (var adapter = new MqttConnectionContext(new MqttPacketFormatterAdapter(), connection))
{
var args = new MqttServerAdapterClientAcceptedEventArgs(adapter);
ClientAccepted?.Invoke(this, args);
@@ -41,7 +39,7 @@ namespace MQTTnet.AspNetCore
{
return Task.CompletedTask;
}
public void Dispose()
{
}
@@ -3,8 +3,8 @@ using System.Net.WebSockets;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using MQTTnet.Serializer;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore
@@ -27,7 +27,7 @@ namespace MQTTnet.AspNetCore
{
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
var clientAdapter = new MqttChannelAdapter(new MqttWebSocketChannel(webSocket, endpoint), new MqttPacketSerializer(), new MqttNetLogger().CreateChildLogger(nameof(MqttWebSocketServerAdapter)));
var clientAdapter = new MqttChannelAdapter(new MqttWebSocketChannel(webSocket, endpoint), new MqttPacketFormatterAdapter(), new MqttNetLogger().CreateChildLogger(nameof(MqttWebSocketServerAdapter)));
var eventArgs = new MqttServerAdapterClientAcceptedEventArgs(clientAdapter);
ClientAccepted?.Invoke(this, eventArgs);
@@ -37,7 +37,7 @@ namespace MQTTnet.AspNetCore
await eventArgs.SessionTask.ConfigureAwait(false);
}
}
public void Dispose()
{
StopAsync().GetAwaiter().GetResult();
@@ -2,19 +2,22 @@
using System.Buffers;
using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Serializer;
namespace MQTTnet.AspNetCore
{
public static class ReaderExtensions
{
public static bool TryDeserialize(this IMqttPacketSerializer serializer, in ReadOnlySequence<byte> input, out MqttBasePacket packet, out SequencePosition consumed, out SequencePosition observed)
public static bool TryDecode(this MqttPacketFormatterAdapter formatter, in ReadOnlySequence<byte> input, out MqttBasePacket packet, out SequencePosition consumed, out SequencePosition observed)
{
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
packet = null;
consumed = input.Start;
observed = input.End;
var copy = input;
if (copy.Length < 2)
{
return false;
@@ -33,7 +36,7 @@ namespace MQTTnet.AspNetCore
var bodySlice = copy.Slice(0, bodyLength);
var buffer = bodySlice.GetArray();
packet = serializer.Deserialize(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(buffer, 0, buffer.Length)));
packet = formatter.Decode(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(buffer, 0, buffer.Length), buffer.Length + 2));
consumed = bodySlice.End;
observed = bodySlice.End;
return true;
@@ -0,0 +1,14 @@
using System;
namespace MQTTnet.Extensions.ManagedClient
{
public class ApplicationMessageSkippedEventArgs : EventArgs
{
public ApplicationMessageSkippedEventArgs(ManagedMqttApplicationMessage applicationMessage)
{
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
}
public ManagedMqttApplicationMessage ApplicationMessage { get; }
}
}
@@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
namespace MQTTnet.Extensions.ManagedClient
{
@@ -10,11 +11,13 @@ namespace MQTTnet.Extensions.ManagedClient
bool IsStarted { get; }
bool IsConnected { get; }
int PendingApplicationMessagesCount { get; }
IManagedMqttClientOptions Options { get; }
event EventHandler<MqttClientConnectedEventArgs> Connected;
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
event EventHandler<ApplicationMessageSkippedEventArgs> ApplicationMessageSkipped;
event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed;
event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed;
@@ -1,5 +1,7 @@
using System;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
{
@@ -12,5 +14,9 @@ namespace MQTTnet.Extensions.ManagedClient
TimeSpan ConnectionCheckInterval { get; }
IManagedMqttClientStorage Storage { get; }
int MaxPendingMessages { get; }
MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; }
}
}
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461</TargetFrameworks>
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);uap10.0</TargetFrameworks>
<Product />
<Company />
@@ -1,19 +1,23 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Client;
using MQTTnet.Client.Publishing;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Protocol;
using MQTTnet.Server;
using MqttClientConnectedEventArgs = MQTTnet.Client.Connecting.MqttClientConnectedEventArgs;
using MqttClientDisconnectedEventArgs = MQTTnet.Client.Disconnecting.MqttClientDisconnectedEventArgs;
namespace MQTTnet.Extensions.ManagedClient
{
public class ManagedMqttClient : IManagedMqttClient
{
private readonly BlockingCollection<ManagedMqttApplicationMessage> _messageQueue = new BlockingCollection<ManagedMqttApplicationMessage>();
private readonly BlockingQueue<ManagedMqttApplicationMessage> _messageQueue = new BlockingQueue<ManagedMqttApplicationMessage>();
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
private readonly HashSet<string> _unsubscriptions = new HashSet<string>();
@@ -24,7 +28,6 @@ namespace MQTTnet.Extensions.ManagedClient
private CancellationTokenSource _publishingCancellationToken;
private ManagedMqttClientStorageManager _storageManager;
private IManagedMqttClientOptions _options;
private bool _subscriptionsNotPushed;
@@ -44,12 +47,14 @@ namespace MQTTnet.Extensions.ManagedClient
public bool IsConnected => _mqttClient.IsConnected;
public bool IsStarted => _connectionCancellationToken != null;
public int PendingApplicationMessagesCount => _messageQueue.Count;
public IManagedMqttClientOptions Options { get; private set; }
public event EventHandler<MqttClientConnectedEventArgs> Connected;
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
public event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
public event EventHandler<ApplicationMessageSkippedEventArgs> ApplicationMessageSkipped;
public event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed;
public event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed;
@@ -66,16 +71,16 @@ namespace MQTTnet.Extensions.ManagedClient
if (_connectionCancellationToken != null) throw new InvalidOperationException("The managed client is already started.");
_options = options;
Options = options;
if (_options.Storage != null)
if (Options.Storage != null)
{
_storageManager = new ManagedMqttClientStorageManager(_options.Storage);
_storageManager = new ManagedMqttClientStorageManager(Options.Storage);
var messages = await _storageManager.LoadQueuedMessagesAsync().ConfigureAwait(false);
foreach (var message in messages)
{
_messageQueue.Add(message);
_messageQueue.Enqueue(message);
}
}
@@ -93,31 +98,55 @@ namespace MQTTnet.Extensions.ManagedClient
StopPublishing();
StopMaintainingConnection();
while (_messageQueue.Any())
{
_messageQueue.Take();
}
_messageQueue.Clear();
return Task.FromResult(0);
}
public Task PublishAsync(MqttApplicationMessage applicationMessage)
public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
return PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build());
await PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build()).ConfigureAwait(false);
return new MqttClientPublishResult();
}
public async Task PublishAsync(ManagedMqttApplicationMessage applicationMessage)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
if (_storageManager != null)
ManagedMqttApplicationMessage removedMessage = null;
lock (_messageQueue)
{
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
if (_messageQueue.Count >= Options.MaxPendingMessages)
{
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
{
_logger.Verbose("Skipping publish of new application message because internal queue is full.");
ApplicationMessageSkipped?.Invoke(this, new ApplicationMessageSkippedEventArgs(applicationMessage));
return;
}
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage)
{
removedMessage = _messageQueue.RemoveFirst();
_logger.Verbose("Removed oldest application message from internal queue because it is full.");
ApplicationMessageSkipped?.Invoke(this, new ApplicationMessageSkippedEventArgs(removedMessage));
}
}
_messageQueue.Enqueue(applicationMessage);
}
_messageQueue.Add(applicationMessage);
if (_storageManager != null)
{
if (removedMessage != null)
{
await _storageManager.RemoveAsync(removedMessage).ConfigureAwait(false);
}
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
}
}
public Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
@@ -157,7 +186,6 @@ namespace MQTTnet.Extensions.ManagedClient
public void Dispose()
{
_messageQueue?.Dispose();
_connectionCancellationToken?.Dispose();
_publishingCancellationToken?.Dispose();
}
@@ -180,7 +208,15 @@ namespace MQTTnet.Extensions.ManagedClient
}
finally
{
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
try
{
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.Error(exception, "Error while disconnecting.");
}
_logger.Info("Stopped");
}
}
@@ -193,7 +229,7 @@ namespace MQTTnet.Extensions.ManagedClient
if (connectionState == ReconnectionResult.NotConnected)
{
StopPublishing();
await Task.Delay(_options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false);
await Task.Delay(Options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false);
return;
}
@@ -206,7 +242,7 @@ namespace MQTTnet.Extensions.ManagedClient
if (connectionState == ReconnectionResult.StillConnected)
{
await Task.Delay(_options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
await Task.Delay(Options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
@@ -228,7 +264,15 @@ namespace MQTTnet.Extensions.ManagedClient
{
while (!cancellationToken.IsCancellationRequested && _mqttClient.IsConnected)
{
var message = _messageQueue.Take(cancellationToken);
//Peek at the message without dequeueing in order to prevent the
//possibility of the queue growing beyond the configured cap.
//Previously, messages could be re-enqueued if there was an
//exception, and this re-enqueueing did not honor the cap.
//Furthermore, because re-enqueueing would shuffle the order
//of the messages, the DropOldestQueuedMessage strategy would
//be unable to know which message is actually the oldest and would
//instead drop the first item in the queue.
var message = _messageQueue.PeekAndWait();
if (message == null)
{
continue;
@@ -258,6 +302,16 @@ namespace MQTTnet.Extensions.ManagedClient
try
{
_mqttClient.PublishAsync(message.ApplicationMessage).GetAwaiter().GetResult();
lock (_messageQueue) //lock to avoid conflict with this.PublishAsync
{
//While publishing this message, this.PublishAsync could have booted this
//message off the queue to make room for another (when using a cap
//with the DropOldestQueuedMessage strategy). If the first item
//in the queue is equal to this message, then it's safe to remove
//it from the queue. If not, that means this.PublishAsync has already
//removed it, in which case we don't want to do anything.
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
}
_storageManager?.RemoveAsync(message).GetAwaiter().GetResult();
}
catch (MqttCommunicationException exception)
@@ -266,9 +320,19 @@ namespace MQTTnet.Extensions.ManagedClient
_logger.Warning(exception, $"Publishing application ({message.Id}) message failed.");
if (message.ApplicationMessage.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
if (message.ApplicationMessage.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{
_messageQueue.Add(message);
//If QoS 0, we don't want this message to stay on the queue.
//If QoS 1 or 2, it's possible that, when using a cap, this message
//has been booted off the queue by this.PublishAsync, in which case this
//thread will not continue to try to publish it. While this does
//contradict the expected behavior of QoS 1 and 2, that's also true
//for the usage of a message queue cap, so it's still consistent
//with prior behavior in that way.
lock (_messageQueue) //lock to avoid conflict with this.PublishAsync
{
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
}
}
}
catch (Exception exception)
@@ -291,7 +355,7 @@ namespace MQTTnet.Extensions.ManagedClient
lock (_subscriptions)
{
subscriptions = _subscriptions.Select(i => new TopicFilter(i.Key, i.Value)).ToList();
subscriptions = _subscriptions.Select(i => new TopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value }).ToList();
unsubscriptions = new HashSet<string>(_unsubscriptions);
_unsubscriptions.Clear();
@@ -334,7 +398,7 @@ namespace MQTTnet.Extensions.ManagedClient
try
{
await _mqttClient.ConnectAsync(_options.ClientOptions).ConfigureAwait(false);
await _mqttClient.ConnectAsync(Options.ClientOptions).ConfigureAwait(false);
return ReconnectionResult.Reconnected;
}
catch (Exception exception)
@@ -1,5 +1,7 @@
using System;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
{
@@ -12,5 +14,9 @@ namespace MQTTnet.Extensions.ManagedClient
public TimeSpan ConnectionCheckInterval { get; set; } = TimeSpan.FromSeconds(1);
public IManagedMqttClientStorage Storage { get; set; }
public int MaxPendingMessages { get; set; } = int.MaxValue;
public MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; set; } = MqttPendingMessagesOverflowStrategy.DropNewMessage;
}
}
@@ -1,5 +1,7 @@
using System;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
{
@@ -8,6 +10,18 @@ namespace MQTTnet.Extensions.ManagedClient
private readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions();
private MqttClientOptionsBuilder _clientOptionsBuilder;
public ManagedMqttClientOptionsBuilder WithMaxPendingMessages(int value)
{
_options.MaxPendingMessages = value;
return this;
}
public ManagedMqttClientOptionsBuilder WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy value)
{
_options.PendingMessagesOverflowStrategy = value;
return this;
}
public ManagedMqttClientOptionsBuilder WithAutoReconnectDelay(TimeSpan value)
{
_options.AutoReconnectDelay = value;
@@ -28,6 +28,8 @@ namespace MQTTnet.Extensions.ManagedClient
public async Task AddAsync(ManagedMqttApplicationMessage applicationMessage)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
{
_messages.Add(applicationMessage);
@@ -37,6 +39,8 @@ namespace MQTTnet.Extensions.ManagedClient
public async Task RemoveAsync(ManagedMqttApplicationMessage applicationMessage)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
{
var index = _messages.IndexOf(applicationMessage);
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461</TargetFrameworks>
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);uap10.0</TargetFrameworks>
<Product />
<Company />
@@ -1,8 +1,8 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Serializer;
namespace MQTTnet.Adapter
{
@@ -10,8 +10,8 @@ namespace MQTTnet.Adapter
{
string Endpoint { get; }
IMqttPacketSerializer PacketSerializer { get; }
MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
event EventHandler ReadingPacketStarted;
event EventHandler ReadingPacketCompleted;
@@ -1,7 +1,7 @@
using MQTTnet.Adapter;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
namespace MQTTnet.Client
namespace MQTTnet.Adapter
{
public interface IMqttClientAdapterFactory
{
+25 -18
View File
@@ -7,9 +7,9 @@ using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Serializer;
namespace MQTTnet.Adapter
{
@@ -22,26 +22,27 @@ namespace MQTTnet.Adapter
private readonly IMqttNetChildLogger _logger;
private readonly IMqttChannel _channel;
private readonly MqttPacketReader _packetReader;
private readonly byte[] _fixedHeaderBuffer = new byte[2];
private readonly byte[] _singleByteBuffer = new byte[1];
private bool _isDisposed;
public MqttChannelAdapter(IMqttChannel channel, IMqttPacketSerializer serializer, IMqttNetChildLogger logger)
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetChildLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
_packetReader = new MqttPacketReader(_channel);
_logger = logger.CreateChildLogger(nameof(MqttChannelAdapter));
}
public string Endpoint => _channel.Endpoint;
public IMqttPacketSerializer PacketSerializer { get; }
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
public event EventHandler ReadingPacketStarted;
public event EventHandler ReadingPacketCompleted;
@@ -91,17 +92,17 @@ namespace MQTTnet.Adapter
WrapException(exception);
}
}
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
var packetData = PacketSerializer.Serialize(packet);
var packetData = PacketFormatterAdapter.Encode(packet);
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
PacketSerializer.FreeBuffer();
PacketFormatterAdapter.FreeBuffer();
_logger.Verbose("TX >>> {0}", packet);
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
}
catch (Exception exception)
{
@@ -140,13 +141,18 @@ namespace MQTTnet.Adapter
return null;
}
var packet = PacketSerializer.Deserialize(receivedMqttPacket);
if (!PacketFormatterAdapter.ProtocolVersion.HasValue)
{
PacketFormatterAdapter.DetectProtocolVersion(receivedMqttPacket);
}
var packet = PacketFormatterAdapter.Decode(receivedMqttPacket);
if (packet == null)
{
throw new MqttProtocolViolationException("Received malformed packet.");
}
_logger.Verbose("RX <<< {0}", packet);
_logger.Verbose("RX ({0} bytes) <<< {1}", receivedMqttPacket.TotalLength, packet);
return packet;
}
@@ -168,7 +174,7 @@ namespace MQTTnet.Adapter
private async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken)
{
var fixedHeader = await MqttPacketReader.ReadFixedHeaderAsync(_channel, _fixedHeaderBuffer, _singleByteBuffer, cancellationToken).ConfigureAwait(false);
var fixedHeader = await _packetReader.ReadFixedHeaderAsync(_fixedHeaderBuffer, cancellationToken).ConfigureAwait(false);
try
{
@@ -176,7 +182,7 @@ namespace MQTTnet.Adapter
if (fixedHeader.RemainingLength == 0)
{
return new ReceivedMqttPacket(fixedHeader.Flags, null);
return new ReceivedMqttPacket(fixedHeader.Flags, null, 2);
}
var body = new byte[fixedHeader.RemainingLength];
@@ -192,9 +198,9 @@ namespace MQTTnet.Adapter
}
#if WINDOWS_UWP
var readBytes = await _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false);
var readBytes = await _channel.ReadAsync(body, bodyOffset, (int)chunkSize, cancellationToken).ConfigureAwait(false);
#else
// async/await is not used to avoid the overhead of context switches. We assume that the reamining data
// async/await is not used to avoid the overhead of context switches. We assume that the remaining data
// has been sent from the sender directly after the initial bytes.
var readBytes = _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
@@ -205,7 +211,8 @@ namespace MQTTnet.Adapter
bodyOffset += readBytes;
} while (bodyOffset < body.Length);
return new ReceivedMqttPacket(fixedHeader.Flags, new MqttPacketBodyReader(body, 0, body.Length));
var bodyReader = new MqttPacketBodyReader(body, 0, body.Length);
return new ReceivedMqttPacket(fixedHeader.Flags, bodyReader, fixedHeader.TotalLength);
}
finally
{
@@ -1,16 +1,16 @@
using MQTTnet.Exceptions;
using MQTTnet.Protocol;
using MQTTnet.Client.Connecting;
using MQTTnet.Exceptions;
namespace MQTTnet.Adapter
{
public class MqttConnectingFailedException : MqttCommunicationException
{
public MqttConnectingFailedException(MqttConnectReturnCode returnCode)
: base($"Connecting with MQTT server failed ({returnCode}).")
public MqttConnectingFailedException(MqttClientConnectResultCode resultCode)
: base($"Connecting with MQTT server failed ({resultCode.ToString()}).")
{
ReturnCode = returnCode;
ResultCode = resultCode;
}
public MqttConnectReturnCode ReturnCode { get; }
public MqttClientConnectResultCode ResultCode { get; }
}
}
+5 -2
View File
@@ -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,6 +1,6 @@
using System;
namespace MQTTnet.Client
namespace MQTTnet.Client.Disconnecting
{
public class MqttClientDisconnectedEventArgs : EventArgs
{
+9 -3
View File
@@ -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);
}
}
+2 -1
View File
@@ -1,4 +1,5 @@
using MQTTnet.Diagnostics;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
namespace MQTTnet.Client
{
+125 -75
View File
@@ -5,9 +5,16 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.PacketDispatcher;
using MQTTnet.Client.Publishing;
using MQTTnet.Client.Subscribing;
using MQTTnet.Client.Unsubscribing;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Protocol;
@@ -23,13 +30,12 @@ namespace MQTTnet.Client
private readonly IMqttClientAdapterFactory _adapterFactory;
private readonly IMqttNetChildLogger _logger;
private IMqttClientOptions _options;
private CancellationTokenSource _cancellationTokenSource;
internal Task _packetReceiverTask;
internal Task _keepAliveMessageSenderTask;
private IMqttChannelAdapter _adapter;
private bool _cleanDisconnectInitiated;
private int _disconnectGate;
private long _disconnectGate;
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
{
@@ -44,6 +50,7 @@ namespace MQTTnet.Client
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
public bool IsConnected { get; private set; }
public IMqttClientOptions Options { get; private set; }
public async Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options)
{
@@ -54,7 +61,8 @@ namespace MQTTnet.Client
try
{
_options = options;
Options = options;
_packetIdentifierProvider.Reset();
_packetDispatcher.Reset();
@@ -62,27 +70,28 @@ namespace MQTTnet.Client
_disconnectGate = 0;
_adapter = _adapterFactory.CreateClientAdapter(options, _logger);
_logger.Verbose($"Trying to connect with server ({_options.ChannelOptions}).");
await _adapter.ConnectAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token).ConfigureAwait(false);
_logger.Verbose($"Trying to connect with server ({Options.ChannelOptions}).");
await _adapter.ConnectAsync(Options.CommunicationTimeout, _cancellationTokenSource.Token).ConfigureAwait(false);
_logger.Verbose("Connection with server established.");
StartReceivingPackets(_cancellationTokenSource.Token);
var connectResponse = await AuthenticateAsync(options.WillMessage, _cancellationTokenSource.Token).ConfigureAwait(false);
var connectResult = await AuthenticateAsync(options.WillMessage, _cancellationTokenSource.Token).ConfigureAwait(false);
_logger.Verbose("MQTT connection with server established.");
_sendTracker.Restart();
if (_options.KeepAlivePeriod != TimeSpan.Zero)
if (Options.KeepAlivePeriod != TimeSpan.Zero)
{
StartSendingKeepAliveMessages(_cancellationTokenSource.Token);
}
IsConnected = true;
Connected?.Invoke(this, new MqttClientConnectedEventArgs(connectResponse.IsSessionPresent));
Connected?.Invoke(this, new MqttClientConnectedEventArgs(connectResult));
_logger.Info("Connected.");
return new MqttClientConnectResult(connectResponse.IsSessionPresent);
return connectResult;
}
catch (Exception exception)
{
@@ -97,7 +106,7 @@ namespace MQTTnet.Client
}
}
public async Task DisconnectAsync()
public async Task DisconnectAsync(MqttClientDisconnectOptions options)
{
try
{
@@ -105,7 +114,8 @@ namespace MQTTnet.Client
if (IsConnected && !_cancellationTokenSource.IsCancellationRequested)
{
await SendAsync(new MqttDisconnectPacket(), _cancellationTokenSource.Token).ConfigureAwait(false);
var disconnectPacket = CreateDisconnectPacket(options);
await SendAsync(disconnectPacket, _cancellationTokenSource.Token).ConfigureAwait(false);
}
}
finally
@@ -117,7 +127,7 @@ namespace MQTTnet.Client
}
}
public async Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
public async Task<MqttClientSubscribeResult> SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
@@ -125,21 +135,16 @@ namespace MQTTnet.Client
var subscribePacket = new MqttSubscribePacket
{
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(),
TopicFilters = topicFilters.ToList()
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier()
};
var response = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false);
subscribePacket.TopicFilters.AddRange(topicFilters);
if (response.SubscribeReturnCodes.Count != subscribePacket.TopicFilters.Count)
{
throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1].");
}
return subscribePacket.TopicFilters.Select((t, i) => new MqttSubscribeResult(t, response.SubscribeReturnCodes[i])).ToList();
var subAckPacket = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientSubscribeResult(subscribePacket, subAckPacket);
}
public async Task UnsubscribeAsync(IEnumerable<string> topicFilters)
public async Task<MqttClientUnsubscribeResult> UnsubscribeAsync(IEnumerable<string> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
@@ -147,34 +152,47 @@ namespace MQTTnet.Client
var unsubscribePacket = new MqttUnsubscribePacket
{
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(),
TopicFilters = topicFilters.ToList()
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier()
};
await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false);
unsubscribePacket.TopicFilters.AddRange(topicFilters);
var unsubAckPacket = await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientUnsubscribeResult(unsubscribePacket, unsubAckPacket);
}
public Task PublishAsync(MqttApplicationMessage applicationMessage)
public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
ThrowIfNotConnected();
var publishPacket = applicationMessage.ToPublishPacket();
var publishPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePublishPacket(applicationMessage);
switch (applicationMessage.QualityOfServiceLevel)
{
case MqttQualityOfServiceLevel.AtMostOnce:
{
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
return SendAsync(publishPacket, _cancellationTokenSource.Token);
await SendAsync(publishPacket, _cancellationTokenSource.Token).ConfigureAwait(false);
return new MqttClientPublishResult();
}
case MqttQualityOfServiceLevel.AtLeastOnce:
{
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier();
return SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, _cancellationTokenSource.Token);
var response = await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, _cancellationTokenSource.Token).ConfigureAwait(false);
var result = new MqttClientPublishResult();
if (response.ReasonCode != null)
{
result.ReasonCode = (MqttClientPublishReasonCode)response.ReasonCode;
}
return result;
}
case MqttQualityOfServiceLevel.ExactlyOnce:
{
return PublishExactlyOnce(publishPacket, _cancellationTokenSource.Token);
return await PublishExactlyOnceAsync(publishPacket, _cancellationTokenSource.Token).ConfigureAwait(false);
}
default:
{
@@ -193,30 +211,27 @@ namespace MQTTnet.Client
_adapter = null;
}
private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
private async Task<MqttClientConnectResult> AuthenticateAsync(MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
{
var connectPacket = new MqttConnectPacket
{
ClientId = _options.ClientId,
Username = _options.Credentials?.Username,
Password = _options.Credentials?.Password,
CleanSession = _options.CleanSession,
KeepAlivePeriod = (ushort)_options.KeepAlivePeriod.TotalSeconds,
WillMessage = willApplicationMessage
};
var connectPacket = _adapter.PacketFormatterAdapter.DataConverter.CreateConnectPacket(
willApplicationMessage,
Options);
var response = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
if (response.ConnectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
var result = _adapter.PacketFormatterAdapter.DataConverter.CreateClientConnectResult(connAckPacket);
if (result.ResultCode != MqttClientConnectResultCode.Success)
{
throw new MqttConnectingFailedException(response.ConnectReturnCode);
throw new MqttConnectingFailedException(result.ResultCode);
}
return response;
return result;
}
private void ThrowIfNotConnected()
{
if (!IsConnected) throw new MqttCommunicationException("The client is not connected.");
if (!IsConnected || Interlocked.Read(ref _disconnectGate) == 1) throw new MqttCommunicationException("The client is not connected.");
}
private void ThrowIfConnected(string message)
@@ -229,19 +244,19 @@ namespace MQTTnet.Client
var clientWasConnected = IsConnected;
InitiateDisconnect();
IsConnected = false;
try
{
await WaitForTaskAsync(_packetReceiverTask, sender).ConfigureAwait(false);
await WaitForTaskAsync(_keepAliveMessageSenderTask, sender).ConfigureAwait(false);
if (_adapter != null)
{
await _adapter.DisconnectAsync(_options.CommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
await _adapter.DisconnectAsync(Options.CommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
}
await WaitForTaskAsync(_packetReceiverTask, sender).ConfigureAwait(false);
await WaitForTaskAsync(_keepAliveMessageSenderTask, sender).ConfigureAwait(false);
_logger.Verbose("Disconnected from adapter.");
}
catch (Exception adapterException)
@@ -303,9 +318,9 @@ namespace MQTTnet.Client
try
{
await _adapter.SendPacketAsync(requestPacket, cancellationToken).ConfigureAwait(false);
var respone = await Internal.TaskExtensions.TimeoutAfterAsync(ct => packetAwaiter.Task, _options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
return await packetAwaiter.WaitOneAsync(Options.CommunicationTimeout);
return (TResponsePacket)respone;
//return (TResponsePacket)await Internal.TaskExtensions.TimeoutAfterAsync(ct => packetAwaiter.Task, Options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
}
catch (MqttCommunicationTimedOutException)
{
@@ -320,16 +335,16 @@ namespace MQTTnet.Client
private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken)
{
_logger.Verbose("Start sending keep alive packets.");
try
{
_logger.Verbose("Start sending keep alive packets.");
while (!cancellationToken.IsCancellationRequested)
{
var keepAliveSendInterval = TimeSpan.FromSeconds(_options.KeepAlivePeriod.TotalSeconds * 0.75);
if (_options.KeepAliveSendInterval.HasValue)
var keepAliveSendInterval = TimeSpan.FromSeconds(Options.KeepAlivePeriod.TotalSeconds * 0.75);
if (Options.KeepAliveSendInterval.HasValue)
{
keepAliveSendInterval = _options.KeepAliveSendInterval.Value;
keepAliveSendInterval = Options.KeepAliveSendInterval.Value;
}
var waitTime = keepAliveSendInterval - _sendTracker.Elapsed;
@@ -374,14 +389,13 @@ namespace MQTTnet.Client
private async Task ReceivePacketsAsync(CancellationToken cancellationToken)
{
_logger.Verbose("Start receiving packets.");
try
{
_logger.Verbose("Start receiving packets.");
while (!cancellationToken.IsCancellationRequested)
{
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken)
.ConfigureAwait(false);
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false);
if (packet != null && !cancellationToken.IsCancellationRequested)
{
@@ -435,7 +449,7 @@ namespace MQTTnet.Client
if (packet is MqttDisconnectPacket)
{
return DisconnectAsync();
return DisconnectAsync(null);
}
if (packet is MqttPubRelPacket pubRelPacket)
@@ -458,14 +472,14 @@ namespace MQTTnet.Client
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{
FireApplicationMessageReceivedEvent(publishPacket);
return SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }, cancellationToken);
return SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier, ReasonCode = MqttPubAckReasonCode.Success }, cancellationToken);
}
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{
// QoS 2 is implement as method "B" (4.3.3 QoS 2: Exactly once delivery)
FireApplicationMessageReceivedEvent(publishPacket);
return SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }, cancellationToken);
return SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier, ReasonCode = MqttPubRecReasonCode.Success }, cancellationToken);
}
throw new MqttCommunicationException("Received a not supported QoS level.");
@@ -475,23 +489,33 @@ namespace MQTTnet.Client
{
var response = new MqttPubCompPacket
{
PacketIdentifier = pubRelPacket.PacketIdentifier
PacketIdentifier = pubRelPacket.PacketIdentifier,
ReasonCode = MqttPubCompReasonCode.Success
};
return SendAsync(response, cancellationToken);
}
private async Task PublishExactlyOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
private async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier();
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
var pubRelPacket = new MqttPubRelPacket
{
PacketIdentifier = pubRecPacket.PacketIdentifier
PacketIdentifier = pubRecPacket.PacketIdentifier,
ReasonCode = MqttPubRelReasonCode.Success
};
await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
var result = new MqttClientPublishResult();
if (pubRecPacket.ReasonCode != null)
{
result.ReasonCode = (MqttClientPublishReasonCode)pubRecPacket.ReasonCode;
}
return result;
}
private void StartReceivingPackets(CancellationToken cancellationToken)
@@ -499,8 +523,8 @@ namespace MQTTnet.Client
_packetReceiverTask = Task.Factory.StartNew(
() => ReceivePacketsAsync(cancellationToken),
cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
TaskCreationOptions.LongRunning,
TaskScheduler.Default).Unwrap();
}
private void StartSendingKeepAliveMessages(CancellationToken cancellationToken)
@@ -509,15 +533,15 @@ namespace MQTTnet.Client
() => SendKeepAliveMessagesAsync(cancellationToken),
cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
TaskScheduler.Default).Unwrap();
}
private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket)
{
try
{
var applicationMessage = publishPacket.ToApplicationMessage();
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(_options.ClientId, applicationMessage));
var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage));
}
catch (Exception exception)
{
@@ -550,5 +574,31 @@ namespace MQTTnet.Client
{
return Interlocked.CompareExchange(ref _disconnectGate, 1, 0) != 0;
}
private MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options)
{
var packet = new MqttDisconnectPacket();
if (_adapter.PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.V500)
{
if (options == null)
{
packet.ReasonCode = MqttDisconnectReasonCode.NormalDisconnection;
}
else
{
packet.ReasonCode = (MqttDisconnectReasonCode)options.ReasonCode;
}
}
else
{
if (options != null)
{
throw new MqttProtocolViolationException("Reason codes for disconnect are only supported for MQTTv5.");
}
}
return packet;
}
}
}
}
@@ -1,12 +0,0 @@
namespace MQTTnet.Client
{
public class MqttClientConnectResult
{
public MqttClientConnectResult(bool isSessionPresent)
{
IsSessionPresent = isSessionPresent;
}
public bool IsSessionPresent { get; }
}
}
@@ -1,14 +0,0 @@
using System;
namespace MQTTnet.Client
{
public class MqttClientConnectedEventArgs : EventArgs
{
public MqttClientConnectedEventArgs(bool isSessionPresent)
{
IsSessionPresent = isSessionPresent;
}
public bool IsSessionPresent { get; }
}
}
+13 -5
View File
@@ -1,14 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MQTTnet.Client.Subscribing;
using MQTTnet.Client.Unsubscribing;
using MQTTnet.Protocol;
namespace MQTTnet.Client
{
public static class MqttClientExtensions
{
public static Task<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, params TopicFilter[] topicFilters)
public static Task DisconnectAsync(this IMqttClient client)
{
if (client == null) throw new ArgumentNullException(nameof(client));
return client.DisconnectAsync(null);
}
public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, params TopicFilter[] topicFilters)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
@@ -16,7 +24,7 @@ namespace MQTTnet.Client
return client.SubscribeAsync(topicFilters.ToList());
}
public static Task<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topic == null) throw new ArgumentNullException(nameof(topic));
@@ -24,7 +32,7 @@ namespace MQTTnet.Client
return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
}
public static Task<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, string topic)
public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, string topic)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topic == null) throw new ArgumentNullException(nameof(topic));
@@ -32,7 +40,7 @@ namespace MQTTnet.Client
return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build());
}
public static Task UnsubscribeAsync(this IMqttClient client, params string[] topicFilters)
public static Task<MqttClientUnsubscribeResult> UnsubscribeAsync(this IMqttClient client, params string[] topicFilters)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
@@ -1,17 +0,0 @@
using MQTTnet.Protocol;
namespace MQTTnet.Client
{
public class MqttSubscribeResult
{
public MqttSubscribeResult(TopicFilter topicFilter, MqttSubscribeReturnCode returnCode)
{
TopicFilter = topicFilter;
ReturnCode = returnCode;
}
public TopicFilter TopicFilter { get; }
public MqttSubscribeReturnCode ReturnCode { get; }
}
}
@@ -1,4 +1,4 @@
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public interface IMqttClientChannelOptions
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public interface IMqttClientCredentials
{
@@ -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,4 +1,4 @@
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public class MqttClientCredentials : IMqttClientCredentials
{
@@ -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; }
}
}
@@ -1,8 +1,8 @@
using System;
using System.Linq;
using MQTTnet.Serializer;
using MQTTnet.Formatter;
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public class MqttClientOptionsBuilder
{
@@ -55,6 +55,55 @@ namespace MQTTnet.Client
return this;
}
public MqttClientOptionsBuilder WithAuthentication(string method, byte[] data)
{
_options.AuthenticationMethod = method;
_options.AuthenticationData = data;
return this;
}
public MqttClientOptionsBuilder WithWillDelayInterval(uint? willDelayInterval)
{
_options.WillDelayInterval = willDelayInterval;
return this;
}
public MqttClientOptionsBuilder WithTopicAliasMaximum(ushort? topicAliasMaximum)
{
_options.TopicAliasMaximum = topicAliasMaximum;
return this;
}
public MqttClientOptionsBuilder WithMaximumPacketSize(uint? maximumPacketSize)
{
_options.MaximumPacketSize = maximumPacketSize;
return this;
}
public MqttClientOptionsBuilder WithReceiveMaximum(ushort? receiveMaximum)
{
_options.ReceiveMaximum = receiveMaximum;
return this;
}
public MqttClientOptionsBuilder WithRequestProblemInformation(bool? requestProblemInformation = true)
{
_options.RequestProblemInformation = requestProblemInformation;
return this;
}
public MqttClientOptionsBuilder WithRequestResponseInformation(bool? requestResponseInformation = true)
{
_options.RequestResponseInformation = requestResponseInformation;
return this;
}
public MqttClientOptionsBuilder WithSessionExpiryInterval(uint? sessionExpiryInterval)
{
_options.SessionExpiryInterval = sessionExpiryInterval;
return this;
}
public MqttClientOptionsBuilder WithCredentials(string username, string password = null)
{
_options.Credentials = new MqttClientCredentials
@@ -92,11 +141,13 @@ namespace MQTTnet.Client
return this;
}
public MqttClientOptionsBuilder WithWebSocketServer(string uri)
public MqttClientOptionsBuilder WithWebSocketServer(string uri, MqttClientOptionsBuilderWebSocketParameters parameters = null)
{
_webSocketOptions = new MqttClientWebSocketOptions
{
Uri = uri
Uri = uri,
RequestHeaders = parameters?.RequestHeaders,
CookieContainer = parameters?.CookieContainer
};
return this;
@@ -4,7 +4,7 @@ using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public class MqttClientOptionsBuilderTlsParameters
{
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Net;
namespace MQTTnet.Client.Options
{
public class MqttClientOptionsBuilderWebSocketParameters
{
public IDictionary<string, string> RequestHeaders { get; set; }
public CookieContainer CookieContainer { get; set; }
}
}
@@ -1,4 +1,4 @@
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public class MqttClientTcpOptions : IMqttClientChannelOptions
{
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public static class MqttClientTcpOptionsExtensions
{
@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public class MqttClientTlsOptions
{
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Net;
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public class MqttClientWebSocketOptions : IMqttClientChannelOptions
{
@@ -1,4 +1,4 @@
namespace MQTTnet.Client
namespace MQTTnet.Client.Options
{
public class MqttClientWebSocketProxyOptions
{
@@ -0,0 +1,12 @@
using System;
using MQTTnet.Packets;
namespace MQTTnet.Client.PacketDispatcher
{
public interface IMqttPacketAwaiter
{
void Complete(MqttBasePacket packet);
void Fail(Exception exception);
}
}
@@ -0,0 +1,37 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Packets;
namespace MQTTnet.Client.PacketDispatcher
{
public sealed class MqttPacketAwaiter<TPacket> : IMqttPacketAwaiter where TPacket : MqttBasePacket
{
private readonly TaskCompletionSource<MqttBasePacket> _packet = new TaskCompletionSource<MqttBasePacket>();
public async Task<TPacket> WaitOneAsync(TimeSpan timeout)
{
using (var timeoutToken = new CancellationTokenSource(timeout))
{
timeoutToken.Token.Register(() => _packet.TrySetCanceled());
var packet = await _packet.Task.ConfigureAwait(false);
return (TPacket)packet;
}
}
public void Complete(MqttBasePacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
_packet.TrySetResult(packet);
}
public void Fail(Exception exception)
{
if (exception == null) throw new ArgumentNullException(nameof(exception));
_packet.TrySetException(exception);
}
}
}
@@ -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();
}
}
@@ -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.");
}
}
}
}
@@ -1,14 +1,24 @@
using System.Threading;
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
namespace MQTTnet.Serializer
namespace MQTTnet.Formatter
{
public static class MqttPacketReader
public class MqttPacketReader
{
public static async Task<MqttFixedHeader> ReadFixedHeaderAsync(IMqttChannel channel, byte[] fixedHeaderBuffer, byte[] singleByteBuffer, CancellationToken cancellationToken)
private readonly byte[] _singleByteBuffer = new byte[1];
private readonly IMqttChannel _channel;
public MqttPacketReader(IMqttChannel channel)
{
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
}
public async Task<MqttFixedHeader> ReadFixedHeaderAsync(byte[] fixedHeaderBuffer, CancellationToken cancellationToken)
{
// The MQTT fixed header contains 1 byte of flags and at least 1 byte for the remaining data length.
// So in all cases at least 2 bytes must be read for a complete MQTT packet.
@@ -19,7 +29,7 @@ namespace MQTTnet.Serializer
while (totalBytesRead < buffer.Length)
{
var bytesRead = await channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
ExceptionHelper.ThrowIfGracefulSocketClose(bytesRead);
@@ -30,29 +40,30 @@ namespace MQTTnet.Serializer
var hasRemainingLength = buffer[1] != 0;
if (!hasRemainingLength)
{
return new MqttFixedHeader(buffer[0], 0);
return new MqttFixedHeader(buffer[0], 0, totalBytesRead);
}
#if WINDOWS_UWP
// UWP will have a dead lock when calling this not async.
var bodyLength = await ReadBodyLengthAsync(channel, buffer[1], singleByteBuffer, cancellationToken).ConfigureAwait(false);
var bodyLength = await ReadBodyLengthAsync(buffer[1], cancellationToken).ConfigureAwait(false);
#else
// Here the async/await pattern is not used becuase the overhead of context switches
// Here the async/await pattern is not used because the overhead of context switches
// is too big for reading 1 byte in a row. We expect that the remaining data was sent
// directly after the initial bytes. If the client disconnects just in this moment we
// will get an exception anyway.
var bodyLength = ReadBodyLength(channel, buffer[1], singleByteBuffer, cancellationToken);
var bodyLength = ReadBodyLength(buffer[1], cancellationToken);
#endif
return new MqttFixedHeader(buffer[0], bodyLength);
totalBytesRead += bodyLength;
return new MqttFixedHeader(buffer[0], bodyLength, totalBytesRead);
}
#if !WINDOWS_UWP
private static int ReadBodyLength(IMqttChannel channel, byte initialEncodedByte, byte[] singleByteBuffer, CancellationToken cancellationToken)
private int ReadBodyLength(byte initialEncodedByte, CancellationToken cancellationToken)
{
var offset = 0;
var multiplier = 128;
var value = initialEncodedByte & 127;
var value = (initialEncodedByte & 127);
int encodedByte = initialEncodedByte;
while ((encodedByte & 128) != 0)
@@ -65,18 +76,18 @@ namespace MQTTnet.Serializer
cancellationToken.ThrowIfCancellationRequested();
encodedByte = ReadByte(channel, singleByteBuffer, cancellationToken);
encodedByte = ReadByte(cancellationToken);
value += (byte)(encodedByte & 127) * multiplier;
value += (encodedByte & 127) * multiplier;
multiplier *= 128;
}
return value;
}
private static byte ReadByte(IMqttChannel channel, byte[] singleByteBuffer, CancellationToken cancellationToken)
private byte ReadByte(CancellationToken cancellationToken)
{
var readCount = channel.ReadAsync(singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
var readCount = _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
cancellationToken.ThrowIfCancellationRequested();
@@ -85,12 +96,12 @@ namespace MQTTnet.Serializer
ExceptionHelper.ThrowGracefulSocketClose();
}
return singleByteBuffer[0];
return _singleByteBuffer[0];
}
#else
private static async Task<int> ReadBodyLengthAsync(IMqttChannel channel, byte initialEncodedByte, byte[] singleByteBuffer, CancellationToken cancellationToken)
private async Task<int> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken)
{
var offset = 0;
var multiplier = 128;
@@ -107,18 +118,18 @@ namespace MQTTnet.Serializer
cancellationToken.ThrowIfCancellationRequested();
encodedByte = await ReadByteAsync(channel, singleByteBuffer, cancellationToken).ConfigureAwait(false);
encodedByte = await ReadByteAsync(cancellationToken).ConfigureAwait(false);
value += (byte)(encodedByte & 127) * multiplier;
value += (encodedByte & 127) * multiplier;
multiplier *= 128;
}
return value;
}
private static async Task<byte> ReadByteAsync(IMqttChannel channel, byte[] singleByteBuffer, CancellationToken cancellationToken)
private async Task<byte> ReadByteAsync(CancellationToken cancellationToken)
{
var readCount = await channel.ReadAsync(singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
@@ -127,7 +138,7 @@ namespace MQTTnet.Serializer
ExceptionHelper.ThrowGracefulSocketClose();
}
return singleByteBuffer[0];
return _singleByteBuffer[0];
}
#endif
@@ -1,8 +1,9 @@
using System;
using System.Runtime.CompilerServices;
using System.Text;
using MQTTnet.Protocol;
namespace MQTTnet.Serializer
namespace MQTTnet.Formatter
{
/// <summary>
/// This is a custom implementation of a memory stream which provides only MQTTnet relevant features.
@@ -16,7 +17,7 @@ namespace MQTTnet.Serializer
private byte[] _buffer = new byte[128];
private int _position;
private int _offset;
public int Length { get; private set; }
@@ -27,9 +28,9 @@ namespace MQTTnet.Serializer
return (byte)fixedHeader;
}
public static ArraySegment<byte> EncodeRemainingLength(int length)
public static ArraySegment<byte> EncodeVariableByteInteger(uint value)
{
if (length <= 0)
if (value <= 0)
{
return new ArraySegment<byte>(new byte[1], 0, 1);
}
@@ -37,7 +38,7 @@ namespace MQTTnet.Serializer
var buffer = new byte[4];
var bufferOffset = 0;
var x = length;
var x = value;
do
{
var encodedByte = x % 128;
@@ -54,6 +55,11 @@ namespace MQTTnet.Serializer
return new ArraySegment<byte>(buffer, 0, bufferOffset);
}
public void WriteVariableLengthInteger(uint value)
{
Write(EncodeVariableByteInteger(value));
}
public void WriteWithLengthPrefix(string value)
{
WriteWithLengthPrefix(Encoding.UTF8.GetBytes(value ?? string.Empty));
@@ -61,17 +67,19 @@ namespace MQTTnet.Serializer
public void WriteWithLengthPrefix(byte[] value)
{
if (value == null) throw new ArgumentNullException(nameof(value));
EnsureAdditionalCapacity(value.Length + 2);
Write((ushort)value.Length);
Write(value, 0, value.Length);
}
public void Write(byte @byte)
{
EnsureAdditionalCapacity(1);
_buffer[_position] = @byte;
_buffer[_offset] = @byte;
IncreasePostition(1);
}
@@ -79,31 +87,50 @@ namespace MQTTnet.Serializer
{
EnsureAdditionalCapacity(2);
_buffer[_position] = (byte)(value >> 8);
_buffer[_offset] = (byte)(value >> 8);
IncreasePostition(1);
_buffer[_position] = (byte)value;
_buffer[_offset] = (byte)value;
IncreasePostition(1);
}
public void Write(byte[] array, int offset, int count)
public void Write(byte[] buffer, int offset, int count)
{
if (array == null) throw new ArgumentNullException(nameof(array));
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
EnsureAdditionalCapacity(count);
Array.Copy(array, offset, _buffer, _position, count);
Array.Copy(buffer, offset, _buffer, _offset, count);
IncreasePostition(count);
}
public void Write(ArraySegment<byte> buffer)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
Write(buffer.Array, buffer.Offset, buffer.Count);
}
public void Write(MqttPacketWriter propertyWriter)
{
if (propertyWriter == null) throw new ArgumentNullException(nameof(propertyWriter));
if (propertyWriter.Length == 0)
{
return;
}
Write(propertyWriter._buffer, 0, propertyWriter.Length);
}
public void Reset()
{
Length = 5;
}
public void Seek(int offset)
public void Seek(int position)
{
EnsureCapacity(offset);
_position = offset;
EnsureCapacity(position);
_offset = position;
}
public byte[] GetBuffer()
@@ -127,9 +154,10 @@ namespace MQTTnet.Serializer
Array.Resize(ref _buffer, MaxBufferSize);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureAdditionalCapacity(int additionalCapacity)
{
var freeSpace = _buffer.Length - _position;
var freeSpace = _buffer.Length - _offset;
if (freeSpace >= additionalCapacity)
{
return;
@@ -138,6 +166,7 @@ namespace MQTTnet.Serializer
EnsureCapacity(_buffer.Length + additionalCapacity - freeSpace);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureCapacity(int capacity)
{
var newBufferLength = _buffer.Length;
@@ -146,7 +175,7 @@ namespace MQTTnet.Serializer
{
return;
}
while (newBufferLength < capacity)
{
newBufferLength *= 2;
@@ -155,13 +184,14 @@ namespace MQTTnet.Serializer
Array.Resize(ref _buffer, newBufferLength);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void IncreasePostition(int length)
{
_position += length;
_offset += length;
if (_position > Length)
if (_offset > Length)
{
Length = _position;
Length = _offset;
}
}
}
@@ -0,0 +1,9 @@
namespace MQTTnet.Formatter
{
public enum MqttProtocolVersion
{
V310 = 3,
V311 = 4,
V500 = 5
}
}
@@ -0,0 +1,147 @@
using System;
using System.Linq;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Subscribing;
using MQTTnet.Client.Unsubscribing;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V3
{
public class MqttV310DataConverter : IMqttDataConverter
{
public MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
if (applicationMessage.UserProperties?.Any() == true)
{
throw new MqttProtocolViolationException("User properties are not supported in MQTT version 3.");
}
return new MqttPublishPacket
{
Topic = applicationMessage.Topic,
Payload = applicationMessage.Payload,
QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel,
Retain = applicationMessage.Retain,
Dup = false
};
}
public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket)
{
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
return new MqttApplicationMessage
{
Topic = publishPacket.Topic,
Payload = publishPacket.Payload,
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
Retain = publishPacket.Retain
};
}
public MqttClientConnectResult CreateClientConnectResult(MqttConnAckPacket connAckPacket)
{
if (connAckPacket == null) throw new ArgumentNullException(nameof(connAckPacket));
MqttClientConnectResultCode resultCode;
switch (connAckPacket.ReturnCode.Value)
{
case MqttConnectReturnCode.ConnectionAccepted:
{
resultCode = MqttClientConnectResultCode.Success;
break;
}
case MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion:
{
resultCode = MqttClientConnectResultCode.UnsupportedProtocolVersion;
break;
}
case MqttConnectReturnCode.ConnectionRefusedNotAuthorized:
{
resultCode = MqttClientConnectResultCode.NotAuthorized;
break;
}
case MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword:
{
resultCode = MqttClientConnectResultCode.BadUserNameOrPassword;
break;
}
case MqttConnectReturnCode.ConnectionRefusedIdentifierRejected:
{
resultCode = MqttClientConnectResultCode.ClientIdentifierNotValid;
break;
}
case MqttConnectReturnCode.ConnectionRefusedServerUnavailable:
{
resultCode = MqttClientConnectResultCode.ServerUnavailable;
break;
}
default:
throw new MqttProtocolViolationException("Received unexpected return code.");
}
return new MqttClientConnectResult
{
IsSessionPresent = connAckPacket.IsSessionPresent,
ResultCode = resultCode
};
}
public MqttConnectPacket CreateConnectPacket(MqttApplicationMessage willApplicationMessage, IMqttClientOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
return new MqttConnectPacket
{
ClientId = options.ClientId,
Username = options.Credentials?.Username,
Password = options.Credentials?.Password,
CleanSession = options.CleanSession,
KeepAlivePeriod = (ushort)options.KeepAlivePeriod.TotalSeconds,
WillMessage = willApplicationMessage
};
}
public MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket)
{
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
if (subAckPacket == null) throw new ArgumentNullException(nameof(subAckPacket));
if (subAckPacket.ReturnCodes.Count != subscribePacket.TopicFilters.Count)
{
throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1].");
}
var result = new MqttClientSubscribeResult();
result.Items.AddRange(subscribePacket.TopicFilters.Select((t, i) =>
new MqttClientSubscribeResultItem(t, (MqttClientSubscribeResultCode)subAckPacket.ReturnCodes[i])));
return result;
}
public MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket)
{
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
if (unsubAckPacket == null) throw new ArgumentNullException(nameof(unsubAckPacket));
var result = new MqttClientUnsubscribeResult();
result.Items.AddRange(unsubscribePacket.TopicFilters.Select((t, i) =>
new MqttClientUnsubscribeResultItem(t, MqttClientUnsubscribeResultCode.Success)));
return result;
}
}
}
@@ -1,21 +1,22 @@
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using System;
using System;
using System.Linq;
using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Serializer
namespace MQTTnet.Formatter.V3
{
public class MqttPacketSerializer : IMqttPacketSerializer
public class MqttV310PacketFormatter : IMqttPacketFormatter
{
private const int FixedHeaderSize = 1;
private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
public IMqttDataConverter DataConverter { get; } = new MqttV310DataConverter();
public ArraySegment<byte> Serialize(MqttBasePacket packet)
public ArraySegment<byte> Encode(MqttBasePacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
@@ -23,15 +24,15 @@ namespace MQTTnet.Serializer
_packetWriter.Reset();
_packetWriter.Seek(5);
var fixedHeader = SerializePacket(packet, _packetWriter);
var remainingLength = _packetWriter.Length - 5;
var fixedHeader = EncodePacket(packet, _packetWriter);
var remainingLength = (uint)(_packetWriter.Length - 5);
var remainingLengthBuffer = MqttPacketWriter.EncodeRemainingLength(remainingLength);
var remainingLengthBuffer = MqttPacketWriter.EncodeVariableByteInteger(remainingLength);
var headerSize = FixedHeaderSize + remainingLengthBuffer.Count;
var headerOffset = 5 - headerSize;
// Position cursor on correct offset on beginining of array (has leading 0x0)
// Position cursor on correct offset on beginning of array (has leading 0x0)
_packetWriter.Seek(headerOffset);
_packetWriter.Write(fixedHeader);
_packetWriter.Write(remainingLengthBuffer.Array, remainingLengthBuffer.Offset, remainingLengthBuffer.Count);
@@ -40,7 +41,7 @@ namespace MQTTnet.Serializer
return new ArraySegment<byte>(buffer, headerOffset, _packetWriter.Length - headerOffset);
}
public MqttBasePacket Deserialize(ReceivedMqttPacket receivedMqttPacket)
public MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket)
{
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
@@ -52,20 +53,20 @@ namespace MQTTnet.Serializer
switch ((MqttControlPacketType)controlPacketType)
{
case MqttControlPacketType.Connect: return DeserializeConnect(receivedMqttPacket.Body);
case MqttControlPacketType.ConnAck: return DeserializeConnAck(receivedMqttPacket.Body);
case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body);
case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Disconnect: return new MqttDisconnectPacket();
case MqttControlPacketType.Publish: return DeserializePublish(receivedMqttPacket);
case MqttControlPacketType.PubAck: return DeserializePubAck(receivedMqttPacket.Body);
case MqttControlPacketType.PubRec: return DeserializePubRec(receivedMqttPacket.Body);
case MqttControlPacketType.PubRel: return DeserializePubRel(receivedMqttPacket.Body);
case MqttControlPacketType.PubComp: return DeserializePubComp(receivedMqttPacket.Body);
case MqttControlPacketType.Publish: return DecodePublish(receivedMqttPacket);
case MqttControlPacketType.PubAck: return DecodePubAck(receivedMqttPacket.Body);
case MqttControlPacketType.PubRec: return DecodePubRec(receivedMqttPacket.Body);
case MqttControlPacketType.PubRel: return DecodePubRel(receivedMqttPacket.Body);
case MqttControlPacketType.PubComp: return DecodePubComp(receivedMqttPacket.Body);
case MqttControlPacketType.PingReq: return new MqttPingReqPacket();
case MqttControlPacketType.PingResp: return new MqttPingRespPacket();
case MqttControlPacketType.Subscribe: return DeserializeSubscribe(receivedMqttPacket.Body);
case MqttControlPacketType.SubAck: return DeserializeSubAck(receivedMqttPacket.Body);
case MqttControlPacketType.Unsubscibe: return DeserializeUnsubscribe(receivedMqttPacket.Body);
case MqttControlPacketType.UnsubAck: return DeserializeUnsubAck(receivedMqttPacket.Body);
case MqttControlPacketType.Subscribe: return DecodeSubscribe(receivedMqttPacket.Body);
case MqttControlPacketType.SubAck: return DecodeSubAck(receivedMqttPacket.Body);
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribe(receivedMqttPacket.Body);
case MqttControlPacketType.UnsubAck: return DecodeUnsubAck(receivedMqttPacket.Body);
default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported.");
}
@@ -76,85 +77,86 @@ namespace MQTTnet.Serializer
_packetWriter.FreeBuffer();
}
private byte SerializePacket(MqttBasePacket packet, MqttPacketWriter packetWriter)
private byte EncodePacket(MqttBasePacket packet, MqttPacketWriter packetWriter)
{
switch (packet)
{
case MqttConnectPacket connectPacket: return Serialize(connectPacket, packetWriter);
case MqttConnAckPacket connAckPacket: return Serialize(connAckPacket, packetWriter);
case MqttDisconnectPacket _: return SerializeEmptyPacket(MqttControlPacketType.Disconnect);
case MqttPingReqPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingReq);
case MqttPingRespPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingResp);
case MqttPublishPacket publishPacket: return Serialize(publishPacket, packetWriter);
case MqttPubAckPacket pubAckPacket: return Serialize(pubAckPacket, packetWriter);
case MqttPubRecPacket pubRecPacket: return Serialize(pubRecPacket, packetWriter);
case MqttPubRelPacket pubRelPacket: return Serialize(pubRelPacket, packetWriter);
case MqttPubCompPacket pubCompPacket: return Serialize(pubCompPacket, packetWriter);
case MqttSubscribePacket subscribePacket: return Serialize(subscribePacket, packetWriter);
case MqttSubAckPacket subAckPacket: return Serialize(subAckPacket, packetWriter);
case MqttUnsubscribePacket unsubscribePacket: return Serialize(unsubscribePacket, packetWriter);
case MqttUnsubAckPacket unsubAckPacket: return Serialize(unsubAckPacket, packetWriter);
case MqttConnectPacket connectPacket: return EncodeConnectPacket(connectPacket, packetWriter);
case MqttConnAckPacket connAckPacket: return EncodeConnAckPacket(connAckPacket, packetWriter);
case MqttDisconnectPacket _: return EncodeEmptyPacket(MqttControlPacketType.Disconnect);
case MqttPingReqPacket _: return EncodeEmptyPacket(MqttControlPacketType.PingReq);
case MqttPingRespPacket _: return EncodeEmptyPacket(MqttControlPacketType.PingResp);
case MqttPublishPacket publishPacket: return EncodePublishPacket(publishPacket, packetWriter);
case MqttPubAckPacket pubAckPacket: return EncodePubAckPacket(pubAckPacket, packetWriter);
case MqttPubRecPacket pubRecPacket: return EncodePubRecPacket(pubRecPacket, packetWriter);
case MqttPubRelPacket pubRelPacket: return EncodePubRelPacket(pubRelPacket, packetWriter);
case MqttPubCompPacket pubCompPacket: return EncodePubCompPacket(pubCompPacket, packetWriter);
case MqttSubscribePacket subscribePacket: return EncodeSubscribePacket(subscribePacket, packetWriter);
case MqttSubAckPacket subAckPacket: return EncodeSubAckPacket(subAckPacket, packetWriter);
case MqttUnsubscribePacket unsubscribePacket: return EncodeUnsubscribePacket(unsubscribePacket, packetWriter);
case MqttUnsubAckPacket unsubAckPacket: return EncodeUnsubAckPacket(unsubAckPacket, packetWriter);
default: throw new MqttProtocolViolationException("Packet type invalid.");
}
}
private static MqttBasePacket DeserializeUnsubAck(MqttPacketBodyReader body)
private static MqttBasePacket DecodeUnsubAck(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
return new MqttUnsubAckPacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
}
private static MqttBasePacket DeserializePubComp(MqttPacketBodyReader body)
private static MqttBasePacket DecodePubComp(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
return new MqttPubCompPacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
}
private static MqttBasePacket DeserializePubRel(MqttPacketBodyReader body)
private static MqttBasePacket DecodePubRel(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
return new MqttPubRelPacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
}
private static MqttBasePacket DeserializePubRec(MqttPacketBodyReader body)
private static MqttBasePacket DecodePubRec(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
return new MqttPubRecPacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
}
private static MqttBasePacket DeserializePubAck(MqttPacketBodyReader body)
private static MqttBasePacket DecodePubAck(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
return new MqttPubAckPacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
}
private static MqttBasePacket DeserializeUnsubscribe(MqttPacketBodyReader body)
private static MqttBasePacket DecodeUnsubscribe(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttUnsubscribePacket
{
PacketIdentifier = body.ReadUInt16(),
PacketIdentifier = body.ReadTwoByteInteger(),
};
while (!body.EndOfStream)
@@ -165,26 +167,30 @@ namespace MQTTnet.Serializer
return packet;
}
private static MqttBasePacket DeserializeSubscribe(MqttPacketBodyReader body)
private static MqttBasePacket DecodeSubscribe(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttSubscribePacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
while (!body.EndOfStream)
{
packet.TopicFilters.Add(new TopicFilter(
body.ReadStringWithLengthPrefix(),
(MqttQualityOfServiceLevel)body.ReadByte()));
var topicFilter = new TopicFilter
{
Topic = body.ReadStringWithLengthPrefix(),
QualityOfServiceLevel = (MqttQualityOfServiceLevel)body.ReadByte()
};
packet.TopicFilters.Add(topicFilter);
}
return packet;
}
private static MqttBasePacket DeserializePublish(ReceivedMqttPacket receivedMqttPacket)
private static MqttBasePacket DecodePublish(ReceivedMqttPacket receivedMqttPacket)
{
ThrowIfBodyIsEmpty(receivedMqttPacket.Body);
@@ -197,7 +203,7 @@ namespace MQTTnet.Serializer
ushort? packetIdentifier = null;
if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{
packetIdentifier = receivedMqttPacket.Body.ReadUInt16();
packetIdentifier = receivedMqttPacket.Body.ReadTwoByteInteger();
}
var packet = new MqttPublishPacket
@@ -213,37 +219,15 @@ namespace MQTTnet.Serializer
return packet;
}
private static MqttBasePacket DeserializeConnect(MqttPacketBodyReader body)
private MqttBasePacket DecodeConnectPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var protocolName = body.ReadStringWithLengthPrefix();
MqttProtocolVersion protocolVersion;
if (protocolName == "MQTT")
var packet = new MqttConnectPacket
{
var protocolLevel = body.ReadByte();
if (protocolLevel != 4)
{
throw new MqttProtocolViolationException($"Protocol level ({protocolLevel}) not supported for MQTT 3.1.1.");
}
protocolVersion = MqttProtocolVersion.V311;
}
else if (protocolName == "MQIsdp")
{
var protocolLevel = body.ReadByte();
if (protocolLevel != 3)
{
throw new MqttProtocolViolationException($"Protocol level ({protocolLevel}) not supported for MQTT 3.1.");
}
protocolVersion = MqttProtocolVersion.V310;
}
else
{
throw new MqttProtocolViolationException($"Protocol name ({protocolName}) is not supported.");
}
ProtocolName = body.ReadStringWithLengthPrefix(),
ProtocolLevel = body.ReadByte()
};
var connectFlags = body.ReadByte();
if ((connectFlags & 0x1) > 0)
@@ -251,11 +235,7 @@ namespace MQTTnet.Serializer
throw new MqttProtocolViolationException("The first bit of the Connect Flags must be set to 0.");
}
var packet = new MqttConnectPacket
{
ProtocolVersion = protocolVersion,
CleanSession = (connectFlags & 0x2) > 0
};
packet.CleanSession = (connectFlags & 0x2) > 0;
var willFlag = (connectFlags & 0x4) > 0;
var willQoS = (connectFlags & 0x18) >> 3;
@@ -263,7 +243,7 @@ namespace MQTTnet.Serializer
var passwordFlag = (connectFlags & 0x40) > 0;
var usernameFlag = (connectFlags & 0x80) > 0;
packet.KeepAlivePeriod = body.ReadUInt16();
packet.KeepAlivePeriod = body.ReadTwoByteInteger();
packet.ClientId = body.ReadStringWithLengthPrefix();
if (willFlag)
@@ -291,42 +271,36 @@ namespace MQTTnet.Serializer
return packet;
}
private static MqttBasePacket DeserializeSubAck(MqttPacketBodyReader body)
private static MqttBasePacket DecodeSubAck(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttSubAckPacket
{
PacketIdentifier = body.ReadUInt16()
PacketIdentifier = body.ReadTwoByteInteger()
};
while (!body.EndOfStream)
{
packet.SubscribeReturnCodes.Add((MqttSubscribeReturnCode)body.ReadByte());
packet.ReturnCodes.Add((MqttSubscribeReturnCode)body.ReadByte());
}
return packet;
}
private MqttBasePacket DeserializeConnAck(MqttPacketBodyReader body)
protected virtual MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttConnAckPacket();
var acknowledgeFlags = body.ReadByte();
if (ProtocolVersion == MqttProtocolVersion.V311)
{
packet.IsSessionPresent = (acknowledgeFlags & 0x1) > 0;
}
packet.ConnectReturnCode = (MqttConnectReturnCode)body.ReadByte();
body.ReadByte(); // Reserved.
packet.ReturnCode = (MqttConnectReturnCode)body.ReadByte();
return packet;
}
private static void ValidateConnectPacket(MqttConnectPacket packet)
protected void ValidateConnectPacket(MqttConnectPacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
@@ -345,21 +319,12 @@ namespace MQTTnet.Serializer
}
}
private byte Serialize(MqttConnectPacket packet, MqttPacketWriter packetWriter)
protected virtual byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter)
{
ValidateConnectPacket(packet);
// Write variable header
if (ProtocolVersion == MqttProtocolVersion.V311)
{
packetWriter.WriteWithLengthPrefix("MQTT");
packetWriter.Write(4); // 3.1.2.2 Protocol Level 4
}
else
{
packetWriter.WriteWithLengthPrefix("MQIsdp");
packetWriter.Write(3); // Protocol Level 3
}
packetWriter.WriteWithLengthPrefix("MQIsdp");
packetWriter.Write(3); // Protocol Level 3
byte connectFlags = 0x0;
if (packet.CleanSession)
@@ -416,33 +381,15 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
}
private byte Serialize(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
protected virtual byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
{
if (ProtocolVersion == MqttProtocolVersion.V310)
{
packetWriter.Write(0);
}
else if (ProtocolVersion == MqttProtocolVersion.V311)
{
byte connectAcknowledgeFlags = 0x0;
if (packet.IsSessionPresent)
{
connectAcknowledgeFlags |= 0x1;
}
packetWriter.Write(connectAcknowledgeFlags);
}
else
{
throw new MqttProtocolViolationException("Protocol version not supported.");
}
packetWriter.Write((byte)packet.ConnectReturnCode);
packetWriter.Write(0); // Reserved.
packetWriter.Write((byte)packet.ReturnCode.Value);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
}
private static byte Serialize(MqttPubRelPacket packet, MqttPacketWriter packetWriter)
private static byte EncodePubRelPacket(MqttPubRelPacket packet, MqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
{
@@ -454,7 +401,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
}
private static byte Serialize(MqttPublishPacket packet, MqttPacketWriter packetWriter)
private static byte EncodePublishPacket(MqttPublishPacket packet, MqttPacketWriter packetWriter)
{
ValidatePublishPacket(packet);
@@ -499,7 +446,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader);
}
private static byte Serialize(MqttPubAckPacket packet, MqttPacketWriter packetWriter)
private static byte EncodePubAckPacket(MqttPubAckPacket packet, MqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
{
@@ -511,7 +458,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
}
private static byte Serialize(MqttPubRecPacket packet, MqttPacketWriter packetWriter)
private static byte EncodePubRecPacket(MqttPubRecPacket packet, MqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
{
@@ -523,7 +470,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
}
private static byte Serialize(MqttPubCompPacket packet, MqttPacketWriter packetWriter)
private static byte EncodePubCompPacket(MqttPubCompPacket packet, MqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
{
@@ -535,7 +482,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
}
private static byte Serialize(MqttSubscribePacket packet, MqttPacketWriter packetWriter)
private static byte EncodeSubscribePacket(MqttSubscribePacket packet, MqttPacketWriter packetWriter)
{
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
@@ -558,7 +505,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02);
}
private static byte Serialize(MqttSubAckPacket packet, MqttPacketWriter packetWriter)
private static byte EncodeSubAckPacket(MqttSubAckPacket packet, MqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
{
@@ -567,9 +514,9 @@ namespace MQTTnet.Serializer
packetWriter.Write(packet.PacketIdentifier.Value);
if (packet.SubscribeReturnCodes?.Any() == true)
if (packet.ReturnCodes?.Any() == true)
{
foreach (var packetSubscribeReturnCode in packet.SubscribeReturnCodes)
foreach (var packetSubscribeReturnCode in packet.ReturnCodes)
{
packetWriter.Write((byte)packetSubscribeReturnCode);
}
@@ -578,7 +525,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck);
}
private static byte Serialize(MqttUnsubscribePacket packet, MqttPacketWriter packetWriter)
private static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, MqttPacketWriter packetWriter)
{
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
@@ -600,7 +547,7 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
}
private static byte Serialize(MqttUnsubAckPacket packet, MqttPacketWriter packetWriter)
private static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, MqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
{
@@ -611,13 +558,13 @@ namespace MQTTnet.Serializer
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
}
private static byte SerializeEmptyPacket(MqttControlPacketType type)
private static byte EncodeEmptyPacket(MqttControlPacketType type)
{
return MqttPacketWriter.BuildFixedHeader(type);
}
// ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local
private static void ThrowIfBodyIsEmpty(MqttPacketBodyReader body)
protected static void ThrowIfBodyIsEmpty(MqttPacketBodyReader body)
{
if (body == null || body.Length == 0)
{
@@ -0,0 +1,99 @@
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V3
{
public class MqttV311PacketFormatter : MqttV310PacketFormatter
{
protected override byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter)
{
ValidateConnectPacket(packet);
packetWriter.WriteWithLengthPrefix("MQTT");
packetWriter.Write(4); // 3.1.2.2 Protocol Level 4
byte connectFlags = 0x0;
if (packet.CleanSession)
{
connectFlags |= 0x2;
}
if (packet.WillMessage != null)
{
connectFlags |= 0x4;
connectFlags |= (byte)((byte)packet.WillMessage.QualityOfServiceLevel << 3);
if (packet.WillMessage.Retain)
{
connectFlags |= 0x20;
}
}
if (packet.Password != null && packet.Username == null)
{
throw new MqttProtocolViolationException("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].");
}
if (packet.Password != null)
{
connectFlags |= 0x40;
}
if (packet.Username != null)
{
connectFlags |= 0x80;
}
packetWriter.Write(connectFlags);
packetWriter.Write(packet.KeepAlivePeriod);
packetWriter.WriteWithLengthPrefix(packet.ClientId);
if (packet.WillMessage != null)
{
packetWriter.WriteWithLengthPrefix(packet.WillMessage.Topic);
packetWriter.WriteWithLengthPrefix(packet.WillMessage.Payload);
}
if (packet.Username != null)
{
packetWriter.WriteWithLengthPrefix(packet.Username);
}
if (packet.Password != null)
{
packetWriter.WriteWithLengthPrefix(packet.Password);
}
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
}
protected override byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
{
byte connectAcknowledgeFlags = 0x0;
if (packet.IsSessionPresent)
{
connectAcknowledgeFlags |= 0x1;
}
packetWriter.Write(connectAcknowledgeFlags);
packetWriter.Write((byte)packet.ReturnCode);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
}
protected override MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttConnAckPacket();
var acknowledgeFlags = body.ReadByte();
packet.IsSessionPresent = (acknowledgeFlags & 0x1) > 0;
packet.ReturnCode = (MqttConnectReturnCode)body.ReadByte();
return packet;
}
}
}
@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Subscribing;
using MQTTnet.Client.Unsubscribing;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
namespace MQTTnet.Formatter.V5
{
public class MqttV500DataConverter : IMqttDataConverter
{
public MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
var packet = new MqttPublishPacket
{
Topic = applicationMessage.Topic,
Payload = applicationMessage.Payload,
QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel,
Retain = applicationMessage.Retain,
Dup = false,
Properties = new MqttPublishPacketProperties
{
ContentType = applicationMessage.ContentType,
CorrelationData = applicationMessage.CorrelationData,
MessageExpiryInterval = applicationMessage.MessageExpiryInterval,
PayloadFormatIndicator = applicationMessage.PayloadFormatIndicator,
ResponseTopic = applicationMessage.ResponseTopic,
SubscriptionIdentifier = applicationMessage.SubscriptionIdentifier,
TopicAlias = applicationMessage.TopicAlias
}
};
if (applicationMessage.UserProperties != null)
{
packet.Properties.UserProperties.AddRange(applicationMessage.UserProperties);
}
return packet;
}
public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket)
{
return new MqttApplicationMessage
{
Topic = publishPacket.Topic,
Payload = publishPacket.Payload,
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
Retain = publishPacket.Retain,
UserProperties = publishPacket.Properties?.UserProperties ?? new List<MqttUserProperty>()
};
}
public MqttClientConnectResult CreateClientConnectResult(MqttConnAckPacket connAckPacket)
{
if (connAckPacket == null) throw new ArgumentNullException(nameof(connAckPacket));
return new MqttClientConnectResult
{
IsSessionPresent = connAckPacket.IsSessionPresent,
ResultCode = (MqttClientConnectResultCode)connAckPacket.ReasonCode.Value
};
}
public MqttConnectPacket CreateConnectPacket(MqttApplicationMessage willApplicationMessage, IMqttClientOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
return new MqttConnectPacket
{
ClientId = options.ClientId,
Username = options.Credentials?.Username,
Password = options.Credentials?.Password,
CleanSession = options.CleanSession,
KeepAlivePeriod = (ushort)options.KeepAlivePeriod.TotalSeconds,
WillMessage = willApplicationMessage,
Properties = new MqttConnectPacketProperties
{
AuthenticationMethod = options.AuthenticationMethod,
AuthenticationData = options.AuthenticationData,
WillDelayInterval = options.WillDelayInterval,
MaximumPacketSize = options.MaximumPacketSize,
ReceiveMaximum = options.ReceiveMaximum,
RequestProblemInformation = options.RequestProblemInformation,
RequestResponseInformation = options.RequestResponseInformation,
SessionExpiryInterval = options.SessionExpiryInterval,
TopicAliasMaximum = options.TopicAliasMaximum
}
};
}
public MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket)
{
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
if (subAckPacket == null) throw new ArgumentNullException(nameof(subAckPacket));
if (subAckPacket.ReasonCodes.Count != subscribePacket.TopicFilters.Count)
{
throw new MqttProtocolViolationException("The reason codes are not matching the topic filters [MQTT-3.9.3-1].");
}
var result = new MqttClientSubscribeResult();
result.Items.AddRange(subscribePacket.TopicFilters.Select((t, i) =>
new MqttClientSubscribeResultItem(t, (MqttClientSubscribeResultCode)subAckPacket.ReasonCodes[i])));
return result;
}
public MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket)
{
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
if (unsubAckPacket == null) throw new ArgumentNullException(nameof(unsubAckPacket));
if (unsubAckPacket.ReasonCodes.Count != unsubscribePacket.TopicFilters.Count)
{
throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1].");
}
var result = new MqttClientUnsubscribeResult();
result.Items.AddRange(unsubscribePacket.TopicFilters.Select((t, i) =>
new MqttClientUnsubscribeResultItem(t, (MqttClientUnsubscribeResultCode)unsubAckPacket.ReasonCodes[i])));
return result;
}
}
}
@@ -0,0 +1,717 @@
using System;
using System.Linq;
using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V5
{
public class MqttV500PacketDecoder
{
public MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket)
{
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
var controlPacketType = receivedMqttPacket.FixedHeader >> 4;
if (controlPacketType < 1 || controlPacketType > 15)
{
throw new MqttProtocolViolationException($"The packet type is invalid ({controlPacketType}).");
}
switch ((MqttControlPacketType)controlPacketType)
{
case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body);
case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Disconnect: return DecodeDisconnectPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket.FixedHeader, receivedMqttPacket.Body);
case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.Body);
case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.Body);
case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.Body);
case MqttControlPacketType.PingReq: return DecodePingReqPacket();
case MqttControlPacketType.PingResp: return DecodePingRespPacket();
case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.Body);
case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.Body);
case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Auth: return DecodeAuthPacket(receivedMqttPacket.Body);
default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported.");
}
}
private static MqttBasePacket DecodeConnectPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttConnectPacket
{
Properties = new MqttConnectPacketProperties()
};
var protocolName = body.ReadStringWithLengthPrefix();
var protocolVersion = body.ReadByte();
if (protocolName != "MQTT" && protocolVersion != 5)
{
throw new MqttProtocolViolationException("MQTT protocol name and version do not match MQTT v5.");
}
var connectFlags = body.ReadByte();
var cleanSessionFlag = (connectFlags & 0x02) > 0;
var willMessageFlag = (connectFlags & 0x04) > 0;
var willMessageQoS = (byte)(connectFlags >> 3 & 3);
var willMessageRetainFlag = (connectFlags & 0x20) > 0;
var passwordFlag = (connectFlags & 0x40) > 0;
var usernameFlag = (connectFlags & 0x80) > 0;
packet.CleanSession = cleanSessionFlag;
if (willMessageFlag)
{
packet.WillMessage = new MqttApplicationMessage
{
QualityOfServiceLevel = (MqttQualityOfServiceLevel)willMessageQoS,
Retain = willMessageRetainFlag
};
}
packet.KeepAlivePeriod = body.ReadTwoByteInteger();
var propertiesReader = new MqttV500PropertiesReader(body);
while (propertiesReader.MoveNext())
{
if (propertiesReader.CurrentPropertyId == MqttPropertyId.SessionExpiryInterval)
{
packet.Properties.SessionExpiryInterval = propertiesReader.ReadSessionExpiryInterval();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationMethod)
{
packet.Properties.AuthenticationMethod = propertiesReader.ReadAuthenticationMethod();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.WillDelayInterval)
{
packet.Properties.WillDelayInterval = propertiesReader.ReadWillDelayInterval();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationData)
{
packet.Properties.AuthenticationData = propertiesReader.ReadAuthenticationData();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReceiveMaximum)
{
packet.Properties.ReceiveMaximum = propertiesReader.ReadReceiveMaximum();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.TopicAliasMaximum)
{
packet.Properties.TopicAliasMaximum = propertiesReader.ReadTopicAliasMaximum();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.MaximumPacketSize)
{
packet.Properties.MaximumPacketSize = propertiesReader.ReadMaximumPacketSize();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.RequestResponseInformation)
{
packet.Properties.RequestResponseInformation = propertiesReader.RequestResponseInformation();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.RequestProblemInformation)
{
packet.Properties.RequestProblemInformation = propertiesReader.RequestProblemInformation();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
{
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
}
else
{
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttConnAckPacket));
}
}
packet.ClientId = body.ReadStringWithLengthPrefix();
if (packet.WillMessage != null)
{
packet.WillMessage.Topic = body.ReadStringWithLengthPrefix();
packet.WillMessage.Payload = body.ReadWithLengthPrefix().ToArray();
}
if (usernameFlag)
{
packet.Username = body.ReadStringWithLengthPrefix();
}
if (passwordFlag)
{
packet.Password = body.ReadStringWithLengthPrefix();
}
return packet;
}
private static MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var acknowledgeFlags = body.ReadByte();
var packet = new MqttConnAckPacket
{
IsSessionPresent = (acknowledgeFlags & 0x1) > 0,
ReasonCode = (MqttConnectReasonCode)body.ReadByte(),
Properties = new MqttConnAckPacketProperties()
};
var propertiesReader = new MqttV500PropertiesReader(body);
while (propertiesReader.MoveNext())
{
if (propertiesReader.CurrentPropertyId == MqttPropertyId.SessionExpiryInterval)
{
packet.Properties.SessionExpiryInterval = propertiesReader.ReadSessionExpiryInterval();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationMethod)
{
packet.Properties.AuthenticationMethod = propertiesReader.ReadAuthenticationMethod();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationData)
{
packet.Properties.AuthenticationData = propertiesReader.ReadAuthenticationData();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.RetainAvailable)
{
packet.Properties.RetainAvailable = propertiesReader.ReadRetainAvailable();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReceiveMaximum)
{
packet.Properties.ReceiveMaximum = propertiesReader.ReadReceiveMaximum();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AssignedClientIdentifer)
{
packet.Properties.AssignedClientIdentifier = propertiesReader.ReadAssignedClientIdentifier();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.TopicAliasMaximum)
{
packet.Properties.TopicAliasMaximum = propertiesReader.ReadTopicAliasMaximum();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
{
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.MaximumPacketSize)
{
packet.Properties.MaximumPacketSize = propertiesReader.ReadMaximumPacketSize();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.WildcardSubscriptionAvailable)
{
packet.Properties.WildcardSubscriptionAvailable = propertiesReader.ReadWildcardSubscriptionAvailable();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.SubscriptionIdentifiersAvailable)
{
packet.Properties.SubscriptionIdentifiersAvailable = propertiesReader.ReadSubscriptionIdentifiersAvailable();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.SharedSubscriptionAvailable)
{
packet.Properties.SharedSubscriptionAvailable = propertiesReader.ReadSharedSubscriptionAvailable();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ServerKeepAlive)
{
packet.Properties.ServerKeepAlive = propertiesReader.ReadServerKeepAlive();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ResponseInformation)
{
packet.Properties.ResponseInformation = propertiesReader.ReadResponseInformation();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ServerReference)
{
packet.Properties.ServerReference = propertiesReader.ReadServerReference();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
{
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
}
else
{
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttConnAckPacket));
}
}
return packet;
}
private static MqttBasePacket DecodeDisconnectPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttDisconnectPacket
{
ReasonCode = (MqttDisconnectReasonCode)body.ReadByte(),
Properties = new MqttDisconnectPacketProperties()
};
var propertiesReader = new MqttV500PropertiesReader(body);
while (propertiesReader.MoveNext())
{
if (propertiesReader.CurrentPropertyId == MqttPropertyId.SessionExpiryInterval)
{
packet.Properties.SessionExpiryInterval = propertiesReader.ReadSessionExpiryInterval();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
{
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ServerReference)
{
packet.Properties.ServerReference = propertiesReader.ReadServerReference();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
{
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
}
else
{
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttDisconnectPacket));
}
}
return packet;
}
private static MqttBasePacket DecodeSubscribePacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttSubscribePacket
{
PacketIdentifier = body.ReadTwoByteInteger(),
Properties = new MqttSubscribePacketProperties()
};
var propertiesReader = new MqttV500PropertiesReader(body);
while (propertiesReader.MoveNext())
{
if (propertiesReader.CurrentPropertyId == MqttPropertyId.SubscriptionIdentifier)
{
packet.Properties.SubscriptionIdentifier = propertiesReader.ReadSubscriptionIdentifier();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
{
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
}
else
{
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttSubscribePacket));
}
}
while (!body.EndOfStream)
{
var topic = body.ReadStringWithLengthPrefix();
var options = body.ReadByte();
var qos = (MqttQualityOfServiceLevel)(options & 3);
var noLocal = (options & (1 << 2)) > 0;
var retainAsPublished = (options & (1 << 3)) > 0;
var retainHandling = (MqttRetainHandling)((options >> 4) & 3);
packet.TopicFilters.Add(new TopicFilter
{
Topic = topic,
QualityOfServiceLevel = qos,
NoLocal = noLocal,
RetainAsPublished = retainAsPublished,
RetainHandling = retainHandling
});
}
return packet;
}
private static MqttBasePacket DecodeSubAckPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttSubAckPacket
{
PacketIdentifier = body.ReadTwoByteInteger(),
Properties = new MqttSubAckPacketProperties()
};
var propertiesReader = new MqttV500PropertiesReader(body);
while (propertiesReader.MoveNext())
{
if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
{
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
{
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
}
else
{
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttSubAckPacket));
}
}
while (!body.EndOfStream)
{
var reasonCode = (MqttSubscribeReasonCode)body.ReadByte();
packet.ReasonCodes.Add(reasonCode);
}
return packet;
}
private static MqttBasePacket DecodeUnsubscribePacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttUnsubscribePacket
{
PacketIdentifier = body.ReadTwoByteInteger(),
Properties = new MqttUnsubscribePacketProperties()
};
var propertiesReader = new MqttV500PropertiesReader(body);
while (propertiesReader.MoveNext())
{
if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
{
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
}
else
{
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttUnsubscribePacket));
}
}
while (!body.EndOfStream)
{
packet.TopicFilters.Add(body.ReadStringWithLengthPrefix());
}
return packet;
}
private static MqttBasePacket DecodeUnsubAckPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttUnsubAckPacket
{
PacketIdentifier = body.ReadTwoByteInteger(),
Properties = new MqttUnsubAckPacketProperties()
};
var propertiesReader = new MqttV500PropertiesReader(body);
while (propertiesReader.MoveNext())
{
if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
{
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
{
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
}
else
{
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttUnsubAckPacket));
}
}
while (!body.EndOfStream)
{
var reasonCode = (MqttUnsubscribeReasonCode)body.ReadByte();
packet.ReasonCodes.Add(reasonCode);
}
return packet;
}
private static MqttBasePacket DecodePingReqPacket()
{
return new MqttPingReqPacket();
}
private static MqttBasePacket DecodePingRespPacket()
{
return new MqttPingRespPacket();
}
private static MqttBasePacket DecodePublishPacket(byte header, MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var retain = (header & 1) > 0;
var qos = (MqttQualityOfServiceLevel)(header >> 1 & 3);
var dup = (header >> 3 & 1) > 0;
var packet = new MqttPublishPacket
{
Topic = body.ReadStringWithLengthPrefix(),
Retain = retain,
QualityOfServiceLevel = qos,
Dup = dup,
Properties = new MqttPublishPacketProperties()
};
if (qos > 0)
{
packet.PacketIdentifier = body.ReadTwoByteInteger();
}
var propertiesReader = new MqttV500PropertiesReader(body);
while (propertiesReader.MoveNext())
{
if (propertiesReader.CurrentPropertyId == MqttPropertyId.PayloadFormatIndicator)
{
packet.Properties.PayloadFormatIndicator = propertiesReader.ReadPayloadFormatIndicator();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.MessageExpiryInterval)
{
packet.Properties.MessageExpiryInterval = propertiesReader.ReadMessageExpiryInterval();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.TopicAlias)
{
packet.Properties.TopicAlias = propertiesReader.ReadTopicAlias();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ResponseTopic)
{
packet.Properties.ResponseTopic = propertiesReader.ReadResponseTopic();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.CorrelationData)
{
packet.Properties.CorrelationData = propertiesReader.ReadCorrelationData();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.SubscriptionIdentifier)
{
packet.Properties.SubscriptionIdentifier = propertiesReader.ReadSubscriptionIdentifier();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ContentType)
{
packet.Properties.ContentType = propertiesReader.ReadContentType();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
{
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
}
else
{
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPublishPacket));
}
}
packet.Payload = body.ReadRemainingData().ToArray();
return packet;
}
private static MqttBasePacket DecodePubAckPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttPubAckPacket
{
PacketIdentifier = body.ReadTwoByteInteger(),
Properties = new MqttPubAckPacketProperties()
};
if (body.EndOfStream)
{
packet.ReasonCode = MqttPubAckReasonCode.Success;
return packet;
}
packet.ReasonCode = (MqttPubAckReasonCode)body.ReadByte();
var propertiesReader = new MqttV500PropertiesReader(body);
while (propertiesReader.MoveNext())
{
if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
{
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
{
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
}
else
{
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPubAckPacket));
}
}
return packet;
}
private static MqttBasePacket DecodePubRecPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttPubRecPacket
{
PacketIdentifier = body.ReadTwoByteInteger(),
Properties = new MqttPubRecPacketProperties()
};
if (body.EndOfStream)
{
packet.ReasonCode = MqttPubRecReasonCode.Success;
return packet;
}
packet.ReasonCode = (MqttPubRecReasonCode)body.ReadByte();
var propertiesReader = new MqttV500PropertiesReader(body);
while (propertiesReader.MoveNext())
{
if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
{
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
{
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
}
else
{
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPubRecPacket));
}
}
return packet;
}
private static MqttBasePacket DecodePubRelPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttPubRelPacket
{
PacketIdentifier = body.ReadTwoByteInteger(),
Properties = new MqttPubRelPacketProperties()
};
if (body.EndOfStream)
{
packet.ReasonCode = MqttPubRelReasonCode.Success;
return packet;
}
packet.ReasonCode = (MqttPubRelReasonCode)body.ReadByte();
var propertiesReader = new MqttV500PropertiesReader(body);
while (propertiesReader.MoveNext())
{
if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
{
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
{
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
}
else
{
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPubRelPacket));
}
}
return packet;
}
private static MqttBasePacket DecodePubCompPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttPubCompPacket
{
PacketIdentifier = body.ReadTwoByteInteger(),
Properties = new MqttPubCompPacketProperties()
};
if (body.EndOfStream)
{
packet.ReasonCode = MqttPubCompReasonCode.Success;
return packet;
}
packet.ReasonCode = (MqttPubCompReasonCode)body.ReadByte();
var propertiesReader = new MqttV500PropertiesReader(body);
while (propertiesReader.MoveNext())
{
if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
{
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
{
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
}
else
{
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttPubCompPacket));
}
}
return packet;
}
private static MqttBasePacket DecodeAuthPacket(MqttPacketBodyReader body)
{
ThrowIfBodyIsEmpty(body);
var packet = new MqttAuthPacket
{
Properties = new MqttAuthPacketProperties()
};
if (body.EndOfStream)
{
packet.ReasonCode = MqttAuthenticateReasonCode.Success;
return packet;
}
packet.ReasonCode = (MqttAuthenticateReasonCode)body.ReadByte();
var propertiesReader = new MqttV500PropertiesReader(body);
while (propertiesReader.MoveNext())
{
if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationMethod)
{
packet.Properties.AuthenticationMethod = propertiesReader.ReadAuthenticationMethod();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AuthenticationData)
{
packet.Properties.AuthenticationData = propertiesReader.ReadAuthenticationData();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.ReasonString)
{
packet.Properties.ReasonString = propertiesReader.ReadReasonString();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.UserProperty)
{
propertiesReader.FillUserProperties(packet.Properties.UserProperties);
}
else
{
propertiesReader.ThrowInvalidPropertyIdException(typeof(MqttAuthPacket));
}
}
return packet;
}
// ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local
private static void ThrowIfBodyIsEmpty(MqttPacketBodyReader body)
{
if (body == null || body.Length == 0)
{
throw new MqttProtocolViolationException("Data from the body is required but not present.");
}
}
}
}
@@ -0,0 +1,572 @@
using System;
using System.Linq;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V5
{
public class MqttV500PacketEncoder
{
private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
public ArraySegment<byte> Encode(MqttBasePacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
// Leave enough head space for max header size (fixed + 4 variable remaining length = 5 bytes)
_packetWriter.Reset();
_packetWriter.Seek(5);
var fixedHeader = EncodePacket(packet, _packetWriter);
var remainingLength = (uint)(_packetWriter.Length - 5);
var remainingLengthBuffer = MqttPacketWriter.EncodeVariableByteInteger(remainingLength);
var headerSize = 1 + remainingLengthBuffer.Count;
var headerOffset = 5 - headerSize;
// Position cursor on correct offset on beginning of array (has leading 0x0)
_packetWriter.Seek(headerOffset);
_packetWriter.Write(fixedHeader);
_packetWriter.Write(remainingLengthBuffer.Array, remainingLengthBuffer.Offset, remainingLengthBuffer.Count);
var buffer = _packetWriter.GetBuffer();
return new ArraySegment<byte>(buffer, headerOffset, _packetWriter.Length - headerOffset);
}
public void FreeBuffer()
{
_packetWriter.FreeBuffer();
}
private static byte EncodePacket(MqttBasePacket packet, MqttPacketWriter packetWriter)
{
switch (packet)
{
case MqttConnectPacket connectPacket: return EncodeConnectPacket(connectPacket, packetWriter);
case MqttConnAckPacket connAckPacket: return EncodeConnAckPacket(connAckPacket, packetWriter);
case MqttDisconnectPacket disconnectPacket: return EncodeDisconnectPacket(disconnectPacket, packetWriter);
case MqttPingReqPacket _: return EncodePingReqPacket();
case MqttPingRespPacket _: return EncodePingRespPacket();
case MqttPublishPacket publishPacket: return EncodePublishPacket(publishPacket, packetWriter);
case MqttPubAckPacket pubAckPacket: return EncodePubAckPacket(pubAckPacket, packetWriter);
case MqttPubRecPacket pubRecPacket: return EncodePubRecPacket(pubRecPacket, packetWriter);
case MqttPubRelPacket pubRelPacket: return EncodePubRelPacket(pubRelPacket, packetWriter);
case MqttPubCompPacket pubCompPacket: return EncodePubCompPacket(pubCompPacket, packetWriter);
case MqttSubscribePacket subscribePacket: return EncodeSubscribePacket(subscribePacket, packetWriter);
case MqttSubAckPacket subAckPacket: return EncodeSubAckPacket(subAckPacket, packetWriter);
case MqttUnsubscribePacket unsubscribePacket: return EncodeUnsubscribePacket(unsubscribePacket, packetWriter);
case MqttUnsubAckPacket unsubAckPacket: return EncodeUnsubAckPacket(unsubAckPacket, packetWriter);
case MqttAuthPacket authPacket: return EncodeAuthPacket(authPacket, packetWriter);
default: throw new MqttProtocolViolationException("Packet type invalid.");
}
}
private static byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession)
{
throw new MqttProtocolViolationException("CleanSession must be set if ClientId is empty [MQTT-3.1.3-7].");
}
packetWriter.WriteWithLengthPrefix("MQTT");
packetWriter.Write(5); // [3.1.2.2 Protocol Version]
byte connectFlags = 0x0;
if (packet.CleanSession)
{
connectFlags |= 0x2;
}
if (packet.WillMessage != null)
{
connectFlags |= 0x4;
connectFlags |= (byte)((byte)packet.WillMessage.QualityOfServiceLevel << 3);
if (packet.WillMessage.Retain)
{
connectFlags |= 0x20;
}
}
if (packet.Password != null && packet.Username == null)
{
throw new MqttProtocolViolationException("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].");
}
if (packet.Password != null)
{
connectFlags |= 0x40;
}
if (packet.Username != null)
{
connectFlags |= 0x80;
}
packetWriter.Write(connectFlags);
packetWriter.Write(packet.KeepAlivePeriod);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WriteWillDelayInterval(packet.Properties.WillDelayInterval);
propertiesWriter.WriteSessionExpiryInterval(packet.Properties.SessionExpiryInterval);
propertiesWriter.WriteAuthenticationMethod(packet.Properties.AuthenticationMethod);
propertiesWriter.WriteAuthenticationData(packet.Properties.AuthenticationData);
propertiesWriter.WriteRequestProblemInformation(packet.Properties.RequestProblemInformation);
propertiesWriter.WriteRequestResponseInformation(packet.Properties.RequestResponseInformation);
propertiesWriter.WriteReceiveMaximum(packet.Properties.ReceiveMaximum);
propertiesWriter.WriteTopicAlias(packet.Properties.TopicAliasMaximum);
propertiesWriter.WriteMaximumPacketSize(packet.Properties.MaximumPacketSize);
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
propertiesWriter.WriteToPacket(packetWriter);
packetWriter.WriteWithLengthPrefix(packet.ClientId);
if (packet.WillMessage != null)
{
packetWriter.WriteWithLengthPrefix(packet.WillMessage.Topic);
packetWriter.WriteWithLengthPrefix(packet.WillMessage.Payload);
}
if (packet.Username != null)
{
packetWriter.WriteWithLengthPrefix(packet.Username);
}
if (packet.Password != null)
{
packetWriter.WriteWithLengthPrefix(packet.Password);
}
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
}
private static byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
if (!packet.ReasonCode.HasValue)
{
ThrowReasonCodeNotSetException();
}
byte connectAcknowledgeFlags = 0x0;
if (packet.IsSessionPresent)
{
connectAcknowledgeFlags |= 0x1;
}
packetWriter.Write(connectAcknowledgeFlags);
packetWriter.Write((byte)packet.ReasonCode.Value);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WriteSessionExpiryInterval(packet.Properties.SessionExpiryInterval);
propertiesWriter.WriteAuthenticationMethod(packet.Properties.AuthenticationMethod);
propertiesWriter.WriteAuthenticationData(packet.Properties.AuthenticationData);
propertiesWriter.WriteRetainAvailable(packet.Properties.RetainAvailable);
propertiesWriter.WriteReceiveMaximum(packet.Properties.ReceiveMaximum);
propertiesWriter.WriteAssignedClientIdentifier(packet.Properties.AssignedClientIdentifier);
propertiesWriter.WriteTopicAliasMaximum(packet.Properties.TopicAliasMaximum);
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
propertiesWriter.WriteMaximumPacketSize(packet.Properties.MaximumPacketSize);
propertiesWriter.WriteWildcardSubscriptionAvailable(packet.Properties.WildcardSubscriptionAvailable);
propertiesWriter.WriteSubscriptionIdentifiersAvailable(packet.Properties.SubscriptionIdentifiersAvailable);
propertiesWriter.WriteSharedSubscriptionAvailable(packet.Properties.SharedSubscriptionAvailable);
propertiesWriter.WriteServerKeepAlive(packet.Properties.ServerKeepAlive);
propertiesWriter.WriteResponseInformation(packet.Properties.ResponseInformation);
propertiesWriter.WriteServerReference(packet.Properties.ServerReference);
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
propertiesWriter.WriteToPacket(packetWriter);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
}
private static byte EncodePublishPacket(MqttPublishPacket packet, MqttPacketWriter packetWriter)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
if (packet.QualityOfServiceLevel == 0 && packet.Dup)
{
throw new MqttProtocolViolationException("Dup flag must be false for QoS 0 packets [MQTT-3.3.1-2].");
}
packetWriter.WriteWithLengthPrefix(packet.Topic);
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{
if (!packet.PacketIdentifier.HasValue)
{
throw new MqttProtocolViolationException("Publish packet has no packet identifier.");
}
packetWriter.Write(packet.PacketIdentifier.Value);
}
else
{
if (packet.PacketIdentifier > 0)
{
throw new MqttProtocolViolationException("Packet identifier must be 0 if QoS == 0 [MQTT-2.3.1-5].");
}
}
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WritePayloadFormatIndicator(packet.Properties.PayloadFormatIndicator);
propertiesWriter.WriteMessageExpiryInterval(packet.Properties.MessageExpiryInterval);
propertiesWriter.WriteTopicAlias(packet.Properties.TopicAlias);
propertiesWriter.WriteResponseTopic(packet.Properties.ResponseTopic);
propertiesWriter.WriteCorrelationData(packet.Properties.CorrelationData);
propertiesWriter.WriteSubscriptionIdentifier(packet.Properties.SubscriptionIdentifier);
propertiesWriter.WriteContentType(packet.Properties.ContentType);
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
propertiesWriter.WriteToPacket(packetWriter);
if (packet.Payload?.Length > 0)
{
packetWriter.Write(packet.Payload, 0, packet.Payload.Length);
}
byte fixedHeader = 0;
if (packet.Retain)
{
fixedHeader |= 0x01;
}
fixedHeader |= (byte)((byte)packet.QualityOfServiceLevel << 1);
if (packet.Dup)
{
fixedHeader |= 0x08;
}
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader);
}
private static byte EncodePubAckPacket(MqttPubAckPacket packet, MqttPacketWriter packetWriter)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
if (!packet.PacketIdentifier.HasValue)
{
throw new MqttProtocolViolationException("PubAck packet has no packet identifier.");
}
if (!packet.ReasonCode.HasValue)
{
throw new MqttProtocolViolationException("PubAck packet must contain a connect reason.");
}
packetWriter.Write(packet.PacketIdentifier.Value);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubAckReasonCode.Success)
{
packetWriter.Write((byte)packet.ReasonCode.Value);
propertiesWriter.WriteToPacket(packetWriter);
}
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
}
private static byte EncodePubRecPacket(MqttPubRecPacket packet, MqttPacketWriter packetWriter)
{
ThrowIfPacketIdentifierIsInvalid(packet);
if (!packet.ReasonCode.HasValue)
{
ThrowReasonCodeNotSetException();
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write((byte)packet.ReasonCode.Value);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRecReasonCode.Success)
{
packetWriter.Write((byte)packet.ReasonCode.Value);
propertiesWriter.WriteToPacket(packetWriter);
}
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
}
private static byte EncodePubRelPacket(MqttPubRelPacket packet, MqttPacketWriter packetWriter)
{
ThrowIfPacketIdentifierIsInvalid(packet);
if (!packet.ReasonCode.HasValue)
{
ThrowReasonCodeNotSetException();
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write((byte)packet.ReasonCode.Value);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRelReasonCode.Success)
{
packetWriter.Write((byte)packet.ReasonCode.Value);
propertiesWriter.WriteToPacket(packetWriter);
}
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
}
private static byte EncodePubCompPacket(MqttPubCompPacket packet, MqttPacketWriter packetWriter)
{
ThrowIfPacketIdentifierIsInvalid(packet);
if (!packet.ReasonCode.HasValue)
{
ThrowReasonCodeNotSetException();
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write((byte)packet.ReasonCode.Value);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubCompReasonCode.Success)
{
packetWriter.Write((byte)packet.ReasonCode.Value);
propertiesWriter.WriteToPacket(packetWriter);
}
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
}
private static byte EncodeSubscribePacket(MqttSubscribePacket packet, MqttPacketWriter packetWriter)
{
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
ThrowIfPacketIdentifierIsInvalid(packet);
packetWriter.Write(packet.PacketIdentifier.Value);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WriteSubscriptionIdentifier(packet.Properties.SubscriptionIdentifier);
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
propertiesWriter.WriteToPacket(packetWriter);
if (packet.TopicFilters?.Count > 0)
{
foreach (var topicFilter in packet.TopicFilters)
{
packetWriter.WriteWithLengthPrefix(topicFilter.Topic);
var options = (byte)topicFilter.QualityOfServiceLevel;
if (topicFilter.NoLocal == true)
{
options |= 1 << 2;
}
if (topicFilter.RetainAsPublished == true)
{
options |= 1 << 3;
}
if (topicFilter.RetainHandling.HasValue)
{
options |= (byte)((byte)topicFilter.RetainHandling.Value << 4);
}
packetWriter.Write(options);
}
}
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02);
}
private static byte EncodeSubAckPacket(MqttSubAckPacket packet, MqttPacketWriter packetWriter)
{
ThrowIfPacketIdentifierIsInvalid(packet);
if (packet.ReasonCodes == null || !packet.ReasonCodes.Any())
{
throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
}
packetWriter.Write(packet.PacketIdentifier.Value);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
propertiesWriter.WriteToPacket(packetWriter);
foreach (var reasonCode in packet.ReasonCodes)
{
packetWriter.Write((byte)reasonCode);
}
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck);
}
private static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, MqttPacketWriter packetWriter)
{
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
ThrowIfPacketIdentifierIsInvalid(packet);
packetWriter.Write(packet.PacketIdentifier.Value);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
propertiesWriter.WriteToPacket(packetWriter);
foreach (var topicFilter in packet.TopicFilters)
{
packetWriter.WriteWithLengthPrefix(topicFilter);
}
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
}
private static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, MqttPacketWriter packetWriter)
{
ThrowIfPacketIdentifierIsInvalid(packet);
if (packet.ReasonCodes == null || !packet.ReasonCodes.Any())
{
throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
}
packetWriter.Write(packet.PacketIdentifier.Value);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
propertiesWriter.WriteToPacket(packetWriter);
foreach (var reasonCode in packet.ReasonCodes)
{
packetWriter.Write((byte)reasonCode);
}
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
}
private static byte EncodeDisconnectPacket(MqttDisconnectPacket packet, MqttPacketWriter packetWriter)
{
if (!packet.ReasonCode.HasValue)
{
ThrowReasonCodeNotSetException();
}
packetWriter.Write((byte)packet.ReasonCode.Value);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WriteServerReference(packet.Properties.ServerReference);
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
propertiesWriter.WriteSessionExpiryInterval(packet.Properties.SessionExpiryInterval);
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
propertiesWriter.WriteToPacket(packetWriter);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Disconnect);
}
private static byte EncodePingReqPacket()
{
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingReq);
}
private static byte EncodePingRespPacket()
{
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingResp);
}
private static byte EncodeAuthPacket(MqttAuthPacket packet, MqttPacketWriter packetWriter)
{
packetWriter.Write((byte)packet.ReasonCode);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WriteAuthenticationMethod(packet.Properties.AuthenticationMethod);
propertiesWriter.WriteAuthenticationData(packet.Properties.AuthenticationData);
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
propertiesWriter.WriteToPacket(packetWriter);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Auth);
}
private static void ThrowReasonCodeNotSetException()
{
throw new MqttProtocolViolationException("The ReasonCode must be set for MQTT version 5.");
}
private static void ThrowIfPacketIdentifierIsInvalid(IMqttPacketWithIdentifier packet)
{
if (!packet.PacketIdentifier.HasValue)
{
throw new MqttProtocolViolationException($"Packet identifier is not set for {packet.GetType().Name}.");
}
}
}
}
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using MQTTnet.Adapter;
using MQTTnet.Packets;
namespace MQTTnet.Formatter.V5
{
public class MqttV500PacketFormatter : IMqttPacketFormatter
{
private readonly MqttV500PacketEncoder _encoder = new MqttV500PacketEncoder();
private readonly MqttV500PacketDecoder _decoder = new MqttV500PacketDecoder();
public IMqttDataConverter DataConverter { get; } = new MqttV500DataConverter();
public ArraySegment<byte> Encode(MqttBasePacket mqttPacket)
{
if (mqttPacket == null) throw new ArgumentNullException(nameof(mqttPacket));
return _encoder.Encode(mqttPacket);
}
public MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket)
{
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
return _decoder.Decode(receivedMqttPacket);
}
public void FreeBuffer()
{
_encoder.FreeBuffer();
}
}
}
@@ -0,0 +1,196 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V5
{
public class MqttV500PropertiesReader
{
private readonly MqttPacketBodyReader _body;
private readonly uint _length;
private readonly ulong _targetOffset;
public MqttV500PropertiesReader(MqttPacketBodyReader body)
{
_body = body ?? throw new ArgumentNullException(nameof(body));
if (!body.EndOfStream)
{
_length = body.ReadVariableLengthInteger();
}
_targetOffset = body.Offset + _length;
}
public MqttPropertyId CurrentPropertyId { get; private set; }
public bool MoveNext()
{
if (_length == 0)
{
return false;
}
if (_body.Offset >= _targetOffset)
{
return false;
}
CurrentPropertyId = (MqttPropertyId)_body.ReadByte();
return true;
}
public void FillUserProperties(List<MqttUserProperty> userProperties)
{
if (userProperties == null) throw new ArgumentNullException(nameof(userProperties));
var userPropertiesLength = _body.ReadVariableLengthInteger();
if (userPropertiesLength == 0)
{
return;
}
var targetPosition = _body.Offset + userPropertiesLength;
while (_body.Offset < targetPosition)
{
var name = _body.ReadStringWithLengthPrefix();
var value = _body.ReadStringWithLengthPrefix();
userProperties.Add(new MqttUserProperty(name, value));
}
}
public string ReadReasonString()
{
return _body.ReadStringWithLengthPrefix();
}
public string ReadAuthenticationMethod()
{
return _body.ReadStringWithLengthPrefix();
}
public byte[] ReadAuthenticationData()
{
return _body.ReadWithLengthPrefix().ToArray();
}
public bool? ReadRetainAvailable()
{
return _body.ReadBoolean();
}
public uint? ReadSessionExpiryInterval()
{
return _body.ReadFourByteInteger();
}
public ushort? ReadReceiveMaximum()
{
return _body.ReadTwoByteInteger();
}
public string ReadAssignedClientIdentifier()
{
return _body.ReadStringWithLengthPrefix();
}
public string ReadServerReference()
{
return _body.ReadStringWithLengthPrefix();
}
public ushort? ReadTopicAliasMaximum()
{
return _body.ReadTwoByteInteger();
}
public uint? ReadMaximumPacketSize()
{
return _body.ReadFourByteInteger();
}
public ushort? ReadServerKeepAlive()
{
return _body.ReadTwoByteInteger();
}
public string ReadResponseInformation()
{
return _body.ReadStringWithLengthPrefix();
}
public bool? ReadSharedSubscriptionAvailable()
{
return _body.ReadBoolean();
}
public bool? ReadSubscriptionIdentifiersAvailable()
{
return _body.ReadBoolean();
}
public bool? ReadWildcardSubscriptionAvailable()
{
return _body.ReadBoolean();
}
public uint? ReadSubscriptionIdentifier()
{
return _body.ReadVariableLengthInteger();
}
public MqttPayloadFormatIndicator? ReadPayloadFormatIndicator()
{
return (MqttPayloadFormatIndicator)_body.ReadByte();
}
public uint? ReadMessageExpiryInterval()
{
return _body.ReadFourByteInteger();
}
public ushort? ReadTopicAlias()
{
return _body.ReadTwoByteInteger();
}
public string ReadResponseTopic()
{
return _body.ReadStringWithLengthPrefix();
}
public byte[] ReadCorrelationData()
{
return _body.ReadWithLengthPrefix().ToArray();
}
public string ReadContentType()
{
return _body.ReadStringWithLengthPrefix();
}
public uint? ReadWillDelayInterval()
{
return _body.ReadFourByteInteger();
}
public bool? RequestResponseInformation()
{
return _body.ReadBoolean();
}
public bool? RequestProblemInformation()
{
return _body.ReadBoolean();
}
public void ThrowInvalidPropertyIdException(Type type)
{
throw new MqttProtocolViolationException($"Property ID '{CurrentPropertyId}' is not supported for package type '{type.Name}'.");
}
}
}
@@ -0,0 +1,238 @@
using System;
using System.Collections.Generic;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V5
{
public class MqttV500PropertiesWriter
{
private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
public void WriteUserProperties(List<MqttUserProperty> userProperties)
{
if (userProperties == null || userProperties.Count == 0)
{
return;
}
var propertyWriter = new MqttPacketWriter();
foreach (var property in userProperties)
{
propertyWriter.WriteWithLengthPrefix(property.Name);
propertyWriter.WriteWithLengthPrefix(property.Value);
}
_packetWriter.Write((byte)MqttPropertyId.UserProperty);
_packetWriter.WriteVariableLengthInteger((uint)propertyWriter.Length);
_packetWriter.Write(propertyWriter);
}
public void WriteCorrelationData(byte[] value)
{
Write(MqttPropertyId.CorrelationData, value);
}
public void WriteAuthenticationData(byte[] value)
{
Write(MqttPropertyId.AuthenticationData, value);
}
public void WriteReasonString(string value)
{
Write(MqttPropertyId.ReasonString, value);
}
public void WriteResponseTopic(string value)
{
Write(MqttPropertyId.ResponseTopic, value);
}
public void WriteContentType(string value)
{
Write(MqttPropertyId.ContentType, value);
}
public void WriteServerReference(string value)
{
Write(MqttPropertyId.ServerReference, value);
}
public void WriteAuthenticationMethod(string value)
{
Write(MqttPropertyId.AuthenticationMethod, value);
}
public void WriteToPacket(MqttPacketWriter packetWriter)
{
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
packetWriter.WriteVariableLengthInteger((uint)_packetWriter.Length);
packetWriter.Write(_packetWriter);
}
public void WriteSessionExpiryInterval(uint? value)
{
WriteAsFourByteInteger(MqttPropertyId.SessionExpiryInterval, value);
}
public void WriteSubscriptionIdentifier(uint? value)
{
WriteAsVariableLengthInteger(MqttPropertyId.SubscriptionIdentifier, value);
}
public void WriteTopicAlias(ushort? value)
{
Write(MqttPropertyId.TopicAlias, value);
}
public void WriteMessageExpiryInterval(uint? value)
{
WriteAsFourByteInteger(MqttPropertyId.MessageExpiryInterval, value);
}
public void WritePayloadFormatIndicator(MqttPayloadFormatIndicator? value)
{
if (!value.HasValue)
{
return;
}
Write(MqttPropertyId.PayloadFormatIndicator, (byte)value.Value);
}
public void WriteWillDelayInterval(uint? value)
{
WriteAsFourByteInteger(MqttPropertyId.WillDelayInterval, value);
}
public void WriteRequestProblemInformation(bool? value)
{
Write(MqttPropertyId.RequestProblemInformation, value);
}
public void WriteRequestResponseInformation(bool? value)
{
Write(MqttPropertyId.RequestResponseInformation, value);
}
public void WriteReceiveMaximum(ushort? value)
{
Write(MqttPropertyId.RequestResponseInformation, value);
}
public void WriteMaximumPacketSize(uint? value)
{
WriteAsFourByteInteger(MqttPropertyId.MaximumPacketSize, value);
}
public void WriteRetainAvailable(bool? value)
{
Write(MqttPropertyId.RetainAvailable, value);
}
public void WriteAssignedClientIdentifier(string value)
{
Write(MqttPropertyId.AssignedClientIdentifer, value);
}
public void WriteTopicAliasMaximum(ushort? value)
{
Write(MqttPropertyId.TopicAliasMaximum, value);
}
public void WriteWildcardSubscriptionAvailable(bool? value)
{
Write(MqttPropertyId.WildcardSubscriptionAvailable, value);
}
public void WriteSubscriptionIdentifiersAvailable(bool? value)
{
Write(MqttPropertyId.SubscriptionIdentifiersAvailable, value);
}
public void WriteSharedSubscriptionAvailable(bool? value)
{
Write(MqttPropertyId.SharedSubscriptionAvailable, value);
}
public void WriteServerKeepAlive(ushort? value)
{
Write(MqttPropertyId.ServerKeepAlive, value);
}
public void WriteResponseInformation(string value)
{
Write(MqttPropertyId.ResponseInformation, value);
}
private void Write(MqttPropertyId id, bool? value)
{
if (!value.HasValue)
{
return;
}
_packetWriter.Write((byte)id);
_packetWriter.Write(value.Value ? (byte)0x1 : (byte)0x0);
}
private void Write(MqttPropertyId id, ushort? value)
{
if (!value.HasValue)
{
return;
}
_packetWriter.Write((byte)id);
_packetWriter.Write(value.Value);
}
private void WriteAsVariableLengthInteger(MqttPropertyId id, uint? value)
{
if (!value.HasValue)
{
return;
}
_packetWriter.Write((byte)id);
_packetWriter.WriteVariableLengthInteger(value.Value);
}
private void WriteAsFourByteInteger(MqttPropertyId id, uint? value)
{
if (!value.HasValue)
{
return;
}
_packetWriter.Write((byte)id);
_packetWriter.Write((byte)(value.Value >> 24));
_packetWriter.Write((byte)(value.Value >> 16));
_packetWriter.Write((byte)(value.Value >> 8));
_packetWriter.Write((byte)value.Value);
}
private void Write(MqttPropertyId id, string value)
{
if (value == null)
{
return;
}
_packetWriter.Write((byte)id);
_packetWriter.WriteWithLengthPrefix(value);
}
private void Write(MqttPropertyId id, byte[] value)
{
if (value == null)
{
return;
}
_packetWriter.Write((byte)id);
_packetWriter.WriteWithLengthPrefix(value);
}
}
}
@@ -1,9 +1,10 @@
using System.Threading.Tasks;
using MQTTnet.Client.Publishing;
namespace MQTTnet
{
public interface IApplicationMessagePublisher
{
Task PublishAsync(MqttApplicationMessage applicationMessage);
Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage);
}
}
@@ -1,8 +1,9 @@
using System;
using MQTTnet.Adapter;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Formatter;
namespace MQTTnet.Implementations
{
@@ -11,19 +12,17 @@ namespace MQTTnet.Implementations
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
{
if (options == null) throw new ArgumentNullException(nameof(options));
var serializer = new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion };
switch (options.ChannelOptions)
{
case MqttClientTcpOptions _:
{
return new MqttChannelAdapter(new MqttTcpChannel(options), serializer, logger);
return new MqttChannelAdapter(new MqttTcpChannel(options), new MqttPacketFormatterAdapter(options.ProtocolVersion), logger);
}
case MqttClientWebSocketOptions webSocketOptions:
{
return new MqttChannelAdapter(new MqttWebSocketChannel(webSocketOptions), serializer, logger);
return new MqttChannelAdapter(new MqttWebSocketChannel(webSocketOptions), new MqttPacketFormatterAdapter(options.ProtocolVersion), logger);
}
default:
@@ -10,7 +10,7 @@ using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Security.Cryptography.Certificates;
using MQTTnet.Channel;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Server;
namespace MQTTnet.Implementations
@@ -2,14 +2,13 @@
using System;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using System.IO;
using System.Linq;
using System.Threading;
using MQTTnet.Channel;
using MQTTnet.Client;
using MQTTnet.Client.Options;
namespace MQTTnet.Implementations
{
@@ -17,7 +16,7 @@ namespace MQTTnet.Implementations
{
private readonly IMqttClientOptions _clientOptions;
private readonly MqttClientTcpOptions _options;
private Socket _socket;
private Stream _stream;
@@ -87,8 +86,9 @@ namespace MQTTnet.Implementations
public void Dispose()
{
Cleanup(ref _stream, (s) => s.Dispose());
Cleanup(ref _socket, (s) => {
Cleanup(ref _stream, s => s.Dispose());
Cleanup(ref _socket, s =>
{
if (s.Connected)
{
s.Shutdown(SocketShutdown.Both);
@@ -102,7 +102,7 @@ namespace MQTTnet.Implementations
// Try the instance callback.
if (_options.TlsOptions.CertificateValidationCallback != null)
{
return _options.TlsOptions.CertificateValidationCallback(x509Certificate, chain, sslPolicyErrors,_clientOptions);
return _options.TlsOptions.CertificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _clientOptions);
}
// Try static callback.
@@ -4,7 +4,7 @@ using System.Threading.Tasks;
using Windows.Networking.Sockets;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Formatter;
using MQTTnet.Server;
namespace MQTTnet.Implementations
@@ -72,7 +72,7 @@ namespace MQTTnet.Implementations
{
try
{
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, _options), new MqttPacketSerializer(), _logger);
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, _options), new MqttPacketFormatterAdapter(), _logger);
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
}
catch (Exception exception)
@@ -9,7 +9,7 @@ using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Formatter;
using MQTTnet.Server;
namespace MQTTnet.Implementations
@@ -20,8 +20,8 @@ namespace MQTTnet.Implementations
private readonly CancellationToken _cancellationToken;
private readonly AddressFamily _addressFamily;
private readonly MqttServerTcpEndpointBaseOptions _options;
private readonly MqttServerTlsTcpEndpointOptions _tlsOptions;
private readonly X509Certificate2 _tlsCertificate;
private Socket _socket;
public MqttTcpServerListener(
@@ -36,6 +36,11 @@ namespace MQTTnet.Implementations
_tlsCertificate = tlsCertificate;
_cancellationToken = cancellationToken;
_logger = logger.CreateChildLogger(nameof(MqttTcpServerListener));
if (_options is MqttServerTlsTcpEndpointOptions tlsOptions)
{
_tlsOptions = tlsOptions;
}
}
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
@@ -75,7 +80,7 @@ namespace MQTTnet.Implementations
if (_tlsCertificate != null)
{
sslStream = new SslStream(new NetworkStream(clientSocket), false);
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false);
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, _tlsOptions.SslProtocol, false).ConfigureAwait(false);
}
_logger.Verbose("Client '{0}' accepted by TCP listener '{1}, {2}'.",
@@ -83,7 +88,7 @@ namespace MQTTnet.Implementations
_socket.LocalEndPoint,
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer(), _logger);
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketFormatterAdapter(), _logger);
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
}
catch (ObjectDisposedException)
@@ -5,7 +5,7 @@ using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Client;
using MQTTnet.Client.Options;
namespace MQTTnet.Implementations
{
@@ -155,9 +155,7 @@ namespace MQTTnet.Implementations
if (!string.IsNullOrEmpty(_options.ProxyOptions.Username) && !string.IsNullOrEmpty(_options.ProxyOptions.Password))
{
var credentials =
new NetworkCredential(_options.ProxyOptions.Username, _options.ProxyOptions.Password, _options.ProxyOptions.Domain);
var credentials = new NetworkCredential(_options.ProxyOptions.Username, _options.ProxyOptions.Password, _options.ProxyOptions.Domain);
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList, credentials);
}
@@ -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)
{
+112
View File
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace MQTTnet.Internal
{
public class BlockingQueue<TItem>
{
private readonly object _syncRoot = new object();
private readonly LinkedList<TItem> _items = new LinkedList<TItem>();
private readonly ManualResetEvent _gate = new ManualResetEvent(false);
public int Count
{
get
{
lock (_syncRoot)
{
return _items.Count;
}
}
}
public void Enqueue(TItem item)
{
if (item == null) throw new ArgumentNullException(nameof(item));
lock (_syncRoot)
{
_items.AddLast(item);
_gate.Set();
}
}
public TItem Dequeue()
{
while (true)
{
lock (_syncRoot)
{
if (_items.Count > 0)
{
var item = _items.First.Value;
_items.RemoveFirst();
return item;
}
if (_items.Count == 0)
{
_gate.Reset();
}
}
_gate.WaitOne();
}
}
public TItem PeekAndWait()
{
while (true)
{
lock (_syncRoot)
{
if (_items.Count > 0)
{
return _items.First.Value;
}
if (_items.Count == 0)
{
_gate.Reset();
}
}
_gate.WaitOne();
}
}
public void RemoveFirst(Predicate<TItem> match)
{
if (match == null) throw new ArgumentNullException(nameof(match));
lock (_syncRoot)
{
if (_items.Count > 0 && match(_items.First.Value))
{
_items.RemoveFirst();
}
}
}
public TItem RemoveFirst()
{
lock (_syncRoot)
{
var item = _items.First;
_items.RemoveFirst();
return item.Value;
}
}
public void Clear()
{
lock (_syncRoot)
{
_items.Clear();
}
}
}
}
@@ -1,35 +0,0 @@
using MQTTnet.Packets;
namespace MQTTnet.Internal
{
public static class MqttApplicationMessageExtensions
{
public static MqttApplicationMessage ToApplicationMessage(this MqttPublishPacket publishPacket)
{
return new MqttApplicationMessage
{
Topic = publishPacket.Topic,
Payload = publishPacket.Payload,
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
Retain = publishPacket.Retain
};
}
public static MqttPublishPacket ToPublishPacket(this MqttApplicationMessage applicationMessage)
{
if (applicationMessage == null)
{
return null;
}
return new MqttPublishPacket
{
Topic = applicationMessage.Topic,
Payload = applicationMessage.Payload,
QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel,
Retain = applicationMessage.Retain,
Dup = false
};
}
}
}
+17 -1
View File
@@ -54,7 +54,7 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='uap10.0'">
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.1.5" />
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.1.9" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net452'">
@@ -63,4 +63,20 @@
<ItemGroup Condition="'$(TargetFramework)'=='net461'">
</ItemGroup>
<ItemGroup>
<Compile Remove="NewFolder\**" />
<EmbeddedResource Remove="NewFolder\**" />
<None Remove="NewFolder\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.2" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Net.Requests">
<HintPath>C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Net.Requests.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
+19 -1
View File
@@ -1,4 +1,6 @@
using MQTTnet.Protocol;
using System.Collections.Generic;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet
{
@@ -11,5 +13,21 @@ namespace MQTTnet
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
public bool Retain { get; set; }
public List<MqttUserProperty> UserProperties { get; set; } = new List<MqttUserProperty>();
public string ContentType { get; set; }
public string ResponseTopic { get; set; }
public MqttPayloadFormatIndicator? PayloadFormatIndicator { get; set; }
public uint? MessageExpiryInterval { get; set; }
public ushort? TopicAlias { get; set; }
public byte[] CorrelationData { get; set; }
public uint? SubscriptionIdentifier { get; set; }
}
}
@@ -3,16 +3,26 @@ using System.IO;
using System.Linq;
using System.Text;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet
{
public class MqttApplicationMessageBuilder
{
private readonly List<MqttUserProperty> _userProperties = new List<MqttUserProperty>();
private MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
private string _topic;
private byte[] _payload;
private bool _retain;
private string _contentType;
private string _responseTopic;
private byte[] _correlationData;
private ushort? _topicAlias;
private uint? _subscriptionIdentifier;
private uint? _messageExpiryInterval;
private MqttPayloadFormatIndicator? _payloadFormatIndicator;
public MqttApplicationMessageBuilder WithTopic(string topic)
{
@@ -100,6 +110,78 @@ namespace MQTTnet
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithUserProperty(string name, string value)
{
_userProperties.Add(new MqttUserProperty(name, value));
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithContentType(string contentType)
{
_contentType = contentType;
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithResponseTopic(string responseTopic)
{
_responseTopic = responseTopic;
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithCorrelationData(byte[] correlationData)
{
_correlationData = correlationData;
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithTopicAlias(ushort topicAlias)
{
_topicAlias = topicAlias;
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithSubscriptionIdentifier(uint subscriptionIdentifier)
{
_subscriptionIdentifier = subscriptionIdentifier;
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithMessageExpiryInterval(uint messageExpiryInterval)
{
_messageExpiryInterval = messageExpiryInterval;
return this;
}
/// <summary>
/// This is only supported when using MQTTv5.
/// </summary>
public MqttApplicationMessageBuilder WithPayloadFormatIndicator(MqttPayloadFormatIndicator payloadFormatIndicator)
{
_payloadFormatIndicator = payloadFormatIndicator;
return this;
}
public MqttApplicationMessage Build()
{
if (string.IsNullOrEmpty(_topic))
@@ -107,13 +189,24 @@ namespace MQTTnet
throw new MqttProtocolViolationException("Topic is not set.");
}
return new MqttApplicationMessage
var applicationMessage = new MqttApplicationMessage
{
Topic = _topic,
Payload = _payload ?? new byte[0],
QualityOfServiceLevel = _qualityOfServiceLevel,
Retain = _retain
Retain = _retain,
ContentType = _contentType,
ResponseTopic = _responseTopic,
CorrelationData = _correlationData,
TopicAlias = _topicAlias,
SubscriptionIdentifier = _subscriptionIdentifier,
MessageExpiryInterval = _messageExpiryInterval,
PayloadFormatIndicator = _payloadFormatIndicator
};
applicationMessage.UserProperties.AddRange(_userProperties);
return applicationMessage;
}
}
}
+14
View File
@@ -0,0 +1,14 @@
using MQTTnet.Protocol;
namespace MQTTnet.Packets
{
/// <summary>
/// Added in MQTTv5.0.0.
/// </summary>
public class MqttAuthPacket : MqttBasePacket
{
public MqttAuthenticateReasonCode ReasonCode { get; set; }
public MqttAuthPacketProperties Properties { get; set; }
}
}
@@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace MQTTnet.Packets
{
public class MqttAuthPacketProperties
{
public string AuthenticationMethod { get; set; }
public byte[] AuthenticationData { get; set; }
public string ReasonString { get; set; }
public List<MqttUserProperty> UserProperties { get; } = new List<MqttUserProperty>();
}
}

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