Compare commits

...
61 Commits
Author SHA1 Message Date
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 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 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 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 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
56 changed files with 1523 additions and 413 deletions
+5 -6
View File
@@ -10,15 +10,14 @@
<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="2.8.5" />
<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="2.8.5" />
</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="2.8.5" />
</dependencies>
</metadata>
+15 -3
View File
@@ -10,7 +10,19 @@
<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] Updated nuget packages due to security issues.
* [Client] Fixed wrong behavior of publish method when client is disconnecting (thanks to @PaulFake).
* [Client] Added readonly property for accessing options.
* [ManagedClient] Added max pending messages count option.
* [ManagedClient] Add pending messages overflow strategy option.
* [ManagedClient] Fixed an issue which deletes the wrong message from the internal queue (thanks to @PaulFake).
* [ManagedClient] Added readonly property for accessing options.
* [Server] Added new method which exposes all retained messages.
* [Server] Removed (wrong) setter from the server options interface.
* [Server] Fixed cpu spike in case a client disconnects (issue 421).
* [Server] Fixed concurrent sends with Aspnetcore.Connections.Abstractions based transport.
* [Server] Fixed wrong retain flag when distributing application messages (thanks to @trev0115).
* [Server] Fixed issue which closes a connection when reconnecting with the same client ID (thanks to @fogzot).
</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 +31,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 +40,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>
+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
+3 -2
View File
@@ -25,7 +25,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
* Performance optimized (processing ~70.000 messages / second)*
* Interfaces included for mocking and testing
* Access to internal trace messages
* Unit tested (~90 tests)
* Unit tested (~100 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_.
@@ -85,12 +85,13 @@ 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
@@ -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,6 +1,7 @@
using Microsoft.AspNetCore.Connections;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Serializer;
using System;
@@ -26,6 +27,8 @@ namespace MQTTnet.AspNetCore
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 +62,7 @@ namespace MQTTnet.AspNetCore
}
else
{
readResult = await readTask;
readResult = await readTask.ConfigureAwait(false);
}
var buffer = readResult.Buffer;
@@ -83,7 +86,7 @@ namespace MQTTnet.AspNetCore
}
else if (readResult.IsCompleted)
{
break;
throw new MqttCommunicationException("Connection Aborted");
}
}
finally
@@ -104,10 +107,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 = PacketSerializer.Serialize(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()
@@ -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; }
}
}
@@ -10,11 +10,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,6 @@
using System;
using MQTTnet.Client;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
{
@@ -12,5 +13,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,5 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -7,13 +6,17 @@ using System.Threading.Tasks;
using MQTTnet.Client;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Protocol;
using MQTTnet.Server;
using MqttClientConnectedEventArgs = MQTTnet.Client.MqttClientConnectedEventArgs;
using MqttClientDisconnectedEventArgs = MQTTnet.Client.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,8 +27,7 @@ namespace MQTTnet.Extensions.ManagedClient
private CancellationTokenSource _publishingCancellationToken;
private ManagedMqttClientStorageManager _storageManager;
private IManagedMqttClientOptions _options;
private bool _subscriptionsNotPushed;
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetChildLogger logger)
@@ -44,12 +46,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 +70,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,10 +97,7 @@ namespace MQTTnet.Extensions.ManagedClient
StopPublishing();
StopMaintainingConnection();
while (_messageQueue.Any())
{
_messageQueue.Take();
}
_messageQueue.Clear();
return Task.FromResult(0);
}
@@ -112,12 +113,38 @@ namespace MQTTnet.Extensions.ManagedClient
{
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 +184,6 @@ namespace MQTTnet.Extensions.ManagedClient
public void Dispose()
{
_messageQueue?.Dispose();
_connectionCancellationToken?.Dispose();
_publishingCancellationToken?.Dispose();
}
@@ -193,7 +219,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 +232,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 +254,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 +292,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 +310,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)
@@ -334,7 +388,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,6 @@
using System;
using MQTTnet.Client;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
{
@@ -12,5 +13,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,6 @@
using System;
using MQTTnet.Client;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
{
@@ -8,6 +9,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
View File
@@ -7,6 +7,7 @@ namespace MQTTnet.Client
public interface IMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
{
bool IsConnected { get; }
IMqttClientOptions Options { get; }
event EventHandler<MqttClientConnectedEventArgs> Connected;
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
+31 -30
View File
@@ -23,13 +23,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 +43,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 +54,8 @@ namespace MQTTnet.Client
try
{
_options = options;
Options = options;
_packetIdentifierProvider.Reset();
_packetDispatcher.Reset();
@@ -62,8 +63,8 @@ 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);
@@ -73,7 +74,7 @@ namespace MQTTnet.Client
_sendTracker.Restart();
if (_options.KeepAlivePeriod != TimeSpan.Zero)
if (Options.KeepAlivePeriod != TimeSpan.Zero)
{
StartSendingKeepAliveMessages(_cancellationTokenSource.Token);
}
@@ -139,7 +140,7 @@ namespace MQTTnet.Client
return subscribePacket.TopicFilters.Select((t, i) => new MqttSubscribeResult(t, response.SubscribeReturnCodes[i])).ToList();
}
public async Task UnsubscribeAsync(IEnumerable<string> topicFilters)
public Task UnsubscribeAsync(IEnumerable<string> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
@@ -151,7 +152,7 @@ namespace MQTTnet.Client
TopicFilters = topicFilters.ToList()
};
await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false);
return SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, _cancellationTokenSource.Token);
}
public Task PublishAsync(MqttApplicationMessage applicationMessage)
@@ -197,11 +198,11 @@ namespace MQTTnet.Client
{
var connectPacket = new MqttConnectPacket
{
ClientId = _options.ClientId,
Username = _options.Credentials?.Username,
Password = _options.Credentials?.Password,
CleanSession = _options.CleanSession,
KeepAlivePeriod = (ushort)_options.KeepAlivePeriod.TotalSeconds,
ClientId = Options.ClientId,
Username = Options.Credentials?.Username,
Password = Options.Credentials?.Password,
CleanSession = Options.CleanSession,
KeepAlivePeriod = (ushort)Options.KeepAlivePeriod.TotalSeconds,
WillMessage = willApplicationMessage
};
@@ -216,7 +217,7 @@ namespace MQTTnet.Client
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)
@@ -234,14 +235,14 @@ namespace MQTTnet.Client
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,7 +304,7 @@ 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);
var respone = await Internal.TaskExtensions.TimeoutAfterAsync(ct => packetAwaiter.Task, Options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
return (TResponsePacket)respone;
}
@@ -320,16 +321,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,10 +375,10 @@ 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)
@@ -500,7 +501,7 @@ namespace MQTTnet.Client
() => ReceivePacketsAsync(cancellationToken),
cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
TaskScheduler.Default).Unwrap();
}
private void StartSendingKeepAliveMessages(CancellationToken cancellationToken)
@@ -509,7 +510,7 @@ namespace MQTTnet.Client
() => SendKeepAliveMessagesAsync(cancellationToken),
cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
TaskScheduler.Default).Unwrap();
}
private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket)
@@ -517,7 +518,7 @@ namespace MQTTnet.Client
try
{
var applicationMessage = publishPacket.ToApplicationMessage();
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(_options.ClientId, applicationMessage));
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage));
}
catch (Exception exception)
{
@@ -551,4 +552,4 @@ namespace MQTTnet.Client
return Interlocked.CompareExchange(ref _disconnectGate, 1, 0) != 0;
}
}
}
}
@@ -2,7 +2,6 @@
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;
@@ -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.
@@ -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}'.",
+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 -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'">
+6 -2
View File
@@ -16,14 +16,18 @@ namespace MQTTnet.Server
IMqttServerOptions Options { get; }
[Obsolete("This method is no longer async. Use the not async method.")]
Task<IList<IMqttClientSessionStatus>> GetClientSessionsStatusAsync();
IList<IMqttClientSessionStatus> GetClientSessionsStatus();
IList<MqttApplicationMessage> GetRetainedMessages();
Task ClearRetainedMessagesAsync();
Task SubscribeAsync(string clientId, IList<TopicFilter> topicFilters);
Task UnsubscribeAsync(string clientId, IList<string> topicFilters);
Task StartAsync(IMqttServerOptions options);
Task StopAsync();
Task ClearRetainedMessagesAsync();
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ namespace MQTTnet.Server
Action<MqttConnectionValidatorContext> ConnectionValidator { get; }
Action<MqttSubscriptionInterceptorContext> SubscriptionInterceptor { get; }
Action<MqttApplicationMessageInterceptorContext> ApplicationMessageInterceptor { get; }
Action<MqttClientMessageQueueInterceptorContext> ClientMessageQueueInterceptor { get; set; }
Action<MqttClientMessageQueueInterceptorContext> ClientMessageQueueInterceptor { get; }
MqttServerTcpEndpointOptions DefaultEndpointOptions { get; }
MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; }
@@ -36,8 +36,8 @@ namespace MQTTnet.Server
{
return;
}
Task.Run(() => RunAsync(keepAlivePeriod, cancellationToken), cancellationToken);
Task.Run(() => RunAsync(keepAlivePeriod, cancellationToken), cancellationToken).ConfigureAwait(false);
}
public void Pause()
@@ -50,6 +50,12 @@ namespace MQTTnet.Server
_isPaused = false;
}
public void Reset()
{
_lastPacketReceivedTracker.Restart();
_lastNonKeepAlivePacketReceivedTracker.Restart();
}
public void PacketReceived(MqttBasePacket packet)
{
_lastPacketReceivedTracker.Restart();
+176 -142
View File
@@ -9,6 +9,7 @@ using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using MQTTnet.Serializer;
namespace MQTTnet.Server
{
@@ -17,6 +18,7 @@ namespace MQTTnet.Server
private readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
private readonly MqttRetainedMessagesManager _retainedMessagesManager;
private readonly MqttServerEventDispatcher _eventDispatcher;
private readonly MqttClientKeepAliveMonitor _keepAliveMonitor;
private readonly MqttClientPendingPacketsQueue _pendingPacketsQueue;
private readonly MqttClientSubscriptionsManager _subscriptionsManager;
@@ -28,29 +30,33 @@ namespace MQTTnet.Server
private CancellationTokenSource _cancellationTokenSource;
private MqttApplicationMessage _willMessage;
private bool _wasCleanDisconnect;
private IMqttChannelAdapter _adapter;
private Task _workerTask;
private IDisposable _cleanupHandle;
private string _adapterEndpoint;
private MqttProtocolVersion? _adapterProtocolVersion;
public MqttClientSession(
string clientId,
IMqttServerOptions options,
MqttClientSessionsManager sessionsManager,
MqttRetainedMessagesManager retainedMessagesManager,
MqttServerEventDispatcher eventDispatcher,
IMqttNetChildLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
_options = options ?? throw new ArgumentNullException(nameof(options));
_sessionsManager = sessionsManager;
_sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
_retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager));
_eventDispatcher = eventDispatcher ?? throw new ArgumentNullException(nameof(eventDispatcher));
ClientId = clientId;
_logger = logger.CreateChildLogger(nameof(MqttClientSession));
_keepAliveMonitor = new MqttClientKeepAliveMonitor(this, _logger);
_subscriptionsManager = new MqttClientSubscriptionsManager(clientId, _options, sessionsManager.Server);
_subscriptionsManager = new MqttClientSubscriptionsManager(clientId, _options, eventDispatcher);
_pendingPacketsQueue = new MqttClientPendingPacketsQueue(_options, this, _logger);
}
@@ -59,9 +65,9 @@ namespace MQTTnet.Server
public void FillStatus(MqttClientSessionStatus status)
{
status.ClientId = ClientId;
status.IsConnected = _adapter != null;
status.Endpoint = _adapter?.Endpoint;
status.ProtocolVersion = _adapter?.PacketSerializer?.ProtocolVersion;
status.IsConnected = _cancellationTokenSource != null;
status.Endpoint = _adapterEndpoint;
status.ProtocolVersion = _adapterProtocolVersion;
status.PendingApplicationMessagesCount = _pendingPacketsQueue.Count;
status.LastPacketReceived = _keepAliveMonitor.LastPacketReceived;
status.LastNonKeepAlivePacketReceived = _keepAliveMonitor.LastNonKeepAlivePacketReceived;
@@ -73,145 +79,11 @@ namespace MQTTnet.Server
return _workerTask;
}
private async Task RunInternalAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter)
{
if (connectPacket == null) throw new ArgumentNullException(nameof(connectPacket));
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
try
{
_adapter = adapter;
adapter.ReadingPacketStarted += OnAdapterReadingPacketStarted;
adapter.ReadingPacketCompleted += OnAdapterReadingPacketCompleted;
_cancellationTokenSource = new CancellationTokenSource();
//woraround for https://github.com/dotnet/corefx/issues/24430
#pragma warning disable 4014
_cleanupHandle = _cancellationTokenSource.Token.Register(() => CleanupAsync());
#pragma warning restore 4014
//endworkaround
_wasCleanDisconnect = false;
_willMessage = connectPacket.WillMessage;
_pendingPacketsQueue.Start(adapter, _cancellationTokenSource.Token);
_keepAliveMonitor.Start(connectPacket.KeepAlivePeriod, _cancellationTokenSource.Token);
while (!_cancellationTokenSource.IsCancellationRequested)
{
var packet = await adapter.ReceivePacketAsync(TimeSpan.Zero, _cancellationTokenSource.Token).ConfigureAwait(false);
if (packet != null)
{
_keepAliveMonitor.PacketReceived(packet);
ProcessReceivedPacket(adapter, packet, _cancellationTokenSource.Token);
}
}
}
catch (OperationCanceledException)
{
}
catch (Exception exception)
{
if (exception is MqttCommunicationException)
{
if (exception is MqttCommunicationClosedGracefullyException)
{
_logger.Verbose("Client '{0}': Connection closed gracefully.", ClientId);
}
else
{
_logger.Warning(exception, "Client '{0}': Communication exception while receiving client packets.", ClientId);
}
}
else
{
_logger.Error(exception, "Client '{0}': Unhandled exception while receiving client packets.", ClientId);
}
Stop(MqttClientDisconnectType.NotClean, true);
}
finally
{
await CleanupAsync().ConfigureAwait(false);
_cleanupHandle?.Dispose();
_cleanupHandle = null;
_adapter = null;
_cancellationTokenSource?.Cancel(false);
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
}
}
private async Task CleanupAsync()
{
var adapter = _adapter;
try
{
if (adapter == null)
{
return;
}
_adapter = null;
adapter.ReadingPacketStarted -= OnAdapterReadingPacketStarted;
adapter.ReadingPacketCompleted -= OnAdapterReadingPacketCompleted;
await adapter.DisconnectAsync(_options.DefaultCommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.Error(exception, exception.Message);
}
finally
{
adapter?.Dispose();
}
}
public void Stop(MqttClientDisconnectType type)
{
Stop(type, false);
}
private void Stop(MqttClientDisconnectType type, bool isInsideSession)
{
try
{
var cts = _cancellationTokenSource;
if (cts == null || cts.IsCancellationRequested)
{
return;
}
_wasCleanDisconnect = type == MqttClientDisconnectType.Clean;
_cancellationTokenSource?.Cancel(false);
if (_willMessage != null && !_wasCleanDisconnect)
{
_sessionsManager.EnqueueApplicationMessage(this, _willMessage.ToPublishPacket());
}
_willMessage = null;
if (!isInsideSession)
{
_workerTask?.GetAwaiter().GetResult();
}
}
finally
{
_logger.Info("Client '{0}': Session stopped.", ClientId);
_sessionsManager.Server.OnClientDisconnected(ClientId, _wasCleanDisconnect);
}
}
public void EnqueueApplicationMessage(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
{
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
@@ -227,7 +99,7 @@ namespace MQTTnet.Server
Topic = publishPacket.Topic,
Payload = publishPacket.Payload,
QualityOfServiceLevel = checkSubscriptionsResult.QualityOfServiceLevel,
Retain = false,
Retain = publishPacket.Retain,
Dup = false
};
@@ -297,6 +169,163 @@ namespace MQTTnet.Server
_cancellationTokenSource = null;
}
private void Stop(MqttClientDisconnectType type, bool isInsideSession)
{
try
{
var cts = _cancellationTokenSource;
if (cts == null || cts.IsCancellationRequested)
{
return;
}
_cancellationTokenSource?.Cancel(false);
_wasCleanDisconnect = type == MqttClientDisconnectType.Clean;
if (_willMessage != null && !_wasCleanDisconnect)
{
_sessionsManager.EnqueueApplicationMessage(this, _willMessage.ToPublishPacket());
}
_willMessage = null;
if (!isInsideSession)
{
_workerTask?.GetAwaiter().GetResult();
}
}
finally
{
_logger.Info("Client '{0}': Disconnected (clean={1}).", ClientId, _wasCleanDisconnect);
_eventDispatcher.OnClientDisconnected(ClientId, _wasCleanDisconnect);
}
}
private async Task RunInternalAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter)
{
if (connectPacket == null) throw new ArgumentNullException(nameof(connectPacket));
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
try
{
if (_cancellationTokenSource != null)
{
Stop(MqttClientDisconnectType.Clean, true);
}
adapter.ReadingPacketStarted += OnAdapterReadingPacketStarted;
adapter.ReadingPacketCompleted += OnAdapterReadingPacketCompleted;
_cancellationTokenSource = new CancellationTokenSource();
//workaround for https://github.com/dotnet/corefx/issues/24430
#pragma warning disable 4014
_cleanupHandle = _cancellationTokenSource.Token.Register(async () =>
{
await TryDisconnectAdapterAsync(adapter).ConfigureAwait(false);
TryDisposeAdapter(adapter);
});
#pragma warning restore 4014
//end workaround
_wasCleanDisconnect = false;
_willMessage = connectPacket.WillMessage;
_pendingPacketsQueue.Start(adapter, _cancellationTokenSource.Token);
_keepAliveMonitor.Start(connectPacket.KeepAlivePeriod, _cancellationTokenSource.Token);
_adapterEndpoint = adapter.Endpoint;
_adapterProtocolVersion = adapter.PacketSerializer.ProtocolVersion;
while (!_cancellationTokenSource.IsCancellationRequested)
{
var packet = await adapter.ReceivePacketAsync(TimeSpan.Zero, _cancellationTokenSource.Token).ConfigureAwait(false);
if (packet != null)
{
_keepAliveMonitor.PacketReceived(packet);
ProcessReceivedPacket(adapter, packet, _cancellationTokenSource.Token);
}
}
}
catch (OperationCanceledException)
{
}
catch (Exception exception)
{
if (exception is MqttCommunicationException)
{
if (exception is MqttCommunicationClosedGracefullyException)
{
_logger.Verbose("Client '{0}': Connection closed gracefully.", ClientId);
}
else
{
_logger.Warning(exception, "Client '{0}': Communication exception while receiving client packets.", ClientId);
}
}
else
{
_logger.Error(exception, "Client '{0}': Unhandled exception while receiving client packets.", ClientId);
}
Stop(MqttClientDisconnectType.NotClean, true);
}
finally
{
_adapterEndpoint = null;
_adapterProtocolVersion = null;
// Uncomment as soon as the workaround above is no longer needed.
//await TryDisconnectAdapterAsync(adapter).ConfigureAwait(false);
//TryDisposeAdapter(adapter);
_cleanupHandle?.Dispose();
_cleanupHandle = null;
_cancellationTokenSource?.Cancel(false);
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
}
}
private void TryDisposeAdapter(IMqttChannelAdapter adapter)
{
if (adapter == null)
{
return;
}
try
{
adapter.ReadingPacketStarted -= OnAdapterReadingPacketStarted;
adapter.ReadingPacketCompleted -= OnAdapterReadingPacketCompleted;
adapter.Dispose();
}
catch (Exception exception)
{
_logger.Error(exception, "Error while disposing channel adapter.");
}
}
private async Task TryDisconnectAdapterAsync(IMqttChannelAdapter adapter)
{
if (adapter == null)
{
return;
}
try
{
await adapter.DisconnectAsync(_options.DefaultCommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.Error(exception, "Error while disconnecting channel adapter.");
}
}
private void ProcessReceivedPacket(IMqttChannelAdapter adapter, MqttBasePacket packet, CancellationToken cancellationToken)
{
if (packet is MqttPublishPacket publishPacket)
@@ -371,7 +400,12 @@ namespace MQTTnet.Server
var retainedMessages = _retainedMessagesManager.GetSubscribedMessages(topicFilters);
foreach (var applicationMessage in retainedMessages)
{
EnqueueApplicationMessage(null, applicationMessage.ToPublishPacket());
var publishPacket = applicationMessage.ToPublishPacket();
// Set the retain flag to true according to [MQTT-3.3.1-8].
publishPacket.Retain = true;
EnqueueApplicationMessage(null, publishPacket);
}
}
@@ -23,25 +23,29 @@ namespace MQTTnet.Server
private readonly Dictionary<string, MqttClientSession> _sessions = new Dictionary<string, MqttClientSession>();
private readonly CancellationToken _cancellationToken;
private readonly MqttServerEventDispatcher _eventDispatcher;
private readonly MqttRetainedMessagesManager _retainedMessagesManager;
private readonly IMqttServerOptions _options;
private readonly IMqttNetChildLogger _logger;
public MqttClientSessionsManager(IMqttServerOptions options, MqttServer server, MqttRetainedMessagesManager retainedMessagesManager, CancellationToken cancellationToken, IMqttNetChildLogger logger)
public MqttClientSessionsManager(
IMqttServerOptions options,
MqttRetainedMessagesManager retainedMessagesManager,
CancellationToken cancellationToken,
MqttServerEventDispatcher eventDispatcher,
IMqttNetChildLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
_cancellationToken = cancellationToken;
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateChildLogger(nameof(MqttClientSessionsManager));
_cancellationToken = cancellationToken;
_eventDispatcher = eventDispatcher ?? throw new ArgumentNullException(nameof(eventDispatcher));
_options = options ?? throw new ArgumentNullException(nameof(options));
Server = server ?? throw new ArgumentNullException(nameof(server));
_retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager));
}
public MqttServer Server { get; }
public void Start()
{
Task.Factory.StartNew(() => TryProcessQueuedApplicationMessages(_cancellationToken), _cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
@@ -62,10 +66,10 @@ namespace MQTTnet.Server
public Task StartSession(IMqttChannelAdapter clientAdapter)
{
return Task.Run(() => RunSession(clientAdapter, _cancellationToken), _cancellationToken);
return Task.Run(() => RunSessionAsync(clientAdapter, _cancellationToken), _cancellationToken);
}
public Task<IList<IMqttClientSessionStatus>> GetClientStatusAsync()
public IList<IMqttClientSessionStatus> GetClientStatus()
{
var result = new List<IMqttClientSessionStatus>();
@@ -77,7 +81,7 @@ namespace MQTTnet.Server
result.Add(status);
}
return Task.FromResult((IList<IMqttClientSessionStatus>)result);
return result;
}
public void EnqueueApplicationMessage(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
@@ -176,7 +180,7 @@ namespace MQTTnet.Server
applicationMessage = interceptorContext.ApplicationMessage;
}
Server.OnApplicationMessageReceived(sender?.ClientId, applicationMessage);
_eventDispatcher.OnApplicationMessageReceived(sender?.ClientId, applicationMessage);
if (applicationMessage.Retain)
{
@@ -185,7 +189,12 @@ namespace MQTTnet.Server
foreach (var clientSession in GetSessions())
{
clientSession.EnqueueApplicationMessage(enqueuedApplicationMessage.Sender, applicationMessage.ToPublishPacket());
var publishPacket = applicationMessage.ToPublishPacket();
// Set the retain flag to true according to [MQTT-3.3.1-9].
publishPacket.Retain = false;
clientSession.EnqueueApplicationMessage(enqueuedApplicationMessage.Sender, publishPacket);
}
}
catch (OperationCanceledException)
@@ -205,7 +214,7 @@ namespace MQTTnet.Server
}
}
private async Task RunSession(IMqttChannelAdapter clientAdapter, CancellationToken cancellationToken)
private async Task RunSessionAsync(IMqttChannelAdapter clientAdapter, CancellationToken cancellationToken)
{
var clientId = string.Empty;
@@ -241,7 +250,6 @@ namespace MQTTnet.Server
}
var result = PrepareClientSession(connectPacket);
var clientSession = result.Session;
await clientAdapter.SendPacketAsync(
new MqttConnAckPacket
@@ -251,9 +259,10 @@ namespace MQTTnet.Server
},
cancellationToken).ConfigureAwait(false);
Server.OnClientConnected(clientId);
_logger.Info("Client '{0}': Connected.", clientId);
_eventDispatcher.OnClientConnected(clientId);
await clientSession.RunAsync(connectPacket, clientAdapter).ConfigureAwait(false);
await result.Session.RunAsync(connectPacket, clientAdapter).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@@ -317,7 +326,7 @@ namespace MQTTnet.Server
{
isExistingSession = false;
clientSession = new MqttClientSession(connectPacket.ClientId, _options, this, _retainedMessagesManager, _logger);
clientSession = new MqttClientSession(connectPacket.ClientId, _options, this, _retainedMessagesManager, _eventDispatcher, _logger);
_sessions[connectPacket.ClientId] = clientSession;
_logger.Verbose("Created a new session for client '{0}'.", connectPacket.ClientId);
@@ -10,14 +10,14 @@ namespace MQTTnet.Server
{
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
private readonly IMqttServerOptions _options;
private readonly MqttServer _server;
private readonly MqttServerEventDispatcher _eventDispatcher;
private readonly string _clientId;
public MqttClientSubscriptionsManager(string clientId, IMqttServerOptions options, MqttServer server)
public MqttClientSubscriptionsManager(string clientId, IMqttServerOptions options, MqttServerEventDispatcher eventDispatcher)
{
_clientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
_options = options ?? throw new ArgumentNullException(nameof(options));
_server = server;
_eventDispatcher = eventDispatcher ?? throw new ArgumentNullException(nameof(eventDispatcher));
}
public MqttClientSubscribeResult Subscribe(MqttSubscribePacket subscribePacket)
@@ -58,7 +58,7 @@ namespace MQTTnet.Server
_subscriptions[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
}
_server.OnClientSubscribedTopic(_clientId, topicFilter);
_eventDispatcher.OnClientSubscribedTopic(_clientId, topicFilter);
}
}
@@ -75,7 +75,7 @@ namespace MQTTnet.Server
{
_subscriptions.Remove(topicFilter);
_server.OnClientUnsubscribedTopic(_clientId, topicFilter);
_eventDispatcher.OnClientUnsubscribedTopic(_clientId, topicFilter);
}
}
@@ -52,7 +52,51 @@ namespace MQTTnet.Server
try
{
await HandleMessageInternalAsync(clientId, applicationMessage).ConfigureAwait(false);
List<MqttApplicationMessage> messagesForSave = null;
lock (_messages)
{
var saveIsRequired = false;
if (applicationMessage.Payload?.Length == 0)
{
saveIsRequired = _messages.Remove(applicationMessage.Topic);
_logger.Verbose("Client '{0}' cleared retained message for topic '{1}'.", clientId, applicationMessage.Topic);
}
else
{
if (!_messages.TryGetValue(applicationMessage.Topic, out var existingMessage))
{
_messages[applicationMessage.Topic] = applicationMessage;
saveIsRequired = true;
}
else
{
if (existingMessage.QualityOfServiceLevel != applicationMessage.QualityOfServiceLevel || !existingMessage.Payload.SequenceEqual(applicationMessage.Payload ?? new byte[0]))
{
_messages[applicationMessage.Topic] = applicationMessage;
saveIsRequired = true;
}
}
_logger.Verbose("Client '{0}' set retained message for topic '{1}'.", clientId, applicationMessage.Topic);
}
if (saveIsRequired)
{
messagesForSave = new List<MqttApplicationMessage>(_messages.Values);
}
}
if (messagesForSave == null)
{
_logger.Verbose("Skipped saving retained messages because no changes were detected.");
return;
}
if (_options.Storage != null)
{
await _options.Storage.SaveRetainedMessagesAsync(messagesForSave).ConfigureAwait(false);
}
}
catch (Exception exception)
{
@@ -84,6 +128,14 @@ namespace MQTTnet.Server
return retainedMessages;
}
public IList<MqttApplicationMessage> GetMessages()
{
lock (_messages)
{
return _messages.Values.ToList();
}
}
public Task ClearMessagesAsync()
{
lock (_messages)
@@ -91,55 +143,12 @@ namespace MQTTnet.Server
_messages.Clear();
}
return _options.Storage.SaveRetainedMessagesAsync(new List<MqttApplicationMessage>());
}
private async Task HandleMessageInternalAsync(string clientId, MqttApplicationMessage applicationMessage)
{
var saveIsRequired = false;
lock (_messages)
if (_options.Storage != null)
{
if (applicationMessage.Payload?.Length == 0)
{
saveIsRequired = _messages.Remove(applicationMessage.Topic);
_logger.Verbose("Client '{0}' cleared retained message for topic '{1}'.", clientId, applicationMessage.Topic);
}
else
{
if (!_messages.TryGetValue(applicationMessage.Topic, out var existingMessage))
{
_messages[applicationMessage.Topic] = applicationMessage;
saveIsRequired = true;
}
else
{
if (existingMessage.QualityOfServiceLevel != applicationMessage.QualityOfServiceLevel || !existingMessage.Payload.SequenceEqual(applicationMessage.Payload ?? new byte[0]))
{
_messages[applicationMessage.Topic] = applicationMessage;
saveIsRequired = true;
}
}
_logger.Verbose("Client '{0}' set retained message for topic '{1}'.", clientId, applicationMessage.Topic);
}
return _options.Storage.SaveRetainedMessagesAsync(new List<MqttApplicationMessage>());
}
if (!saveIsRequired)
{
_logger.Verbose("Skipped saving retained messages because no changes were detected.");
}
if (saveIsRequired && _options.Storage != null)
{
List<MqttApplicationMessage> messages;
lock (_messages)
{
messages = _messages.Values.ToList();
}
await _options.Storage.SaveRetainedMessagesAsync(messages).ConfigureAwait(false);
}
return Task.FromResult((object)null);
}
}
}
+20 -30
View File
@@ -11,6 +11,7 @@ namespace MQTTnet.Server
{
public class MqttServer : IMqttServer
{
private readonly MqttServerEventDispatcher _eventDispatcher = new MqttServerEventDispatcher();
private readonly ICollection<IMqttServerAdapter> _adapters;
private readonly IMqttNetChildLogger _logger;
@@ -21,10 +22,16 @@ namespace MQTTnet.Server
public MqttServer(IEnumerable<IMqttServerAdapter> adapters, IMqttNetChildLogger logger)
{
if (adapters == null) throw new ArgumentNullException(nameof(adapters));
_adapters = adapters.ToList();
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateChildLogger(nameof(MqttServer));
_adapters = adapters.ToList();
_eventDispatcher.ClientConnected += (s, e) => ClientConnected?.Invoke(s, e);
_eventDispatcher.ClientDisconnected += (s, e) => ClientDisconnected?.Invoke(s, e);
_eventDispatcher.ClientSubscribedTopic += (s, e) => ClientSubscribedTopic?.Invoke(s, e);
_eventDispatcher.ClientUnsubscribedTopic += (s, e) => ClientUnsubscribedTopic?.Invoke(s, e);
_eventDispatcher.ApplicationMessageReceived += (s, e) => ApplicationMessageReceived?.Invoke(s, e);
}
public event EventHandler Started;
@@ -41,7 +48,17 @@ namespace MQTTnet.Server
public Task<IList<IMqttClientSessionStatus>> GetClientSessionsStatusAsync()
{
return _clientSessionsManager.GetClientStatusAsync();
return Task.FromResult(_clientSessionsManager.GetClientStatus());
}
public IList<IMqttClientSessionStatus> GetClientSessionsStatus()
{
return _clientSessionsManager.GetClientStatus();
}
public IList<MqttApplicationMessage> GetRetainedMessages()
{
return _retainedMessagesManager.GetMessages();
}
public Task SubscribeAsync(string clientId, IList<TopicFilter> topicFilters)
@@ -82,7 +99,7 @@ namespace MQTTnet.Server
_retainedMessagesManager = new MqttRetainedMessagesManager(Options, _logger);
await _retainedMessagesManager.LoadMessagesAsync().ConfigureAwait(false);
_clientSessionsManager = new MqttClientSessionsManager(Options, this, _retainedMessagesManager, _cancellationTokenSource.Token, _logger);
_clientSessionsManager = new MqttClientSessionsManager(Options, _retainedMessagesManager, _cancellationTokenSource.Token, _eventDispatcher, _logger);
_clientSessionsManager.Start();
foreach (var adapter in _adapters)
@@ -134,33 +151,6 @@ namespace MQTTnet.Server
return _retainedMessagesManager?.ClearMessagesAsync();
}
internal void OnClientConnected(string clientId)
{
_logger.Info("Client '{0}': Connected.", clientId);
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientId));
}
internal void OnClientDisconnected(string clientId, bool wasCleanDisconnect)
{
_logger.Info("Client '{0}': Disconnected (clean={1}).", clientId, wasCleanDisconnect);
ClientDisconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientId, wasCleanDisconnect));
}
internal void OnClientSubscribedTopic(string clientId, TopicFilter topicFilter)
{
ClientSubscribedTopic?.Invoke(this, new MqttClientSubscribedTopicEventArgs(clientId, topicFilter));
}
internal void OnClientUnsubscribedTopic(string clientId, string topicFilter)
{
ClientUnsubscribedTopic?.Invoke(this, new MqttClientUnsubscribedTopicEventArgs(clientId, topicFilter));
}
internal void OnApplicationMessageReceived(string clientId, MqttApplicationMessage applicationMessage)
{
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(clientId, applicationMessage));
}
private void OnClientAccepted(object sender, MqttServerAdapterClientAcceptedEventArgs eventArgs)
{
eventArgs.SessionTask = _clientSessionsManager.StartSession(eventArgs.Client);
@@ -0,0 +1,42 @@
using System;
namespace MQTTnet.Server
{
public class MqttServerEventDispatcher
{
public event EventHandler<MqttClientSubscribedTopicEventArgs> ClientSubscribedTopic;
public event EventHandler<MqttClientUnsubscribedTopicEventArgs> ClientUnsubscribedTopic;
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
public event EventHandler<MqttClientDisconnectedEventArgs> ClientDisconnected;
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
public void OnClientSubscribedTopic(string clientId, TopicFilter topicFilter)
{
ClientSubscribedTopic?.Invoke(this, new MqttClientSubscribedTopicEventArgs(clientId, topicFilter));
}
public void OnClientUnsubscribedTopic(string clientId, string topicFilter)
{
ClientUnsubscribedTopic?.Invoke(this, new MqttClientUnsubscribedTopicEventArgs(clientId, topicFilter));
}
public void OnClientDisconnected(string clientId, bool wasCleanDisconnect)
{
ClientDisconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientId, wasCleanDisconnect));
}
public void OnApplicationMessageReceived(string senderClientId, MqttApplicationMessage applicationMessage)
{
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(senderClientId, applicationMessage));
}
public void OnClientConnected(string clientId)
{
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientId));
}
}
}
@@ -1,5 +1,6 @@
using System;
using System.Net;
using System.Security.Authentication;
namespace MQTTnet.Server
{
@@ -80,6 +81,12 @@ namespace MQTTnet.Server
return this;
}
public MqttServerOptionsBuilder WithEncryptionSslProtocol(SslProtocols value)
{
_options.TlsEndpointOptions.SslProtocol = value;
return this;
}
public MqttServerOptionsBuilder WithoutEncryptedEndpoint()
{
_options.TlsEndpointOptions.IsEnabled = false;
@@ -1,4 +1,6 @@
namespace MQTTnet.Server
using System.Security.Authentication;
namespace MQTTnet.Server
{
public class MqttServerTlsTcpEndpointOptions : MqttServerTcpEndpointBaseOptions
{
@@ -8,5 +10,8 @@
}
public byte[] Certificate { get; set; }
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
}
}
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
@@ -7,9 +7,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.2.1" />
<PackageReference Include="MSTest.TestFramework" Version="1.2.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
<PackageReference Include="MSTest.TestFramework" Version="1.3.2" />
</ItemGroup>
<ItemGroup>
@@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.IO.Pipelines;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features;
namespace MQTTnet.AspNetCore.Tests.Mockups
{
public class ConnectionContextMockup : ConnectionContext
{
public override string ConnectionId { get; set; }
public override IFeatureCollection Features { get; }
public override IDictionary<object, object> Items { get; set; }
public override IDuplexPipe Transport { get; set; }
public ConnectionContextMockup()
{
//Transport = new DefaultConnectionContext
}
}
}
@@ -0,0 +1,16 @@
using System.IO.Pipelines;
namespace MQTTnet.AspNetCore.Tests.Mockups
{
public class DuplexPipeMockup : IDuplexPipe
{
PipeReader IDuplexPipe.Input => Receive.Reader;
PipeWriter IDuplexPipe.Output => Send.Writer;
public Pipe Receive { get; set; } = new Pipe();
public Pipe Send { get; set; } = new Pipe();
}
}
@@ -0,0 +1,51 @@
using Microsoft.AspNetCore.Connections;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.AspNetCore.Tests.Mockups;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Serializer;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.AspNetCore.Tests
{
[TestClass]
public class MqttConnectionContextTest
{
[TestMethod]
public async Task TestReceivePacketAsyncThrowsWhenReaderCompleted()
{
var serializer = new MqttPacketSerializer {};
var pipe = new DuplexPipeMockup();
var connection = new DefaultConnectionContext();
connection.Transport = pipe;
var ctx = new MqttConnectionContext(serializer, connection);
pipe.Receive.Writer.Complete();
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(TimeSpan.FromSeconds(1), CancellationToken.None));
}
[TestMethod]
public async Task TestParallelWrites()
{
var serializer = new MqttPacketSerializer { };
var pipe = new DuplexPipeMockup();
var connection = new DefaultConnectionContext();
connection.Transport = pipe;
var ctx = new MqttConnectionContext(serializer, connection);
var tasks = Enumerable.Range(1, 10).Select(_ => Task.Run(async () =>
{
for (int i = 0; i < 100; i++)
{
await ctx.SendPacketAsync(new MqttPublishPacket(), CancellationToken.None).ConfigureAwait(false);
}
}));
await Task.WhenAll(tasks).ConfigureAwait(false);
}
}
}
@@ -1,6 +1,4 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using MQTTnet.Diagnostics;
namespace MQTTnet.Benchmarks
@@ -3,14 +3,15 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<DebugType>Full</DebugType>
<TargetFramework>net461</TargetFramework>
<LangVersion>7.2</LangVersion>
<TargetFramework Condition=" '$(OS)' == 'Windows_NT' ">net461</TargetFramework>
<TargetFramework Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.1</TargetFramework>
<LangVersion>7.2</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.10.14" />
<PackageReference Include="System.IO.Pipelines" Version="4.5.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.0" />
<PackageReference Include="BenchmarkDotNet" Version="0.11.1" />
<PackageReference Include="System.IO.Pipelines" Version="4.5.2" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.4" />
</ItemGroup>
<ItemGroup>
@@ -1,7 +1,4 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using MQTTnet.Client;
using MQTTnet.Server;
@@ -2,8 +2,6 @@
using MQTTnet.Packets;
using MQTTnet.Serializer;
using MQTTnet.Internal;
using BenchmarkDotNet.Attributes.Jobs;
using BenchmarkDotNet.Attributes.Exporters;
using System;
using System.Threading;
using System.Threading.Tasks;
@@ -1,6 +1,4 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using MQTTnet.Server;
using System;
@@ -0,0 +1,108 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Internal;
namespace MQTTnet.Core.Tests
{
[TestClass]
public class BlockingQueueTests
{
[TestMethod]
public void Preserve_Order()
{
var queue = new BlockingQueue<string>();
queue.Enqueue("a");
queue.Enqueue("b");
queue.Enqueue("c");
Assert.AreEqual(3, queue.Count);
Assert.AreEqual("a", queue.Dequeue());
Assert.AreEqual("b", queue.Dequeue());
Assert.AreEqual("c", queue.Dequeue());
}
[TestMethod]
public void Remove_First_Items()
{
var queue = new BlockingQueue<string>();
queue.Enqueue("a");
queue.Enqueue("b");
queue.Enqueue("c");
Assert.AreEqual("a", queue.RemoveFirst());
Assert.AreEqual("b", queue.RemoveFirst());
Assert.AreEqual(1, queue.Count);
Assert.AreEqual("c", queue.Dequeue());
}
[TestMethod]
public void Clear_Items()
{
var queue = new BlockingQueue<string>();
queue.Enqueue("a");
queue.Enqueue("b");
queue.Enqueue("c");
Assert.AreEqual(3, queue.Count);
queue.Clear();
Assert.AreEqual(0, queue.Count);
}
[TestMethod]
public async Task Wait_For_Item()
{
var queue = new BlockingQueue<string>();
string item = null;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(() =>
{
item = queue.Dequeue();
});
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
await Task.Delay(100);
Assert.AreEqual(0, queue.Count);
Assert.AreEqual(null, item);
queue.Enqueue("x");
await Task.Delay(100);
Assert.AreEqual("x", item);
Assert.AreEqual(0, queue.Count);
}
[TestMethod]
public void Wait_For_Times()
{
var number = 0;
var queue = new BlockingQueue<int>();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(async () =>
{
while (true)
{
queue.Enqueue(1);
await Task.Delay(100);
}
});
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
while (number < 50)
{
queue.Dequeue();
Interlocked.Increment(ref number);
}
}
}
}
@@ -3,16 +3,17 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<DebugType>Full</DebugType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
<PackageReference Include="MSTest.TestFramework" Version="1.3.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj" />
<ProjectReference Include="..\..\Source\MQTTnet\MQTTnet.csproj" />
</ItemGroup>
@@ -0,0 +1,38 @@
using MQTTnet.Extensions.ManagedClient;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Server;
namespace MQTTnet.Core.Tests
{
[TestClass]
public class ManagedMqttClientTests
{
[TestMethod]
public async Task Drop_New_Messages_On_Full_Queue()
{
var factory = new MqttFactory();
var managedClient = factory.CreateManagedMqttClient();
var clientOptions = new ManagedMqttClientOptionsBuilder()
.WithMaxPendingMessages(5)
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropNewMessage);
clientOptions.WithClientOptions(o => o.WithTcpServer("localhost"));
await managedClient.StartAsync(clientOptions.Build());
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "1" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "2" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "3" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "4" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "5" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "6" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "7" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "8" });
Assert.AreEqual(5, managedClient.PendingApplicationMessagesCount);
}
}
}
+96 -6
View File
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
@@ -8,6 +10,7 @@ using MQTTnet.Client;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Server;
namespace MQTTnet.Core.Tests
{
@@ -15,7 +18,7 @@ namespace MQTTnet.Core.Tests
public class MqttClientTests
{
[TestMethod]
public async Task ClientDisconnectException()
public async Task Client_Disconnect_Exception()
{
var factory = new MqttFactory();
var client = factory.CreateMqttClient();
@@ -39,9 +42,95 @@ namespace MQTTnet.Core.Tests
Assert.IsInstanceOfType(ex.InnerException, typeof(SocketException));
}
[TestMethod]
public async Task Client_Publish()
{
var server = new MqttFactory().CreateMqttServer();
try
{
var receivedMessages = new List<MqttApplicationMessage>();
await server.StartAsync(new MqttServerOptions());
var client1 = new MqttFactory().CreateMqttClient();
client1.ApplicationMessageReceived += (_, e) =>
{
lock (receivedMessages)
{
receivedMessages.Add(e.ApplicationMessage);
}
};
await client1.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build());
await client1.SubscribeAsync("a");
var client2 = new MqttFactory().CreateMqttClient();
await client2.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build());
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithRetainFlag().Build();
await client2.PublishAsync(message);
await Task.Delay(500);
Assert.AreEqual(1, receivedMessages.Count);
Assert.IsFalse(receivedMessages.First().Retain); // Must be false even if set above!
}
finally
{
await server.StopAsync();
}
}
[TestMethod]
public async Task Publish_Special_Content()
{
var factory = new MqttFactory();
var server = factory.CreateMqttServer();
var serverOptions = new MqttServerOptionsBuilder().Build();
var receivedMessages = new List<MqttApplicationMessage>();
var client = factory.CreateMqttClient();
try
{
await server.StartAsync(serverOptions);
client.Connected += async (s, e) =>
{
await client.SubscribeAsync("RCU/P1/H0001/R0003");
var msg = new MqttApplicationMessageBuilder()
.WithPayload("DA|18RS00SC00XI0000RV00R100R200R300R400L100L200L300L400Y100Y200AC0102031800BELK0000BM0000|")
.WithTopic("RCU/P1/H0001/R0003");
await client.PublishAsync(msg.Build());
};
client.ApplicationMessageReceived += (s, e) =>
{
lock (receivedMessages)
{
receivedMessages.Add(e.ApplicationMessage);
}
};
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
await Task.Delay(500);
Assert.AreEqual(1, receivedMessages.Count);
Assert.AreEqual("DA|18RS00SC00XI0000RV00R100R200R300R400L100L200L300L400Y100Y200AC0102031800BELK0000BM0000|", receivedMessages.First().ConvertPayloadToString());
}
finally
{
await server.StopAsync();
}
}
#if DEBUG
[TestMethod]
public async Task ClientCleanupOnAuthentificationFails()
public async Task Client_Cleanup_On_Authentification_Fails()
{
var channel = new TestMqttCommunicationAdapter();
var channel2 = new TestMqttCommunicationAdapter();
@@ -50,11 +139,12 @@ namespace MQTTnet.Core.Tests
Task.Run(async () => {
var connect = await channel2.ReceivePacketAsync(TimeSpan.Zero, CancellationToken.None);
await channel2.SendPacketAsync(new MqttConnAckPacket() { ConnectReturnCode = Protocol.MqttConnectReturnCode.ConnectionRefusedNotAuthorized }, CancellationToken.None);
await channel2.SendPacketAsync(new MqttConnAckPacket
{
ConnectReturnCode = Protocol.MqttConnectReturnCode.ConnectionRefusedNotAuthorized
}, CancellationToken.None);
});
var fake = new TestMqttCommunicationAdapterFactory(channel);
var client = new MqttClient(fake, new MqttNetLogger());
+200 -26
View File
@@ -4,7 +4,9 @@ using MQTTnet.Diagnostics;
using MQTTnet.Protocol;
using MQTTnet.Server;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -98,11 +100,11 @@ namespace MQTTnet.Core.Tests
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithAtLeastOnceQoS().Build();
await c2.PublishAsync(message);
await Task.Delay(1000);
Assert.AreEqual(0, receivedMessagesCount);
var subscribeEventCalled = false;
s.ClientSubscribedTopic += (_, e) =>
{
@@ -116,7 +118,7 @@ namespace MQTTnet.Core.Tests
await c2.PublishAsync(message);
await Task.Delay(500);
Assert.AreEqual(1, receivedMessagesCount);
var unsubscribeEventCalled = false;
s.ClientUnsubscribedTopic += (_, e) =>
{
@@ -226,6 +228,39 @@ namespace MQTTnet.Core.Tests
Assert.AreEqual(2000, receivedMessagesCount);
}
[TestMethod]
public async Task MqttServer_SessionTakeover()
{
var server = new MqttFactory().CreateMqttServer();
try
{
await server.StartAsync(new MqttServerOptions());
var client1 = new MqttFactory().CreateMqttClient();
var client2 = new MqttFactory().CreateMqttClient();
var options = new MqttClientOptionsBuilder()
.WithTcpServer("localhost")
.WithCleanSession(false)
.WithClientId("a").Build();
await client1.ConnectAsync(options);
await Task.Delay(500);
await client2.ConnectAsync(options);
await Task.Delay(500);
Assert.IsFalse(client1.IsConnected);
Assert.IsTrue(client2.IsConnected);
}
finally
{
await server.StopAsync();
}
}
private static async Task Publish(IMqttClient c1, MqttApplicationMessage message)
{
@@ -234,7 +269,7 @@ namespace MQTTnet.Core.Tests
await c1.PublishAsync(message);
}
}
[TestMethod]
public async Task MqttServer_ShutdownDisconnectsClientsGracefully()
{
@@ -266,11 +301,6 @@ namespace MQTTnet.Core.Tests
[TestMethod]
public async Task MqttServer_HandleCleanDisconnect()
{
MqttNetGlobalLogger.LogMessagePublished += (_, e) =>
{
System.Diagnostics.Debug.WriteLine($"[{e.TraceMessage.Timestamp:s}] {e.TraceMessage.Source} {e.TraceMessage.Message}");
};
var serverAdapter = new MqttTcpServerAdapter(new MqttNetLogger().CreateChildLogger());
var s = new MqttFactory().CreateMqttServer(new[] { serverAdapter }, new MqttNetLogger());
@@ -303,6 +333,104 @@ namespace MQTTnet.Core.Tests
Assert.AreEqual(clientConnectedCalled, clientDisconnectedCalled);
}
[TestMethod]
public async Task MqttServer_Client_Disconnect_Without_Errors()
{
var errors = 0;
MqttNetGlobalLogger.LogMessagePublished += (_, e) =>
{
System.Diagnostics.Debug.WriteLine($"[{e.TraceMessage.Timestamp:s}] {e.TraceMessage.Source} {e.TraceMessage.Message}");
if (e.TraceMessage.Level == MqttNetLogLevel.Error)
{
errors++;
}
};
bool clientWasConnected;
var server = new MqttFactory().CreateMqttServer();
try
{
var options = new MqttServerOptionsBuilder().Build();
await server.StartAsync(options);
var client = new MqttFactory().CreateMqttClient();
var clientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("localhost")
.Build();
await client.ConnectAsync(clientOptions);
clientWasConnected = true;
await client.DisconnectAsync();
await Task.Delay(500);
}
finally
{
await server.StopAsync();
}
Assert.IsTrue(clientWasConnected);
Assert.AreEqual(0, errors);
}
[TestMethod]
public async Task MqttServer_LotsOfRetainedMessages()
{
const int ClientCount = 100;
var server = new MqttFactory().CreateMqttServer();
try
{
await server.StartAsync(new MqttServerOptionsBuilder().Build());
Parallel.For(
0,
ClientCount,
new ParallelOptions { MaxDegreeOfParallelism = 10 },
i =>
{
using (var client = new MqttFactory().CreateMqttClient())
{
client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build())
.GetAwaiter().GetResult();
for (var j = 0; j < 10; j++)
{
// Clear retained message.
client.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("r" + i)
.WithPayload(new byte[0]).WithRetainFlag().Build()).GetAwaiter().GetResult();
// Set retained message.
client.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("r" + i)
.WithPayload("value" + j).WithRetainFlag().Build()).GetAwaiter().GetResult();
}
client.DisconnectAsync().GetAwaiter().GetResult();
}
});
await Task.Delay(100);
var retainedMessages = server.GetRetainedMessages();
Assert.AreEqual(ClientCount, retainedMessages.Count);
for (var i = 0; i < ClientCount; i++)
{
Assert.IsTrue(retainedMessages.Any(m => m.Topic == "r" + i));
}
}
finally
{
await server.StopAsync();
}
}
[TestMethod]
public async Task MqttServer_RetainedMessagesFlow()
{
@@ -335,6 +463,8 @@ namespace MQTTnet.Core.Tests
}
await c2.DisconnectAsync();
await s.StopAsync();
}
[TestMethod]
@@ -373,7 +503,7 @@ namespace MQTTnet.Core.Tests
var serverAdapter = new TestMqttServerAdapter();
var s = new MqttFactory().CreateMqttServer(new[] { serverAdapter }, new MqttNetLogger());
var receivedMessagesCount = 0;
var receivedMessages = new List<MqttApplicationMessage>();
try
{
await s.StartAsync(new MqttServerOptions());
@@ -383,7 +513,14 @@ namespace MQTTnet.Core.Tests
await c1.DisconnectAsync();
var c2 = await serverAdapter.ConnectTestClient("c2");
c2.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
c2.ApplicationMessageReceived += (_, e) =>
{
lock (receivedMessages)
{
receivedMessages.Add(e.ApplicationMessage);
}
};
await c2.SubscribeAsync(new TopicFilterBuilder().WithTopic("retained").Build());
await Task.Delay(500);
@@ -393,7 +530,8 @@ namespace MQTTnet.Core.Tests
await s.StopAsync();
}
Assert.AreEqual(1, receivedMessagesCount);
Assert.AreEqual(1, receivedMessages.Count);
Assert.IsTrue(receivedMessages.First().Retain);
}
[TestMethod]
@@ -568,7 +706,7 @@ namespace MQTTnet.Core.Tests
await server.StartAsync(options);
var clientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("localhost").Build();
@@ -593,7 +731,7 @@ namespace MQTTnet.Core.Tests
{
await client.DisconnectAsync();
await server.StopAsync();
client.Dispose();
}
}
@@ -601,26 +739,29 @@ namespace MQTTnet.Core.Tests
[TestMethod]
public async Task MqttServer_SameClientIdConnectDisconnectEventOrder()
{
var serverAdapter = new MqttTcpServerAdapter(new MqttNetLogger().CreateChildLogger());
var s = new MqttFactory().CreateMqttServer(new[] { serverAdapter }, new MqttNetLogger());
var s = new MqttFactory().CreateMqttServer();
var connectedClient = false;
var connecteCalledBeforeConnectedClients = false;
var events = new List<string>();
s.ClientConnected += (_, __) =>
{
connecteCalledBeforeConnectedClients |= connectedClient;
connectedClient = true;
lock (events)
{
events.Add("c");
}
};
s.ClientDisconnected += (_, __) =>
{
connectedClient = false;
lock (events)
{
events.Add("d");
}
};
var clientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("localhost")
.WithClientId(Guid.NewGuid().ToString())
.WithClientId("same_id")
.Build();
await s.StartAsync(new MqttServerOptions());
@@ -630,20 +771,53 @@ namespace MQTTnet.Core.Tests
await c1.ConnectAsync(clientOptions);
await Task.Delay(100);
await Task.Delay(250);
await c2.ConnectAsync(clientOptions);
await Task.Delay(100);
await Task.Delay(250);
await c1.DisconnectAsync();
await Task.Delay(250);
await c2.DisconnectAsync();
await Task.Delay(250);
await s.StopAsync();
await Task.Delay(100);
var flow = string.Join(string.Empty, events);
Assert.AreEqual("cdcd", flow);
}
Assert.IsFalse(connecteCalledBeforeConnectedClients, "ClientConnected was called before ClientDisconnect was called");
[TestMethod]
public async Task MqttServer_StopAndRestart()
{
var server = new MqttFactory().CreateMqttServer();
await server.StartAsync(new MqttServerOptions());
var client = new MqttFactory().CreateMqttClient();
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
await server.StopAsync();
try
{
var client2 = new MqttFactory().CreateMqttClient();
await client2.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
Assert.Fail("Connecting should fail.");
}
catch (Exception)
{
}
await server.StartAsync(new MqttServerOptions());
var client3 = new MqttFactory().CreateMqttClient();
await client3.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
await server.StopAsync();
}
private class TestStorage : IMqttServerStorage
@@ -1,6 +1,4 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using MQTTnet.Server;
@@ -13,7 +11,7 @@ namespace MQTTnet.Core.Tests
[TestMethod]
public void MqttSubscriptionsManager_SubscribeSingleSuccess()
{
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServer(new IMqttServerAdapter[0], new MqttNetLogger().CreateChildLogger("")));
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServerEventDispatcher());
var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new TopicFilterBuilder().WithTopic("A/B/C").Build());
@@ -28,7 +26,7 @@ namespace MQTTnet.Core.Tests
[TestMethod]
public void MqttSubscriptionsManager_SubscribeDifferentQoSSuccess()
{
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServer(new IMqttServerAdapter[0], new MqttNetLogger().CreateChildLogger("")));
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServerEventDispatcher());
var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new TopicFilter("A/B/C", MqttQualityOfServiceLevel.AtMostOnce));
@@ -43,7 +41,7 @@ namespace MQTTnet.Core.Tests
[TestMethod]
public void MqttSubscriptionsManager_SubscribeTwoTimesSuccess()
{
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServer(new IMqttServerAdapter[0], new MqttNetLogger().CreateChildLogger("")));
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServerEventDispatcher());
var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new TopicFilter("#", MqttQualityOfServiceLevel.AtMostOnce));
@@ -59,7 +57,7 @@ namespace MQTTnet.Core.Tests
[TestMethod]
public void MqttSubscriptionsManager_SubscribeSingleNoSuccess()
{
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServer(new IMqttServerAdapter[0], new MqttNetLogger().CreateChildLogger("")));
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServerEventDispatcher());
var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new TopicFilterBuilder().WithTopic("A/B/C").Build());
@@ -72,7 +70,7 @@ namespace MQTTnet.Core.Tests
[TestMethod]
public void MqttSubscriptionsManager_SubscribeAndUnsubscribeSingle()
{
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServer(new IMqttServerAdapter[0], new MqttNetLogger().CreateChildLogger("")));
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServerEventDispatcher());
var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new TopicFilterBuilder().WithTopic("A/B/C").Build());
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Client;
using MQTTnet.Server;
namespace MQTTnet.Core.Tests
{
[TestClass]
public class RoundtripTimeTests
{
[TestMethod]
public async Task Round_Trip_Time()
{
var factory = new MqttFactory();
var server = factory.CreateMqttServer();
var receiverClient = factory.CreateMqttClient();
var senderClient = factory.CreateMqttClient();
await server.StartAsync(new MqttServerOptionsBuilder().Build());
await receiverClient.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
await receiverClient.SubscribeAsync("#");
await senderClient.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
TaskCompletionSource<string> response = null;
receiverClient.ApplicationMessageReceived += (sender, args) =>
{
response?.SetResult(args.ApplicationMessage.ConvertPayloadToString());
};
var times = new List<TimeSpan>();
var stopwatch = Stopwatch.StartNew();
for (var i = 0; i < 100; i++)
{
response = new TaskCompletionSource<string>();
await senderClient.PublishAsync("test", DateTime.UtcNow.Ticks.ToString());
response.Task.GetAwaiter().GetResult();
stopwatch.Stop();
times.Add(stopwatch.Elapsed);
stopwatch.Restart();
}
await server.StopAsync();
}
}
}
@@ -11,19 +11,20 @@ namespace MQTTnet.Core.Tests
/// publishes a message with a client and waits in the server until a message with the same topic is received
/// </summary>
/// <returns></returns>
public static async Task PublishAndWaitForAsync(this IMqttClient client, IMqttServer server, MqttApplicationMessage message)
public static async Task PublishAndWaitForAsync(this IMqttClient client, IMqttServer server, MqttApplicationMessage message)
{
var tcs = new TaskCompletionSource<object>();
EventHandler<MqttApplicationMessageReceivedEventArgs> handler = (sender, args) =>
void Handler(object sender, MqttApplicationMessageReceivedEventArgs args)
{
if (args.ApplicationMessage.Topic == message.Topic)
{
tcs.SetResult(true);
}
};
server.ApplicationMessageReceived += handler;
}
server.ApplicationMessageReceived += Handler;
try
{
await client.PublishAsync(message).ConfigureAwait(false);
@@ -31,7 +32,7 @@ namespace MQTTnet.Core.Tests
}
finally
{
server.ApplicationMessageReceived -= handler;
server.ApplicationMessageReceived -= Handler;
}
}
}
@@ -10,8 +10,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.4" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.5" />
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.1.1" />
</ItemGroup>
<ItemGroup>
@@ -3,11 +3,12 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<DebugType>Full</DebugType>
<TargetFrameworks>netcoreapp2.0;net452;net461</TargetFrameworks>
<TargetFrameworks>netcoreapp2.1</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netcoreapp2.0|AnyCPU'">
<DefineConstants>RELEASE;NETCOREAPP2_0</DefineConstants>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netcoreapp2.1|AnyCPU'">
<DefineConstants>RELEASE;NETCOREAPP2_1</DefineConstants>
</PropertyGroup>
<ItemGroup>
@@ -61,13 +61,7 @@ namespace MQTTnet.TestApp.NetCore
public class RandomPassword : IMqttClientCredentials
{
public string Password
{
get
{
return Guid.NewGuid().ToString(); // The random password.
}
}
public string Password => Guid.NewGuid().ToString();
public string Username => "the_static_user";
}
@@ -142,10 +142,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.1.5</Version>
<Version>6.1.9</Version>
</PackageReference>
<PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls">
<Version>3.0.0</Version>
<Version>4.0.0</Version>
</PackageReference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
+1 -1
View File
@@ -3,7 +3,7 @@ image: Visual Studio 2017
configuration: Release
before_build:
- cmd: >-
msbuild /t:restore
msbuild /t:restore MQTTnet.sln
cd Tests/MQTTnet.TestApp.AspNetCore2/