mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 00:15:15 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
195357835b | ||
|
|
28b2562112 | ||
|
|
ab24368de1 | ||
|
|
9b51404b4d | ||
|
|
619eaf284d | ||
|
|
86c348df58 | ||
|
|
fdb2ec897c | ||
|
|
b091fee554 | ||
|
|
14ec6ee793 | ||
|
|
0cfef0649a | ||
|
|
7292af7db4 | ||
|
|
bc1a197c4f | ||
|
|
ccc275902f | ||
|
|
83eca982e9 | ||
|
|
017f17a6b1 | ||
|
|
b3564e01a4 | ||
|
|
43105f71d8 | ||
|
|
40c408a9c0 | ||
|
|
91a918d30b | ||
|
|
2a93fc18e4 | ||
|
|
31de6ee186 | ||
|
|
74d59673a8 | ||
|
|
d1c3d9b453 | ||
|
|
baa9241216 | ||
|
|
2c6c3ac6a4 | ||
|
|
b2c31331b6 | ||
|
|
9ff39c1fd9 | ||
|
|
210d57b254 | ||
|
|
d7b98080f5 | ||
|
|
3e7b41e712 | ||
|
|
5f18f35e02 | ||
|
|
af39e87703 | ||
|
|
fcff3a2c8d | ||
|
|
803dd6f64e | ||
|
|
a1e258eb24 | ||
|
|
e9df698a1f | ||
|
|
bddcbf6999 | ||
|
|
be22e2551d | ||
|
|
4e01c7f13d | ||
|
|
7181813f12 | ||
|
|
6fc9f12c34 | ||
|
|
cc4e37fa40 | ||
|
|
ccadd4e41a | ||
|
|
a5caab62f8 | ||
|
|
2bff218806 | ||
|
|
95c30a2843 | ||
|
|
28efbcdb3d | ||
|
|
ca7952ab33 | ||
|
|
62693af196 | ||
|
|
5abee2512f | ||
|
|
c9a6155834 | ||
|
|
aad3ab903f | ||
|
|
4c76a898b6 | ||
|
|
d273148bc5 | ||
|
|
5314f5c67a | ||
|
|
22aab44d49 | ||
|
|
b819746165 | ||
|
|
73d33e2200 | ||
|
|
740bc3aa30 | ||
|
|
583b32f17a | ||
|
|
b7ab29473b | ||
|
|
ffc4f1408a | ||
|
|
c8b3066883 | ||
|
|
fb4f89b412 | ||
|
|
6bee6a0469 | ||
|
|
0c2ab9b231 | ||
|
|
03bef4c15d | ||
|
|
ba62ca7306 | ||
|
|
56a1530cee | ||
|
|
699cd5fc23 | ||
|
|
381c59d1e5 | ||
|
|
70f4dc9e10 | ||
|
|
0367ad31f1 | ||
|
|
5c6bcdbca6 | ||
|
|
400c0f05ad | ||
|
|
00df7c9b7b | ||
|
|
a272ed7daa | ||
|
|
b2e7a2e370 | ||
|
|
39bc70cbf0 | ||
|
|
682298b69f | ||
|
|
028a39ec25 | ||
|
|
3b104049b4 | ||
|
|
dd83a360b4 | ||
|
|
976c62d9c2 | ||
|
|
4cc9f12a03 | ||
|
|
0b5a751c56 | ||
|
|
cdb05e5894 | ||
|
|
6c08b5caf3 | ||
|
|
d5b53f3712 | ||
|
|
80fbb2f0b2 | ||
|
|
b4f90decc7 | ||
|
|
61b324043a | ||
|
|
2ced921008 | ||
|
|
1e2fee0f33 | ||
|
|
ac36c9c406 | ||
|
|
f70c79aaf9 | ||
|
|
0839fa5757 | ||
|
|
60bc4ebcee | ||
|
|
65e239652d | ||
|
|
6b9015a928 | ||
|
|
a740c38ee6 | ||
|
|
603639126e | ||
|
|
043cfdb3f9 | ||
|
|
b0da4b2b2d | ||
|
|
5402859a30 | ||
|
|
2b10200101 | ||
|
|
55e8927ab7 | ||
|
|
d30fe61264 | ||
|
|
f7ea2d29e1 | ||
|
|
e6cfef5295 | ||
|
|
0dea9e6a2b | ||
|
|
ae80de26ec | ||
|
|
53bd2a0960 | ||
|
|
b55f5ff298 | ||
|
|
21048b51ae | ||
|
|
773fd09166 | ||
|
|
57532a290a | ||
|
|
740a35b018 | ||
|
|
6e995230a7 | ||
|
|
65a8b091b9 | ||
|
|
fa34908d82 | ||
|
|
b09eb1213c | ||
|
|
23ae8ab3b5 | ||
|
|
0bdb7e9e44 | ||
|
|
54e3cd7cb7 | ||
|
|
16ad07cfc9 | ||
|
|
dee0adcd8c | ||
|
|
acbf1226ab | ||
|
|
f002744d87 | ||
|
|
9efee7af21 | ||
|
|
9814b10496 | ||
|
|
978694b3c0 | ||
|
|
bc6f155f12 | ||
|
|
d494c2104b | ||
|
|
8035c6859f | ||
|
|
2f10295022 | ||
|
|
e5f32730a7 | ||
|
|
cd257b4d25 | ||
|
|
ce534baa75 | ||
|
|
2b420b8e66 | ||
|
|
35300abfb5 | ||
|
|
3d0ecc05ff | ||
|
|
bfb6ec8856 | ||
|
|
bf1d682a85 | ||
|
|
7d9326a147 | ||
|
|
7fd09009e7 | ||
|
|
9fa5e2dc1f |
@@ -10,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>* Updated to MQTTnet 2.8.0.
|
||||
</releaseNotes>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="2.8.0" />
|
||||
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.0" />
|
||||
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.0.1" />
|
||||
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.0.1" />
|
||||
<dependency id="MQTTnet" version="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>* Updated to MQTTnet 2.8.0.
|
||||
</releaseNotes>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="2.8.0" />
|
||||
<dependency id="MQTTnet" version="2.8.5" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
@@ -33,6 +32,9 @@
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\"/>
|
||||
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net462\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net470\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net471\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net472\"/>
|
||||
</files>
|
||||
</package>
|
||||
@@ -10,12 +10,11 @@
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
|
||||
<releaseNotes>* Updated to MQTTnet 2.8.0.
|
||||
</releaseNotes>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="2.8.0" />
|
||||
<dependency id="MQTTnet" version="2.8.5" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
@@ -34,5 +33,8 @@
|
||||
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net462\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net470\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net471\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net472\"/>
|
||||
</files>
|
||||
</package>
|
||||
+18
-29
@@ -10,33 +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> ** MQTTnet is now available at Open Collective for donations (https://opencollective.com/mqttnet). **
|
||||
* [Core] Performance optimizations.
|
||||
* [Core] Due to performance reasons the timestamp of log messages is now in UTC format.
|
||||
* [Core] Added several packet validations.
|
||||
* [Core] Log messages now contain the complete source path including parent components.
|
||||
* [Core] The adapter now has an _Endpoint_ definition as string containing remote IP and port.
|
||||
* [Client] Received messages are now processed completely in the worker thread without creating new Tasks.
|
||||
* [Client] Fixed wrong calculation for sending keep alive packets (thanks to @cstichlberger)
|
||||
* [Client] A clean disconnect (via DisconnectAsync) will no longer throw an exception.
|
||||
* [Client] Added new overloads for quick message publishing.
|
||||
* [ManagedClient] The managed client is moved to a separate nuget package.
|
||||
* [ManagedClient] Added an own message format with extended properties like ID (BREAKING CHANGE).
|
||||
* [ManagedClient] Fixed a loading issue of stored application messages (thanks to @JTrotta).
|
||||
* [ManagedClient] Added a new event which is fired when a synchronization of the subscriptions has failed.
|
||||
* [ManagedClient] Added a new event which is fired when a connection attempt has failed.
|
||||
* [ManagedClient] Exposed a new property which provides the count of not published messages (pending messages count).
|
||||
* [Server] Added support for other WebSocket sub protocol formats like mqttv-3.1.1 (thanks to @israellot).
|
||||
* [Server] The takeover of an existing client sessions is now treated as a _clean_ disconnect of the previous client.
|
||||
* [Server] The pending messages queue per client is now limited to 250 messages. Overflow strategy and count can be changed via options (thanks to @VladimirAkopyan)
|
||||
* [Server] Keep alive checking is now suspended while large packages are being received (and thus the client is connected). Keep alive checking continues after a large packet is received completely.
|
||||
* [Server] Rewritten the _ConnectedClients_ API and added new features for disconnecting and Endpoint information (IP etc.).
|
||||
* [Server] Added settings for disabling persistent sessions and defining a max pending messages queue size per session.
|
||||
* [Server] Persistent sessions are disabled by default (BREAKING CHANGE!).
|
||||
* [Server] Added a new interceptor which is invoked before a new message is added to the client queue.
|
||||
* [Server] Added support for Linux servers by dividing IPv4 and IPv6 support and adding new options (BREAKING CHANGE!).
|
||||
* [Server] Gracefully closed connections are no longer reported as warnings.
|
||||
* [Server] Added new overloads for initializing the ASP.NET Core integration.
|
||||
<releaseNotes> * [Core] 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>
|
||||
@@ -45,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" />
|
||||
@@ -54,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>
|
||||
@@ -77,6 +63,9 @@
|
||||
<file src="..\Source\MQTTnet\bin\Release\net452\MQTTnet.*" target="lib\net452\"/>
|
||||
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net462\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net470\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net471\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net472\"/>
|
||||
</files>
|
||||
</package>
|
||||
@@ -0,0 +1,222 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2010
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Build\build.ps1 = Build\build.ps1
|
||||
Build\MQTTnet.AspNetCore.nuspec = Build\MQTTnet.AspNetCore.nuspec
|
||||
Build\MQTTnet.Extensions.ManagedClient.nuspec = Build\MQTTnet.Extensions.ManagedClient.nuspec
|
||||
Build\MQTTnet.Extensions.Rpc.nuspec = Build\MQTTnet.Extensions.Rpc.nuspec
|
||||
Build\MQTTnet.nuspec = Build\MQTTnet.nuspec
|
||||
Build\upload.ps1 = Build\upload.ps1
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.bettercodehub.yml = .bettercodehub.yml
|
||||
appveyor.yml = appveyor.yml
|
||||
LICENSE = LICENSE
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.NetCore", "Tests\MQTTnet.TestApp.NetCore\MQTTnet.TestApp.NetCore.csproj", "{3D283AAD-AAA8-4339-8394-52F80B6304DB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.AspNetCore2", "Tests\MQTTnet.TestApp.AspNetCore2\MQTTnet.TestApp.AspNetCore2.csproj", "{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore", "Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{12816BCC-AF9E-44A9-9AE5-C246AF2A0587}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.Rpc", "Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj", "{C444E9C8-95FA-430E-9126-274129DE16CD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Benchmarks", "Tests\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{998D04DD-7CB0-45F5-A393-E2495C16399E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedClient", "Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj", "{C400533A-8EBA-4F0B-BF4D-295C3708604B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM = Release|ARM
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x86.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,9 +1,11 @@
|
||||
<p align="center">
|
||||
<img src="https://github.com/chkr1011/MQTTnet/blob/master/Images/Logo_128x128.png?raw=true" width="128">
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
[](https://www.nuget.org/packages/MQTTnet)
|
||||
[](https://ci.appveyor.com/project/chkr1011/mqttnet/branch/develop)
|
||||
[](https://ci.appveyor.com/project/chkr1011/mqttnet)
|
||||
[](https://bettercodehub.com/)
|
||||
[](https://opencollective.com/mqttnet)
|
||||
[](https://opencollective.com/mqttnet)
|
||||
@@ -23,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_.
|
||||
|
||||
@@ -83,11 +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))
|
||||
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
|
||||
* Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>)
|
||||
|
||||
If you use this library and want to see your project here please let me know.
|
||||
If you use this library and want to see your project here please create a pull request.
|
||||
|
||||
## MIT License
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Linq;
|
||||
using MQTTnet.Server;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
@@ -23,10 +24,7 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
|
||||
{
|
||||
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
|
||||
subProtocol = requestedSubProtocolValues
|
||||
.OrderByDescending(p => p.Length)
|
||||
.FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
|
||||
subProtocol = SelectSubProtocol(requestedSubProtocolValues);
|
||||
}
|
||||
|
||||
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
|
||||
@@ -40,6 +38,14 @@ namespace MQTTnet.AspNetCore
|
||||
return app;
|
||||
}
|
||||
|
||||
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
|
||||
{
|
||||
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
|
||||
return requestedSubProtocolValues
|
||||
.OrderByDescending(p => p.Length)
|
||||
.FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
|
||||
}
|
||||
|
||||
public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action<IMqttServer> configure)
|
||||
{
|
||||
var server = app.ApplicationServices.GetRequiredService<IMqttServer>();
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using MQTTnet.Server;
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class AspNetMqttServerOptionsBuilder : MqttServerOptionsBuilder
|
||||
{
|
||||
public AspNetMqttServerOptionsBuilder(IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Server;
|
||||
@@ -13,6 +14,14 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
public override async Task OnConnectedAsync(ConnectionContext connection)
|
||||
{
|
||||
// required for websocket transport to work
|
||||
var transferFormatFeature = connection.Features.Get<ITransferFormatFeature>();
|
||||
if (transferFormatFeature != null)
|
||||
{
|
||||
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
|
||||
}
|
||||
|
||||
|
||||
var serializer = new MqttPacketSerializer();
|
||||
using (var adapter = new MqttConnectionContext(serializer, connection))
|
||||
{
|
||||
|
||||
@@ -32,7 +32,8 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
|
||||
var bodySlice = copy.Slice(0, bodyLength);
|
||||
packet = serializer.Deserialize(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(bodySlice.GetArray(), 0)));
|
||||
var buffer = bodySlice.GetArray();
|
||||
packet = serializer.Deserialize(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(buffer, 0, buffer.Length)));
|
||||
consumed = bodySlice.End;
|
||||
observed = bodySlice.End;
|
||||
return true;
|
||||
|
||||
@@ -20,14 +20,27 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
|
||||
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action<MqttServerOptionsBuilder> configure)
|
||||
{
|
||||
var builder = new MqttServerOptionsBuilder();
|
||||
services.AddSingleton<IMqttServerOptions>(s => {
|
||||
var builder = new MqttServerOptionsBuilder();
|
||||
configure(builder);
|
||||
return builder.Build();
|
||||
});
|
||||
|
||||
configure(builder);
|
||||
|
||||
services.AddSingleton<IMqttServerOptions>(builder.Build());
|
||||
services.AddHostedMqttServer();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
|
||||
{
|
||||
services.AddSingleton<IMqttServerOptions>(s => {
|
||||
var builder = new AspNetMqttServerOptionsBuilder(s);
|
||||
configure(builder);
|
||||
return builder.Build();
|
||||
});
|
||||
|
||||
services.AddHostedMqttServer();
|
||||
|
||||
@@ -44,7 +57,7 @@ namespace MQTTnet.AspNetCore
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
|
||||
private static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
|
||||
{
|
||||
var logger = new MqttNetLogger();
|
||||
var childLogger = logger.CreateChildLogger();
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ApplicationMessageSkippedEventArgs : EventArgs
|
||||
{
|
||||
public ApplicationMessageSkippedEventArgs(ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
||||
}
|
||||
|
||||
public ManagedMqttApplicationMessage ApplicationMessage { get; }
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
@@ -9,6 +10,12 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
TimeSpan AutoReconnectDelay { get; }
|
||||
|
||||
TimeSpan ConnectionCheckInterval { get; }
|
||||
|
||||
IManagedMqttClientStorage Storage { get; }
|
||||
|
||||
int MaxPendingMessages { get; }
|
||||
|
||||
MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461;uap10.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
<Authors />
|
||||
|
||||
@@ -1,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(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
||||
await Task.Delay(Options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -226,9 +252,17 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
while (!cancellationToken.IsCancellationRequested && _mqttClient.IsConnected)
|
||||
{
|
||||
var message = _messageQueue.Take(cancellationToken);
|
||||
//Peek at the message without dequeueing in order to prevent the
|
||||
//possibility of the queue growing beyond the configured cap.
|
||||
//Previously, messages could be re-enqueued if there was an
|
||||
//exception, and this re-enqueueing did not honor the cap.
|
||||
//Furthermore, because re-enqueueing would shuffle the order
|
||||
//of the messages, the DropOldestQueuedMessage strategy would
|
||||
//be unable to know which message is actually the oldest and would
|
||||
//instead drop the first item in the queue.
|
||||
var message = _messageQueue.PeekAndWait();
|
||||
if (message == null)
|
||||
{
|
||||
continue;
|
||||
@@ -258,6 +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)
|
||||
@@ -306,15 +360,15 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
try
|
||||
{
|
||||
if (subscriptions.Any())
|
||||
{
|
||||
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (unsubscriptions.Any())
|
||||
{
|
||||
await _mqttClient.UnsubscribeAsync(unsubscriptions).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (subscriptions.Any())
|
||||
{
|
||||
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -334,7 +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)
|
||||
@@ -367,7 +421,6 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
_publishingCancellationToken = cts;
|
||||
|
||||
Task.Factory.StartNew(() => PublishQueuedMessages(cts.Token), cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
@@ -9,6 +10,12 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public TimeSpan AutoReconnectDelay { get; set; } = TimeSpan.FromSeconds(5);
|
||||
|
||||
public TimeSpan ConnectionCheckInterval { get; set; } = TimeSpan.FromSeconds(1);
|
||||
|
||||
public IManagedMqttClientStorage Storage { get; set; }
|
||||
|
||||
public int MaxPendingMessages { get; set; } = int.MaxValue;
|
||||
|
||||
public MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; set; } = MqttPendingMessagesOverflowStrategy.DropNewMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,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,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461;uap10.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
<Authors />
|
||||
|
||||
@@ -3,7 +3,7 @@ _mqttClient.subscribe("MQTTnet.RPC/+/ping");
|
||||
_mqttClient.subscribe("MQTTnet.RPC/+/do_something");
|
||||
|
||||
// It is not allowed to change the structure of the topic. Otherwise RPC will not work. So method names can be separated using
|
||||
// an _ or . but no +, # or . If it is required to distinguish between devices own rules can be defined like the following.
|
||||
// an _ or . but no +, # or /. If it is required to distinguish between devices own rules can be defined like the following.
|
||||
_mqttClient.subscribe("MQTTnet.RPC/+/deviceA.ping");
|
||||
_mqttClient.subscribe("MQTTnet.RPC/+/deviceB.ping");
|
||||
_mqttClient.subscribe("MQTTnet.RPC/+/deviceC.getTemperature");
|
||||
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
using MQTTnet.Client;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Security.Authentication;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Serializer;
|
||||
using WebSocket4Net;
|
||||
|
||||
namespace MQTTnet.TestApp.NetCore
|
||||
{
|
||||
public class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
|
||||
{
|
||||
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
if (!(options.ChannelOptions is MqttClientWebSocketOptions))
|
||||
{
|
||||
throw new NotSupportedException("Only WebSocket connections are supported.");
|
||||
}
|
||||
|
||||
return new MqttChannelAdapter(new WebSocket4NetMqttChannel(options), new MqttPacketSerializer(), logger);
|
||||
}
|
||||
|
||||
private class WebSocket4NetMqttChannel : IMqttChannel
|
||||
{
|
||||
private readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
|
||||
|
||||
private readonly IMqttClientOptions _clientOptions;
|
||||
private WebSocket4Net.WebSocket _webSocket;
|
||||
|
||||
public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions)
|
||||
{
|
||||
_clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions));
|
||||
}
|
||||
|
||||
public string Endpoint { get; } = "";
|
||||
|
||||
public Task ConnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var channelOptions = (MqttClientWebSocketOptions)_clientOptions.ChannelOptions;
|
||||
|
||||
var uri = "ws://" + channelOptions.Uri;
|
||||
var sslProtocols = SslProtocols.None;
|
||||
|
||||
if (channelOptions.TlsOptions.UseTls)
|
||||
{
|
||||
uri = "wss://" + channelOptions.Uri;
|
||||
sslProtocols = SslProtocols.Tls12;
|
||||
}
|
||||
|
||||
var subProtocol = channelOptions.SubProtocols.FirstOrDefault() ?? string.Empty;
|
||||
|
||||
_webSocket = new WebSocket4Net.WebSocket(uri, subProtocol, sslProtocols: sslProtocols);
|
||||
_webSocket.DataReceived += OnDataReceived;
|
||||
_webSocket.Open();
|
||||
SpinWait.SpinUntil(() => _webSocket.State == WebSocketState.Open, _clientOptions.CommunicationTimeout);
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task DisconnectAsync()
|
||||
{
|
||||
if (_webSocket != null)
|
||||
{
|
||||
_webSocket.DataReceived -= OnDataReceived;
|
||||
_webSocket.Close();
|
||||
SpinWait.SpinUntil(() => _webSocket.State == WebSocketState.Closed, _clientOptions.CommunicationTimeout);
|
||||
}
|
||||
|
||||
_webSocket = null;
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
var readBytes = 0;
|
||||
while (count > 0 && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
byte @byte;
|
||||
if (readBytes == 0)
|
||||
{
|
||||
// Block until at lease one byte was received.
|
||||
@byte = _receiveBuffer.Take(cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_receiveBuffer.TryTake(out @byte))
|
||||
{
|
||||
return Task.FromResult(readBytes);
|
||||
}
|
||||
}
|
||||
|
||||
buffer[offset] = @byte;
|
||||
offset++;
|
||||
count--;
|
||||
readBytes++;
|
||||
}
|
||||
|
||||
return Task.FromResult(readBytes);
|
||||
}
|
||||
|
||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
_webSocket.Send(buffer, offset, count);
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_webSocket != null)
|
||||
{
|
||||
_webSocket.DataReceived -= OnDataReceived;
|
||||
_webSocket.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDataReceived(object sender, WebSocket4Net.DataReceivedEventArgs e)
|
||||
{
|
||||
foreach (var @byte in e.Data)
|
||||
{
|
||||
_receiveBuffer.Add(@byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,13 +97,11 @@ namespace MQTTnet.Adapter
|
||||
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
_logger.Verbose("TX >>> {0}", packet);
|
||||
|
||||
var packetData = PacketSerializer.Serialize(packet);
|
||||
|
||||
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
PacketSerializer.FreeBuffer();
|
||||
|
||||
_logger.Verbose("TX >>> {0}", packet);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -130,11 +128,11 @@ namespace MQTTnet.Adapter
|
||||
|
||||
if (timeout > TimeSpan.Zero)
|
||||
{
|
||||
receivedMqttPacket = await Internal.TaskExtensions.TimeoutAfterAsync(ct => ReceiveAsync(_channel, ct), timeout, cancellationToken).ConfigureAwait(false);
|
||||
receivedMqttPacket = await Internal.TaskExtensions.TimeoutAfterAsync(ReceiveAsync, timeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
receivedMqttPacket = await ReceiveAsync(_channel, cancellationToken).ConfigureAwait(false);
|
||||
receivedMqttPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (receivedMqttPacket == null || cancellationToken.IsCancellationRequested)
|
||||
@@ -149,9 +147,12 @@ namespace MQTTnet.Adapter
|
||||
}
|
||||
|
||||
_logger.Verbose("RX <<< {0}", packet);
|
||||
|
||||
|
||||
return packet;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (IsWrappedException(exception))
|
||||
@@ -165,9 +166,9 @@ namespace MQTTnet.Adapter
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<ReceivedMqttPacket> ReceiveAsync(IMqttChannel channel, CancellationToken cancellationToken)
|
||||
private async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var fixedHeader = await MqttPacketReader.ReadFixedHeaderAsync(channel, _fixedHeaderBuffer, _singleByteBuffer, cancellationToken).ConfigureAwait(false);
|
||||
var fixedHeader = await MqttPacketReader.ReadFixedHeaderAsync(_channel, _fixedHeaderBuffer, _singleByteBuffer, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -190,18 +191,21 @@ namespace MQTTnet.Adapter
|
||||
chunkSize = bytesLeft;
|
||||
}
|
||||
|
||||
#if WINDOWS_UWP
|
||||
var readBytes = await _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
// async/await is not used to avoid the overhead of context switches. We assume that the reamining data
|
||||
// has been sent from the sender directly after the initial bytes.
|
||||
var readBytes = channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).GetAwaiter().GetResult();
|
||||
if (readBytes <= 0)
|
||||
{
|
||||
ExceptionHelper.ThrowGracefulSocketClose();
|
||||
}
|
||||
var readBytes = _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
#endif
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ExceptionHelper.ThrowIfGracefulSocketClose(readBytes);
|
||||
|
||||
bodyOffset += readBytes;
|
||||
} while (bodyOffset < body.Length);
|
||||
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, new MqttPacketBodyReader(body, 0));
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, new MqttPacketBodyReader(body, 0, body.Length));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -236,7 +240,8 @@ namespace MQTTnet.Adapter
|
||||
{
|
||||
if (exception is IOException && exception.InnerException is SocketException socketException)
|
||||
{
|
||||
if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
|
||||
if (socketException.SocketErrorCode == SocketError.ConnectionAborted ||
|
||||
socketException.SocketErrorCode == SocketError.OperationAborted)
|
||||
{
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -7,5 +7,9 @@ namespace MQTTnet.Client
|
||||
IMqttClient CreateMqttClient();
|
||||
|
||||
IMqttClient CreateMqttClient(IMqttNetLogger logger);
|
||||
|
||||
IMqttClient CreateMqttClient(IMqttClientAdapterFactory adapterFactory);
|
||||
|
||||
IMqttClient CreateMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory adapterFactory);
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,12 @@ namespace MQTTnet.Client
|
||||
private readonly IMqttClientAdapterFactory _adapterFactory;
|
||||
private readonly IMqttNetChildLogger _logger;
|
||||
|
||||
private IMqttClientOptions _options;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private Task _packetReceiverTask;
|
||||
private Task _keepAliveMessageSenderTask;
|
||||
internal Task _packetReceiverTask;
|
||||
internal Task _keepAliveMessageSenderTask;
|
||||
private IMqttChannelAdapter _adapter;
|
||||
private bool _cleanDisconnectInitiated;
|
||||
private long _disconnectGate;
|
||||
|
||||
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
|
||||
{
|
||||
@@ -43,6 +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)
|
||||
{
|
||||
@@ -53,15 +54,17 @@ namespace MQTTnet.Client
|
||||
|
||||
try
|
||||
{
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_options = options;
|
||||
Options = options;
|
||||
|
||||
_packetIdentifierProvider.Reset();
|
||||
_packetDispatcher.Reset();
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_disconnectGate = 0;
|
||||
_adapter = _adapterFactory.CreateClientAdapter(options, _logger);
|
||||
|
||||
_logger.Verbose($"Trying to connect with server ({_options.ChannelOptions}).");
|
||||
await _adapter.ConnectAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
_logger.Verbose($"Trying to connect with server ({Options.ChannelOptions}).");
|
||||
await _adapter.ConnectAsync(Options.CommunicationTimeout, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
_logger.Verbose("Connection with server established.");
|
||||
|
||||
StartReceivingPackets(_cancellationTokenSource.Token);
|
||||
@@ -71,7 +74,7 @@ namespace MQTTnet.Client
|
||||
|
||||
_sendTracker.Restart();
|
||||
|
||||
if (_options.KeepAlivePeriod != TimeSpan.Zero)
|
||||
if (Options.KeepAlivePeriod != TimeSpan.Zero)
|
||||
{
|
||||
StartSendingKeepAliveMessages(_cancellationTokenSource.Token);
|
||||
}
|
||||
@@ -85,7 +88,11 @@ namespace MQTTnet.Client
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Error while connecting with server.");
|
||||
await DisconnectInternalAsync(null, exception).ConfigureAwait(false);
|
||||
|
||||
if (!DisconnectIsPending())
|
||||
{
|
||||
await DisconnectInternalAsync(null, exception).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
@@ -104,7 +111,10 @@ namespace MQTTnet.Client
|
||||
}
|
||||
finally
|
||||
{
|
||||
await DisconnectInternalAsync(null, null).ConfigureAwait(false);
|
||||
if (!DisconnectIsPending())
|
||||
{
|
||||
await DisconnectInternalAsync(null, null).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,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));
|
||||
|
||||
@@ -142,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)
|
||||
@@ -176,21 +186,23 @@ namespace MQTTnet.Client
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel(false);
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
|
||||
_adapter?.Dispose();
|
||||
_adapter = null;
|
||||
}
|
||||
|
||||
private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
@@ -205,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)
|
||||
@@ -215,21 +227,22 @@ namespace MQTTnet.Client
|
||||
|
||||
private async Task DisconnectInternalAsync(Task sender, Exception exception)
|
||||
{
|
||||
InitiateDisconnect();
|
||||
|
||||
var clientWasConnected = IsConnected;
|
||||
|
||||
InitiateDisconnect();
|
||||
|
||||
IsConnected = false;
|
||||
|
||||
try
|
||||
{
|
||||
await WaitForTaskAsync(_packetReceiverTask, sender).ConfigureAwait(false);
|
||||
await WaitForTaskAsync(_keepAliveMessageSenderTask, sender).ConfigureAwait(false);
|
||||
|
||||
if (_adapter != null)
|
||||
{
|
||||
await _adapter.DisconnectAsync(_options.CommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
|
||||
await _adapter.DisconnectAsync(Options.CommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await WaitForTaskAsync(_packetReceiverTask, sender).ConfigureAwait(false);
|
||||
await WaitForTaskAsync(_keepAliveMessageSenderTask, sender).ConfigureAwait(false);
|
||||
|
||||
_logger.Verbose("Disconnected from adapter.");
|
||||
}
|
||||
catch (Exception adapterException)
|
||||
@@ -238,10 +251,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
finally
|
||||
{
|
||||
_adapter?.Dispose();
|
||||
_adapter = null;
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
Dispose();
|
||||
_cleanDisconnectInitiated = false;
|
||||
|
||||
_logger.Info("Disconnected.");
|
||||
@@ -255,16 +265,16 @@ namespace MQTTnet.Client
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_cancellationTokenSource == null || _cancellationTokenSource.IsCancellationRequested)
|
||||
if (_cancellationTokenSource?.IsCancellationRequested == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_cancellationTokenSource.Cancel(false);
|
||||
_cancellationTokenSource?.Cancel(false);
|
||||
}
|
||||
catch (Exception adapterException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Warning(adapterException, "Error while initiating disconnect.");
|
||||
_logger.Warning(exception, "Error while initiating disconnect.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,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;
|
||||
}
|
||||
@@ -311,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;
|
||||
@@ -352,7 +362,10 @@ namespace MQTTnet.Client
|
||||
_logger.Error(exception, "Unhandled exception while sending/receiving keep alive packets.");
|
||||
}
|
||||
|
||||
await DisconnectInternalAsync(_keepAliveMessageSenderTask, exception).ConfigureAwait(false);
|
||||
if (!DisconnectIsPending())
|
||||
{
|
||||
await DisconnectInternalAsync(_keepAliveMessageSenderTask, exception).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -362,14 +375,16 @@ namespace MQTTnet.Client
|
||||
|
||||
private async Task ReceivePacketsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.Verbose("Start receiving packets.");
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Verbose("Start receiving packets.");
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false);
|
||||
if (packet != null)
|
||||
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (packet != null && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
await ProcessReceivedPacketAsync(packet, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
@@ -395,7 +410,11 @@ namespace MQTTnet.Client
|
||||
}
|
||||
|
||||
_packetDispatcher.Dispatch(exception);
|
||||
await DisconnectInternalAsync(_packetReceiverTask, exception).ConfigureAwait(false);
|
||||
|
||||
if (!DisconnectIsPending())
|
||||
{
|
||||
await DisconnectInternalAsync(_packetReceiverTask, exception).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -478,16 +497,20 @@ namespace MQTTnet.Client
|
||||
|
||||
private void StartReceivingPackets(CancellationToken cancellationToken)
|
||||
{
|
||||
_packetReceiverTask = Task.Run(
|
||||
_packetReceiverTask = Task.Factory.StartNew(
|
||||
() => ReceivePacketsAsync(cancellationToken),
|
||||
cancellationToken);
|
||||
cancellationToken,
|
||||
TaskCreationOptions.LongRunning,
|
||||
TaskScheduler.Default).Unwrap();
|
||||
}
|
||||
|
||||
private void StartSendingKeepAliveMessages(CancellationToken cancellationToken)
|
||||
{
|
||||
_keepAliveMessageSenderTask = Task.Run(
|
||||
_keepAliveMessageSenderTask = Task.Factory.StartNew(
|
||||
() => SendKeepAliveMessagesAsync(cancellationToken),
|
||||
cancellationToken);
|
||||
cancellationToken,
|
||||
TaskCreationOptions.LongRunning,
|
||||
TaskScheduler.Default).Unwrap();
|
||||
}
|
||||
|
||||
private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket)
|
||||
@@ -495,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)
|
||||
{
|
||||
@@ -523,5 +546,10 @@ namespace MQTTnet.Client
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private bool DisconnectIsPending()
|
||||
{
|
||||
return Interlocked.CompareExchange(ref _disconnectGate, 1, 0) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@ namespace MQTTnet.Client
|
||||
public class MqttClientOptionsBuilder
|
||||
{
|
||||
private readonly MqttClientOptions _options = new MqttClientOptions();
|
||||
|
||||
private MqttClientTcpOptions _tcpOptions;
|
||||
private MqttClientWebSocketOptions _webSocketOptions;
|
||||
|
||||
private MqttClientTlsOptions _tlsOptions;
|
||||
private MqttClientOptionsBuilderTlsParameters _tlsParameters;
|
||||
private MqttClientWebSocketProxyOptions _proxyOptions;
|
||||
|
||||
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
|
||||
{
|
||||
@@ -76,6 +77,21 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithProxy(string address, string username = null, string password = null, string domain = null, bool bypassOnLocal = false, string[] bypassList = null)
|
||||
{
|
||||
_proxyOptions = new MqttClientWebSocketProxyOptions
|
||||
{
|
||||
Address = address,
|
||||
Username = username,
|
||||
Password = password,
|
||||
Domain = domain,
|
||||
BypassOnLocal = bypassOnLocal,
|
||||
BypassList = bypassList
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWebSocketServer(string uri)
|
||||
{
|
||||
_webSocketOptions = new MqttClientWebSocketOptions
|
||||
@@ -86,13 +102,25 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTls(MqttClientOptionsBuilderTlsParameters parameters)
|
||||
{
|
||||
_tlsParameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTls()
|
||||
{
|
||||
return WithTls(new MqttClientOptionsBuilderTlsParameters { UseTls = true });
|
||||
}
|
||||
|
||||
[Obsolete("Use method _WithTls_ which accepts the _MqttClientOptionsBuilderTlsParameters_.")]
|
||||
public MqttClientOptionsBuilder WithTls(
|
||||
bool allowUntrustedCertificates = false,
|
||||
bool ignoreCertificateChainErrors = false,
|
||||
bool ignoreCertificateRevocationErrors = false,
|
||||
params byte[][] certificates)
|
||||
{
|
||||
_tlsOptions = new MqttClientTlsOptions
|
||||
_tlsParameters = new MqttClientOptionsBuilderTlsParameters
|
||||
{
|
||||
UseTls = true,
|
||||
AllowUntrustedCertificates = allowUntrustedCertificates,
|
||||
@@ -104,33 +132,47 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTls()
|
||||
{
|
||||
_tlsOptions = new MqttClientTlsOptions
|
||||
{
|
||||
UseTls = true
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IMqttClientOptions Build()
|
||||
{
|
||||
if (_tlsOptions != null)
|
||||
if (_tcpOptions == null && _webSocketOptions == null)
|
||||
{
|
||||
if (_tcpOptions == null && _webSocketOptions == null)
|
||||
throw new InvalidOperationException("A channel must be set.");
|
||||
}
|
||||
|
||||
if (_tlsParameters != null)
|
||||
{
|
||||
if (_tlsParameters?.UseTls == true)
|
||||
{
|
||||
throw new InvalidOperationException("A channel (TCP or WebSocket) must be set if TLS is configured.");
|
||||
var tlsOptions = new MqttClientTlsOptions
|
||||
{
|
||||
UseTls = true,
|
||||
SslProtocol = _tlsParameters.SslProtocol,
|
||||
AllowUntrustedCertificates = _tlsParameters.AllowUntrustedCertificates,
|
||||
Certificates = _tlsParameters.Certificates?.Select(c => c.ToArray()).ToList(),
|
||||
CertificateValidationCallback = _tlsParameters.CertificateValidationCallback,
|
||||
IgnoreCertificateChainErrors = _tlsParameters.IgnoreCertificateChainErrors,
|
||||
IgnoreCertificateRevocationErrors = _tlsParameters.IgnoreCertificateRevocationErrors
|
||||
};
|
||||
|
||||
if (_tcpOptions != null)
|
||||
{
|
||||
_tcpOptions.TlsOptions = tlsOptions;
|
||||
}
|
||||
else if (_webSocketOptions != null)
|
||||
{
|
||||
_webSocketOptions.TlsOptions = tlsOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_proxyOptions != null)
|
||||
{
|
||||
if (_webSocketOptions == null)
|
||||
{
|
||||
throw new InvalidOperationException("Proxies are only supported for WebSocket connections.");
|
||||
}
|
||||
|
||||
if (_tcpOptions != null)
|
||||
{
|
||||
_tcpOptions.TlsOptions = _tlsOptions;
|
||||
}
|
||||
else if (_webSocketOptions != null)
|
||||
{
|
||||
_webSocketOptions.TlsOptions = _tlsOptions;
|
||||
}
|
||||
_webSocketOptions.ProxyOptions = _proxyOptions;
|
||||
}
|
||||
|
||||
_options.ChannelOptions = (IMqttClientChannelOptions)_tcpOptions ?? _webSocketOptions;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientOptionsBuilderTlsParameters
|
||||
{
|
||||
public bool UseTls { get; set; }
|
||||
|
||||
public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
|
||||
|
||||
public IEnumerable<IEnumerable<byte>> Certificates { get; set; }
|
||||
|
||||
public bool AllowUntrustedCertificates { get; set; }
|
||||
|
||||
public bool IgnoreCertificateChainErrors { get; set; }
|
||||
|
||||
public bool IgnoreCertificateRevocationErrors { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Authentication;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
@@ -17,6 +18,8 @@ namespace MQTTnet.Client
|
||||
|
||||
public List<byte[]> Certificates { get; set; }
|
||||
|
||||
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
|
||||
|
||||
public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace MQTTnet.Client
|
||||
|
||||
public CookieContainer CookieContainer { get; set; }
|
||||
|
||||
public MqttClientWebSocketProxyOptions ProxyOptions { get; set; }
|
||||
|
||||
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
||||
|
||||
public override string ToString()
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientWebSocketProxyOptions
|
||||
{
|
||||
public string Address { get; set; }
|
||||
|
||||
public string Username { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public string Domain { get; set; }
|
||||
|
||||
public bool BypassOnLocal { get; set; }
|
||||
|
||||
public string[] BypassList { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -154,7 +154,11 @@ namespace MQTTnet.Implementations
|
||||
|
||||
private void CreateStreams()
|
||||
{
|
||||
_readStream = _socket.InputStream.AsStreamForRead(_bufferSize);
|
||||
// Attention! Do not set the buffer for the read method. This will
|
||||
// limit the internal buffer and the read operation will hang forever
|
||||
// if more data than the buffer size was received.
|
||||
_readStream = _socket.InputStream.AsStreamForRead();
|
||||
|
||||
_writeStream = _socket.OutputStream.AsStreamForWrite(_bufferSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,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;
|
||||
|
||||
@@ -63,7 +62,7 @@ namespace MQTTnet.Implementations
|
||||
if (_options.TlsOptions.UseTls)
|
||||
{
|
||||
sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback);
|
||||
await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
||||
await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), _options.TlsOptions.SslProtocol, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
CreateStream(sslStream);
|
||||
@@ -87,8 +86,15 @@ namespace MQTTnet.Implementations
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
TryDispose(_stream, () => _stream = null);
|
||||
TryDispose(_socket, () => _socket = null);
|
||||
Cleanup(ref _stream, s => s.Dispose());
|
||||
Cleanup(ref _socket, s =>
|
||||
{
|
||||
if (s.Connected)
|
||||
{
|
||||
s.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
s.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
private bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
@@ -96,7 +102,7 @@ namespace MQTTnet.Implementations
|
||||
// Try the instance callback.
|
||||
if (_options.TlsOptions.CertificateValidationCallback != null)
|
||||
{
|
||||
return _options.TlsOptions.CertificateValidationCallback(x509Certificate, chain, sslPolicyErrors,_clientOptions);
|
||||
return _options.TlsOptions.CertificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _clientOptions);
|
||||
}
|
||||
|
||||
// Try static callback.
|
||||
@@ -110,7 +116,7 @@ namespace MQTTnet.Implementations
|
||||
return true;
|
||||
}
|
||||
|
||||
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.RevocationStatusUnknown))
|
||||
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.OfflineRevocation))
|
||||
{
|
||||
if (!_options.TlsOptions.IgnoreCertificateRevocationErrors)
|
||||
{
|
||||
@@ -157,11 +163,16 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryDispose(IDisposable disposable, Action afterDispose)
|
||||
private static void Cleanup<T>(ref T item, Action<T> handler) where T : class
|
||||
{
|
||||
var temp = item;
|
||||
item = null;
|
||||
try
|
||||
{
|
||||
disposable?.Dispose();
|
||||
if (temp != null)
|
||||
{
|
||||
handler(temp);
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
@@ -169,10 +180,6 @@ namespace MQTTnet.Implementations
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
afterDispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#if !WINDOWS_UWP
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
@@ -79,27 +80,33 @@ namespace MQTTnet.Implementations
|
||||
|
||||
private void RegisterListeners(MqttServerTcpEndpointBaseOptions options, X509Certificate2 tlsCertificate)
|
||||
{
|
||||
var listenerV4 = new MqttTcpServerListener(
|
||||
AddressFamily.InterNetwork,
|
||||
options,
|
||||
tlsCertificate,
|
||||
_cancellationTokenSource.Token,
|
||||
_logger);
|
||||
if (!options.BoundInterNetworkAddress.Equals(IPAddress.None))
|
||||
{
|
||||
var listenerV4 = new MqttTcpServerListener(
|
||||
AddressFamily.InterNetwork,
|
||||
options,
|
||||
tlsCertificate,
|
||||
_cancellationTokenSource.Token,
|
||||
_logger);
|
||||
|
||||
listenerV4.ClientAccepted += OnClientAccepted;
|
||||
listenerV4.Start();
|
||||
_listeners.Add(listenerV4);
|
||||
listenerV4.ClientAccepted += OnClientAccepted;
|
||||
listenerV4.Start();
|
||||
_listeners.Add(listenerV4);
|
||||
}
|
||||
|
||||
var listenerV6 = new MqttTcpServerListener(
|
||||
AddressFamily.InterNetworkV6,
|
||||
options,
|
||||
tlsCertificate,
|
||||
_cancellationTokenSource.Token,
|
||||
_logger);
|
||||
if (!options.BoundInterNetworkV6Address.Equals(IPAddress.None))
|
||||
{
|
||||
var listenerV6 = new MqttTcpServerListener(
|
||||
AddressFamily.InterNetworkV6,
|
||||
options,
|
||||
tlsCertificate,
|
||||
_cancellationTokenSource.Token,
|
||||
_logger);
|
||||
|
||||
listenerV6.ClientAccepted += OnClientAccepted;
|
||||
listenerV6.Start();
|
||||
_listeners.Add(listenerV6);
|
||||
listenerV6.ClientAccepted += OnClientAccepted;
|
||||
listenerV6.Start();
|
||||
_listeners.Add(listenerV6);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClientAccepted(object sender, MqttServerAdapterClientAcceptedEventArgs e)
|
||||
|
||||
@@ -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,11 +80,13 @@ namespace MQTTnet.Implementations
|
||||
if (_tlsCertificate != null)
|
||||
{
|
||||
sslStream = new SslStream(new NetworkStream(clientSocket), false);
|
||||
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false);
|
||||
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, _tlsOptions.SslProtocol, false).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var protocol = _addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6";
|
||||
_logger.Verbose($"Client '{clientSocket.RemoteEndPoint}' accepted by TCP listener '{_socket.LocalEndPoint}, {protocol}'.");
|
||||
_logger.Verbose("Client '{0}' accepted by TCP listener '{1}, {2}'.",
|
||||
clientSocket.RemoteEndPoint,
|
||||
_socket.LocalEndPoint,
|
||||
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
|
||||
|
||||
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer(), _logger);
|
||||
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.WebSockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
@@ -14,7 +15,7 @@ namespace MQTTnet.Implementations
|
||||
private readonly MqttClientWebSocketOptions _options;
|
||||
|
||||
private WebSocket _webSocket;
|
||||
|
||||
|
||||
public MqttWebSocketChannel(MqttClientWebSocketOptions options)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
@@ -45,6 +46,11 @@ namespace MQTTnet.Implementations
|
||||
|
||||
var clientWebSocket = new ClientWebSocket();
|
||||
|
||||
if (_options.ProxyOptions != null)
|
||||
{
|
||||
clientWebSocket.Options.Proxy = CreateProxy();
|
||||
}
|
||||
|
||||
if (_options.RequestHeaders != null)
|
||||
{
|
||||
foreach (var requestHeader in _options.RequestHeaders)
|
||||
@@ -132,5 +138,31 @@ namespace MQTTnet.Implementations
|
||||
_webSocket = null;
|
||||
}
|
||||
}
|
||||
|
||||
private IWebProxy CreateProxy()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_options.ProxyOptions?.Address))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#if WINDOWS_UWP
|
||||
throw new NotSupportedException("Proxies are not supported in UWP.");
|
||||
#elif NETSTANDARD1_3
|
||||
throw new NotSupportedException("Proxies are not supported in netstandard 1.3.");
|
||||
#else
|
||||
var proxyUri = new Uri(_options.ProxyOptions.Address);
|
||||
|
||||
if (!string.IsNullOrEmpty(_options.ProxyOptions.Username) && !string.IsNullOrEmpty(_options.ProxyOptions.Password))
|
||||
{
|
||||
var credentials =
|
||||
new NetworkCredential(_options.ProxyOptions.Username, _options.ProxyOptions.Password, _options.ProxyOptions.Domain);
|
||||
|
||||
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList, credentials);
|
||||
}
|
||||
|
||||
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,5 +8,13 @@ namespace MQTTnet.Internal
|
||||
{
|
||||
throw new MqttCommunicationClosedGracefullyException();
|
||||
}
|
||||
|
||||
public static void ThrowIfGracefulSocketClose(int readBytesCount)
|
||||
{
|
||||
if (readBytesCount <= 0)
|
||||
{
|
||||
throw new MqttCommunicationClosedGracefullyException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,7 @@ namespace MQTTnet.Internal
|
||||
_stream = stream;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public string Endpoint { get; }
|
||||
public string Endpoint { get; } = "<Test channel>";
|
||||
|
||||
public Task ConnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -39,5 +35,9 @@ namespace MQTTnet.Internal
|
||||
{
|
||||
return _stream.WriteAsync(buffer, offset, count, cancellationToken);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">netstandard1.3;netstandard2.0;net452;net461;uap10.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.3;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<AssemblyName>MQTTnet</AssemblyName>
|
||||
<RootNamespace>MQTTnet</RootNamespace>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
@@ -20,7 +21,7 @@
|
||||
<NugetTargetMoniker>UAP,Version=v10.0</NugetTargetMoniker>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.17134.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
||||
<TargetPlatformMinVersion>10.0.10586.212</TargetPlatformMinVersion>
|
||||
<TargetFrameworkIdentifier>.NETCore</TargetFrameworkIdentifier>
|
||||
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
|
||||
<DefineConstants>$(DefineConstants);WINDOWS_UWP</DefineConstants>
|
||||
@@ -53,7 +54,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='uap10.0'">
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.1.4" />
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.1.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net452'">
|
||||
|
||||
@@ -22,12 +22,19 @@ namespace MQTTnet
|
||||
return new MqttClient(new MqttClientAdapterFactory(), logger);
|
||||
}
|
||||
|
||||
public IMqttClient CreateMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory mqttClientAdapterFactory)
|
||||
public IMqttClient CreateMqttClient(IMqttClientAdapterFactory adapterFactory)
|
||||
{
|
||||
if (adapterFactory == null) throw new ArgumentNullException(nameof(adapterFactory));
|
||||
|
||||
return new MqttClient(adapterFactory, new MqttNetLogger());
|
||||
}
|
||||
|
||||
public IMqttClient CreateMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory adapterFactory)
|
||||
{
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
if (mqttClientAdapterFactory == null) throw new ArgumentNullException(nameof(mqttClientAdapterFactory));
|
||||
if (adapterFactory == null) throw new ArgumentNullException(nameof(adapterFactory));
|
||||
|
||||
return new MqttClient(mqttClientAdapterFactory, logger);
|
||||
return new MqttClient(adapterFactory, logger);
|
||||
}
|
||||
|
||||
public IMqttServer CreateMqttServer()
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#if DEBUG
|
||||
[assembly:InternalsVisibleTo("MQTTnet.Core.Tests")]
|
||||
#endif
|
||||
@@ -6,17 +6,20 @@ namespace MQTTnet.Serializer
|
||||
public class MqttPacketBodyReader
|
||||
{
|
||||
private readonly byte[] _buffer;
|
||||
private int _offset;
|
||||
private readonly int _length;
|
||||
|
||||
public MqttPacketBodyReader(byte[] buffer, int offset)
|
||||
private int _offset;
|
||||
|
||||
public MqttPacketBodyReader(byte[] buffer, int offset, int length)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_offset = offset;
|
||||
_length = length;
|
||||
}
|
||||
|
||||
public int Length => _buffer.Length - _offset;
|
||||
public int Length => _length - _offset;
|
||||
|
||||
public bool EndOfStream => _offset == _buffer.Length;
|
||||
public bool EndOfStream => _offset == _length;
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
@@ -26,7 +29,7 @@ namespace MQTTnet.Serializer
|
||||
|
||||
public ArraySegment<byte> ReadRemainingData()
|
||||
{
|
||||
return new ArraySegment<byte>(_buffer, _offset, _buffer.Length - _offset);
|
||||
return new ArraySegment<byte>(_buffer, _offset, _length - _offset);
|
||||
}
|
||||
|
||||
public ushort ReadUInt16()
|
||||
@@ -53,9 +56,9 @@ namespace MQTTnet.Serializer
|
||||
|
||||
private void ValidateReceiveBuffer(ushort length)
|
||||
{
|
||||
if (_buffer.Length < _offset + length)
|
||||
if (_length < _offset + length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(_buffer), $"expected at least {_offset + length} bytes but there are only {_buffer.Length} bytes");
|
||||
throw new ArgumentOutOfRangeException(nameof(_buffer), $"expected at least {_offset + length} bytes but there are only {_length} bytes");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Exceptions;
|
||||
@@ -21,11 +20,9 @@ namespace MQTTnet.Serializer
|
||||
while (totalBytesRead < buffer.Length)
|
||||
{
|
||||
var bytesRead = await channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
|
||||
if (bytesRead <= 0)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ExceptionHelper.ThrowGracefulSocketClose();
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ExceptionHelper.ThrowIfGracefulSocketClose(bytesRead);
|
||||
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
@@ -35,34 +32,42 @@ namespace MQTTnet.Serializer
|
||||
{
|
||||
return new MqttFixedHeader(buffer[0], 0);
|
||||
}
|
||||
|
||||
|
||||
#if WINDOWS_UWP
|
||||
// UWP will have a dead lock when calling this not async.
|
||||
var bodyLength = await ReadBodyLengthAsync(channel, buffer[1], singleByteBuffer, cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
// Here the async/await pattern is not used becuase the overhead of context switches
|
||||
// is too big for reading 1 byte in a row. We expect that the remaining data was sent
|
||||
// directly after the initial bytes. If the client disconnects just in this moment we
|
||||
// will get an exception anyway.
|
||||
var bodyLength = ReadBodyLength(channel, buffer[1], singleByteBuffer, cancellationToken);
|
||||
#endif
|
||||
|
||||
return new MqttFixedHeader(buffer[0], bodyLength);
|
||||
}
|
||||
|
||||
#if !WINDOWS_UWP
|
||||
private static int ReadBodyLength(IMqttChannel channel, byte initialEncodedByte, byte[] singleByteBuffer, CancellationToken cancellationToken)
|
||||
{
|
||||
// Alorithm taken from https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html.
|
||||
var offset = 0;
|
||||
var multiplier = 128;
|
||||
var value = initialEncodedByte & 127;
|
||||
int encodedByte = initialEncodedByte;
|
||||
|
||||
while ((encodedByte & 128) != 0)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Here the async/await pattern is not used becuase the overhead of context switches
|
||||
// is too big for reading 1 byte in a row. We expect that the remaining data was sent
|
||||
// directly after the initial bytes. If the client disconnects just in this moment we
|
||||
// will get an exception anyway.
|
||||
encodedByte = ReadByte(channel, singleByteBuffer, cancellationToken);
|
||||
|
||||
value += (byte)(encodedByte & 127) * multiplier;
|
||||
if (multiplier > 128 * 128 * 128)
|
||||
offset++;
|
||||
if (offset > 3)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Remaining length is invalid.");
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
encodedByte = ReadByte(channel, singleByteBuffer, cancellationToken);
|
||||
|
||||
value += (byte)(encodedByte & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
}
|
||||
|
||||
@@ -71,14 +76,60 @@ namespace MQTTnet.Serializer
|
||||
|
||||
private static byte ReadByte(IMqttChannel channel, byte[] singleByteBuffer, CancellationToken cancellationToken)
|
||||
{
|
||||
var readCount = channel.ReadAsync(singleByteBuffer, 0, 1, cancellationToken).GetAwaiter().GetResult();
|
||||
var readCount = channel.ReadAsync(singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (readCount <= 0)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ExceptionHelper.ThrowGracefulSocketClose();
|
||||
}
|
||||
|
||||
return singleByteBuffer[0];
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
private static async Task<int> ReadBodyLengthAsync(IMqttChannel channel, byte initialEncodedByte, byte[] singleByteBuffer, CancellationToken cancellationToken)
|
||||
{
|
||||
var offset = 0;
|
||||
var multiplier = 128;
|
||||
var value = initialEncodedByte & 127;
|
||||
int encodedByte = initialEncodedByte;
|
||||
|
||||
while ((encodedByte & 128) != 0)
|
||||
{
|
||||
offset++;
|
||||
if (offset > 3)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Remaining length is invalid.");
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
encodedByte = await ReadByteAsync(channel, singleByteBuffer, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
value += (byte)(encodedByte & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static async Task<byte> ReadByteAsync(IMqttChannel channel, byte[] singleByteBuffer, CancellationToken cancellationToken)
|
||||
{
|
||||
var readCount = await channel.ReadAsync(singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (readCount <= 0)
|
||||
{
|
||||
ExceptionHelper.ThrowGracefulSocketClose();
|
||||
}
|
||||
|
||||
return singleByteBuffer[0];
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace MQTTnet.Serializer
|
||||
|
||||
public static ArraySegment<byte> EncodeRemainingLength(int length)
|
||||
{
|
||||
// write the encoded remaining length right aligned on the 4 byte buffer
|
||||
if (length <= 0)
|
||||
{
|
||||
return new ArraySegment<byte>(new byte[1], 0, 1);
|
||||
@@ -38,7 +37,6 @@ namespace MQTTnet.Serializer
|
||||
var buffer = new byte[4];
|
||||
var bufferOffset = 0;
|
||||
|
||||
// Algorithm taken from http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html.
|
||||
var x = length;
|
||||
do
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace MQTTnet.Server
|
||||
void EnqueueApplicationMessage(MqttClientSession senderClientSession, MqttPublishPacket publishPacket);
|
||||
void ClearPendingApplicationMessages();
|
||||
|
||||
Task<bool> RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter);
|
||||
Task RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter);
|
||||
void Stop(MqttClientDisconnectType disconnectType);
|
||||
|
||||
Task SubscribeAsync(IList<TopicFilter> topicFilters);
|
||||
|
||||
@@ -16,8 +16,14 @@ 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);
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace MQTTnet.Server
|
||||
{
|
||||
IMqttServer CreateMqttServer();
|
||||
|
||||
IMqttServer CreateMqttServer(IMqttNetLogger logger);
|
||||
|
||||
IMqttServer CreateMqttServer(IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger);
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
@@ -14,9 +14,9 @@ namespace MQTTnet.Server
|
||||
|
||||
private readonly IMqttClientSession _clientSession;
|
||||
private readonly IMqttNetChildLogger _logger;
|
||||
|
||||
|
||||
private bool _isPaused;
|
||||
|
||||
|
||||
public MqttClientKeepAliveMonitor(IMqttClientSession clientSession, IMqttNetChildLogger logger)
|
||||
{
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
@@ -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();
|
||||
@@ -70,15 +76,21 @@ namespace MQTTnet.Server
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
// Values described here: [MQTT-3.1.2-24].
|
||||
if (!_isPaused && _lastPacketReceivedTracker.Elapsed.TotalSeconds > keepAlivePeriod * 1.5D)
|
||||
// If the client sends 5 sec. the server will allow up to 7.5 seconds.
|
||||
// If the client sends 1 sec. the server will allow up to 1.5 seconds.
|
||||
if (!_isPaused && _lastPacketReceivedTracker.Elapsed.TotalSeconds >= keepAlivePeriod * 1.5D)
|
||||
{
|
||||
_logger.Warning(null, "Client '{0}': Did not receive any packet or keep alive signal.", _clientSession.ClientId);
|
||||
_clientSession.Stop(MqttClientDisconnectType.NotClean);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.Delay(keepAlivePeriod, cancellationToken).ConfigureAwait(false);
|
||||
// The server checks the keep alive timeout every 50 % of the overall keep alive timeout
|
||||
// because the server allows 1.5 times the keep alive value. This means that a value of 5 allows
|
||||
// up to 7.5 seconds. With an interval of 2.5 (5 / 2) the 7.5 is also affected. Waiting the whole
|
||||
// keep alive time will hit at 10 instead of 7.5 (but only one time instead of two times).
|
||||
await Task.Delay(TimeSpan.FromSeconds(keepAlivePeriod * 0.5D), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -90,7 +102,7 @@ namespace MQTTnet.Server
|
||||
}
|
||||
finally
|
||||
{
|
||||
_logger.Verbose("Client {0}: Stopped checking keep alive timeout.", _clientSession.ClientId);
|
||||
_logger.Verbose("Client '{0}': Stopped checking keep alive timeout.", _clientSession.ClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,27 +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);
|
||||
}
|
||||
|
||||
@@ -57,107 +65,23 @@ 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;
|
||||
}
|
||||
|
||||
public async Task<bool> RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter)
|
||||
public Task RunAsync(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();
|
||||
_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);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_adapter != null)
|
||||
{
|
||||
_adapter.ReadingPacketStarted -= OnAdapterReadingPacketStarted;
|
||||
_adapter.ReadingPacketCompleted -= OnAdapterReadingPacketCompleted;
|
||||
}
|
||||
|
||||
_adapter = null;
|
||||
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
}
|
||||
|
||||
return _wasCleanDisconnect;
|
||||
_workerTask = RunInternalAsync(connectPacket, adapter);
|
||||
return _workerTask;
|
||||
}
|
||||
|
||||
public void Stop(MqttClientDisconnectType type)
|
||||
{
|
||||
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;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_logger.Info("Client '{0}': Session stopped.", ClientId);
|
||||
}
|
||||
Stop(type, false);
|
||||
}
|
||||
|
||||
public void EnqueueApplicationMessage(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
|
||||
@@ -175,7 +99,7 @@ namespace MQTTnet.Server
|
||||
Topic = publishPacket.Topic,
|
||||
Payload = publishPacket.Payload,
|
||||
QualityOfServiceLevel = checkSubscriptionsResult.QualityOfServiceLevel,
|
||||
Retain = false,
|
||||
Retain = publishPacket.Retain,
|
||||
Dup = false
|
||||
};
|
||||
|
||||
@@ -240,7 +164,166 @@ namespace MQTTnet.Server
|
||||
{
|
||||
_pendingPacketsQueue?.Dispose();
|
||||
|
||||
_cancellationTokenSource?.Cancel ();
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_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)
|
||||
@@ -298,18 +381,18 @@ namespace MQTTnet.Server
|
||||
|
||||
if (packet is MqttDisconnectPacket)
|
||||
{
|
||||
Stop(MqttClientDisconnectType.Clean);
|
||||
Stop(MqttClientDisconnectType.Clean, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet is MqttConnectPacket)
|
||||
{
|
||||
Stop(MqttClientDisconnectType.NotClean);
|
||||
Stop(MqttClientDisconnectType.NotClean, true);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Warning(null, "Client '{0}': Received not supported packet ({1}). Closing connection.", ClientId, packet);
|
||||
Stop(MqttClientDisconnectType.NotClean);
|
||||
Stop(MqttClientDisconnectType.NotClean, true);
|
||||
}
|
||||
|
||||
private void EnqueueSubscribedRetainedMessages(ICollection<TopicFilter> topicFilters)
|
||||
@@ -317,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +416,7 @@ namespace MQTTnet.Server
|
||||
|
||||
if (subscribeResult.CloseConnection)
|
||||
{
|
||||
Stop(MqttClientDisconnectType.NotClean);
|
||||
Stop(MqttClientDisconnectType.NotClean, true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -405,12 +493,12 @@ namespace MQTTnet.Server
|
||||
|
||||
private void OnAdapterReadingPacketCompleted(object sender, EventArgs e)
|
||||
{
|
||||
_keepAliveMonitor?.Pause();
|
||||
_keepAliveMonitor?.Resume();
|
||||
}
|
||||
|
||||
private void OnAdapterReadingPacketStarted(object sender, EventArgs e)
|
||||
{
|
||||
_keepAliveMonitor?.Resume();
|
||||
_keepAliveMonitor?.Pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,28 +23,32 @@ 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(() => ProcessQueuedApplicationMessages(_cancellationToken), _cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
Task.Factory.StartNew(() => TryProcessQueuedApplicationMessages(_cancellationToken), _cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
@@ -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)
|
||||
@@ -125,6 +129,7 @@ namespace MQTTnet.Server
|
||||
{
|
||||
_sessions.Remove(clientId);
|
||||
}
|
||||
|
||||
_logger.Verbose("Session for client '{0}' deleted.", clientId);
|
||||
}
|
||||
|
||||
@@ -133,54 +138,74 @@ namespace MQTTnet.Server
|
||||
_messageQueue?.Dispose();
|
||||
}
|
||||
|
||||
private void ProcessQueuedApplicationMessages(CancellationToken cancellationToken)
|
||||
private void TryProcessQueuedApplicationMessages(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enqueuedApplicationMessage = _messageQueue.Take(cancellationToken);
|
||||
var sender = enqueuedApplicationMessage.Sender;
|
||||
var applicationMessage = enqueuedApplicationMessage.PublishPacket.ToApplicationMessage();
|
||||
|
||||
var interceptorContext = InterceptApplicationMessage(sender, applicationMessage);
|
||||
if (interceptorContext != null)
|
||||
{
|
||||
if (interceptorContext.CloseConnection)
|
||||
{
|
||||
enqueuedApplicationMessage.Sender.Stop(MqttClientDisconnectType.NotClean);
|
||||
}
|
||||
|
||||
if (interceptorContext.ApplicationMessage == null || !interceptorContext.AcceptPublish)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
applicationMessage = interceptorContext.ApplicationMessage;
|
||||
}
|
||||
|
||||
Server.OnApplicationMessageReceived(sender?.ClientId, applicationMessage);
|
||||
|
||||
if (applicationMessage.Retain)
|
||||
{
|
||||
_retainedMessagesManager.HandleMessageAsync(sender?.ClientId, applicationMessage).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
foreach (var clientSession in GetSessions())
|
||||
{
|
||||
clientSession.EnqueueApplicationMessage(enqueuedApplicationMessage.Sender, applicationMessage.ToPublishPacket());
|
||||
}
|
||||
TryProcessNextQueuedApplicationMessage(cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Unhandled exception while processing queued application message.");
|
||||
_logger.Error(exception, "Unhandled exception while processing queued application messages.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TryProcessNextQueuedApplicationMessage(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enqueuedApplicationMessage = _messageQueue.Take(cancellationToken);
|
||||
var sender = enqueuedApplicationMessage.Sender;
|
||||
var applicationMessage = enqueuedApplicationMessage.PublishPacket.ToApplicationMessage();
|
||||
|
||||
var interceptorContext = InterceptApplicationMessage(sender, applicationMessage);
|
||||
if (interceptorContext != null)
|
||||
{
|
||||
if (interceptorContext.CloseConnection)
|
||||
{
|
||||
enqueuedApplicationMessage.Sender.Stop(MqttClientDisconnectType.NotClean);
|
||||
}
|
||||
|
||||
if (interceptorContext.ApplicationMessage == null || !interceptorContext.AcceptPublish)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
applicationMessage = interceptorContext.ApplicationMessage;
|
||||
}
|
||||
|
||||
_eventDispatcher.OnApplicationMessageReceived(sender?.ClientId, applicationMessage);
|
||||
|
||||
if (applicationMessage.Retain)
|
||||
{
|
||||
_retainedMessagesManager.HandleMessageAsync(sender?.ClientId, applicationMessage).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
foreach (var clientSession in GetSessions())
|
||||
{
|
||||
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)
|
||||
{
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Unhandled exception while processing next queued application message.");
|
||||
}
|
||||
}
|
||||
|
||||
private List<MqttClientSession> GetSessions()
|
||||
{
|
||||
lock (_sessions)
|
||||
@@ -189,11 +214,10 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunSession(IMqttChannelAdapter clientAdapter, CancellationToken cancellationToken)
|
||||
private async Task RunSessionAsync(IMqttChannelAdapter clientAdapter, CancellationToken cancellationToken)
|
||||
{
|
||||
var clientId = string.Empty;
|
||||
var wasCleanDisconnect = false;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var firstPacket = await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false);
|
||||
@@ -212,7 +236,7 @@ namespace MQTTnet.Server
|
||||
// Switch to the required protocol version before sending any response.
|
||||
clientAdapter.PacketSerializer.ProtocolVersion = connectPacket.ProtocolVersion;
|
||||
|
||||
var connectReturnCode = ValidateConnection(connectPacket);
|
||||
var connectReturnCode = ValidateConnection(connectPacket, clientAdapter);
|
||||
if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
||||
{
|
||||
await clientAdapter.SendPacketAsync(
|
||||
@@ -226,7 +250,6 @@ namespace MQTTnet.Server
|
||||
}
|
||||
|
||||
var result = PrepareClientSession(connectPacket);
|
||||
var clientSession = result.Session;
|
||||
|
||||
await clientAdapter.SendPacketAsync(
|
||||
new MqttConnAckPacket
|
||||
@@ -236,9 +259,10 @@ namespace MQTTnet.Server
|
||||
},
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Server.OnClientConnected(clientId);
|
||||
_logger.Info("Client '{0}': Connected.", clientId);
|
||||
_eventDispatcher.OnClientConnected(clientId);
|
||||
|
||||
wasCleanDisconnect = await clientSession.RunAsync(connectPacket, clientAdapter).ConfigureAwait(false);
|
||||
await result.Session.RunAsync(connectPacket, clientAdapter).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -249,26 +273,14 @@ namespace MQTTnet.Server
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
await clientAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
|
||||
clientAdapter.Dispose();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, exception.Message);
|
||||
}
|
||||
|
||||
if (!_options.EnablePersistentSessions)
|
||||
{
|
||||
DeleteSession(clientId);
|
||||
}
|
||||
|
||||
Server.OnClientDisconnected(clientId, wasCleanDisconnect);
|
||||
}
|
||||
}
|
||||
|
||||
private MqttConnectReturnCode ValidateConnection(MqttConnectPacket connectPacket)
|
||||
private MqttConnectReturnCode ValidateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter clientAdapter)
|
||||
{
|
||||
if (_options.ConnectionValidator == null)
|
||||
{
|
||||
@@ -279,7 +291,8 @@ namespace MQTTnet.Server
|
||||
connectPacket.ClientId,
|
||||
connectPacket.Username,
|
||||
connectPacket.Password,
|
||||
connectPacket.WillMessage);
|
||||
connectPacket.WillMessage,
|
||||
clientAdapter.Endpoint);
|
||||
|
||||
_options.ConnectionValidator(context);
|
||||
return context.ReturnCode;
|
||||
@@ -313,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,13 @@ namespace MQTTnet.Server
|
||||
{
|
||||
public class MqttConnectionValidatorContext
|
||||
{
|
||||
public MqttConnectionValidatorContext(string clientId, string username, string password, MqttApplicationMessage willMessage)
|
||||
public MqttConnectionValidatorContext(string clientId, string username, string password, MqttApplicationMessage willMessage, string endpoint)
|
||||
{
|
||||
ClientId = clientId;
|
||||
Username = username;
|
||||
Password = password;
|
||||
WillMessage = willMessage;
|
||||
Endpoint = endpoint;
|
||||
}
|
||||
|
||||
public string ClientId { get; }
|
||||
@@ -20,6 +21,8 @@ namespace MQTTnet.Server
|
||||
|
||||
public MqttApplicationMessage WillMessage { get; }
|
||||
|
||||
public string Endpoint { get; }
|
||||
|
||||
public MqttConnectReturnCode ReturnCode { get; set; } = MqttConnectReturnCode.ConnectionAccepted;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,52 +128,27 @@ namespace MQTTnet.Server
|
||||
return retainedMessages;
|
||||
}
|
||||
|
||||
private async Task HandleMessageInternalAsync(string clientId, MqttApplicationMessage applicationMessage)
|
||||
public IList<MqttApplicationMessage> GetMessages()
|
||||
{
|
||||
var saveIsRequired = false;
|
||||
|
||||
lock (_messages)
|
||||
{
|
||||
if (applicationMessage.Payload?.Length == 0)
|
||||
{
|
||||
saveIsRequired = _messages.Remove(applicationMessage.Topic);
|
||||
_logger.Info("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.Info("Client '{0}' set retained message for topic '{1}'.", clientId, applicationMessage.Topic);
|
||||
}
|
||||
return _messages.Values.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
if (!saveIsRequired)
|
||||
public Task ClearMessagesAsync()
|
||||
{
|
||||
lock (_messages)
|
||||
{
|
||||
_logger.Verbose("Skipped saving retained messages because no changes were detected.");
|
||||
_messages.Clear();
|
||||
}
|
||||
|
||||
if (saveIsRequired && _options.Storage != null)
|
||||
if (_options.Storage != null)
|
||||
{
|
||||
List<MqttApplicationMessage> messages;
|
||||
lock (_messages)
|
||||
{
|
||||
messages = _messages.Values.ToList();
|
||||
}
|
||||
|
||||
await _options.Storage.SaveRetainedMessagesAsync(messages).ConfigureAwait(false);
|
||||
return _options.Storage.SaveRetainedMessagesAsync(new List<MqttApplicationMessage>());
|
||||
}
|
||||
|
||||
return Task.FromResult((object)null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -106,14 +123,14 @@ namespace MQTTnet.Server
|
||||
|
||||
_cancellationTokenSource.Cancel(false);
|
||||
|
||||
_clientSessionsManager.Stop();
|
||||
|
||||
foreach (var adapter in _adapters)
|
||||
{
|
||||
adapter.ClientAccepted -= OnClientAccepted;
|
||||
await adapter.StopAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_clientSessionsManager.Stop();
|
||||
|
||||
_logger.Info("Stopped.");
|
||||
Stopped?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
@@ -129,31 +146,9 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnClientConnected(string clientId)
|
||||
public Task ClearRetainedMessagesAsync()
|
||||
{
|
||||
_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));
|
||||
return _retainedMessagesManager?.ClearMessagesAsync();
|
||||
}
|
||||
|
||||
private void OnClientAccepted(object sender, MqttServerAdapterClientAcceptedEventArgs eventArgs)
|
||||
|
||||
@@ -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;
|
||||
@@ -58,7 +56,7 @@ namespace MQTTnet.Benchmarks
|
||||
|
||||
var receivedPacket = new ReceivedMqttPacket(
|
||||
header.Flags,
|
||||
new MqttPacketBodyReader(_serializedPacket.Array, _serializedPacket.Count - header.RemainingLength));
|
||||
new MqttPacketBodyReader(_serializedPacket.Array, _serializedPacket.Count - header.RemainingLength, _serializedPacket.Array.Length));
|
||||
|
||||
_serializer.Deserialize(receivedPacket);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Attributes.Exporters;
|
||||
using BenchmarkDotNet.Attributes.Jobs;
|
||||
using MQTTnet.Server;
|
||||
using System;
|
||||
|
||||
|
||||
@@ -61,25 +61,26 @@ namespace MQTTnet.Core.Tests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that inlining continuations do not have to complete execution before Set() returns.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public async Task SetReturnsBeforeInlinedContinuations()
|
||||
{
|
||||
var setReturned = new ManualResetEventSlim();
|
||||
var inlinedContinuation = _aare.WaitOneAsync()
|
||||
.ContinueWith(delegate
|
||||
{
|
||||
// Arrange to synchronously block the continuation until Set() has returned,
|
||||
// which would deadlock if Set does not return until inlined continuations complete.
|
||||
Assert.IsTrue(setReturned.Wait(500));
|
||||
});
|
||||
await Task.Delay(100);
|
||||
_aare.Set();
|
||||
setReturned.Set();
|
||||
Assert.IsTrue(inlinedContinuation.Wait(500));
|
||||
}
|
||||
// This test does not work in appveyor but on local machine it does!?
|
||||
/////// <summary>
|
||||
/////// Verifies that inlining continuations do not have to complete execution before Set() returns.
|
||||
/////// </summary>
|
||||
////[TestMethod]
|
||||
////public async Task SetReturnsBeforeInlinedContinuations()
|
||||
////{
|
||||
//// var setReturned = new ManualResetEventSlim();
|
||||
//// var inlinedContinuation = _aare.WaitOneAsync()
|
||||
//// .ContinueWith(delegate
|
||||
//// {
|
||||
//// // Arrange to synchronously block the continuation until Set() has returned,
|
||||
//// // which would deadlock if Set does not return until inlined continuations complete.
|
||||
//// Assert.IsTrue(setReturned.Wait(500));
|
||||
//// });
|
||||
//// await Task.Delay(100);
|
||||
//// _aare.Set();
|
||||
//// setReturned.Set();
|
||||
//// Assert.IsTrue(inlinedContinuation.Wait(500));
|
||||
////}
|
||||
|
||||
[TestMethod]
|
||||
public void WaitAsync_WithCancellationToken()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,32 @@
|
||||
using System.Net.Sockets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Core.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class MqttClientTests
|
||||
{
|
||||
|
||||
[TestMethod]
|
||||
public async Task ClientDisconnectException()
|
||||
public async Task Client_Disconnect_Exception()
|
||||
{
|
||||
var factory = new MqttFactory();
|
||||
var client = factory.CreateMqttClient();
|
||||
|
||||
var exceptionIsCorrect = false;
|
||||
Exception ex = null;
|
||||
client.Disconnected += (s, e) =>
|
||||
{
|
||||
exceptionIsCorrect = e.Exception is MqttCommunicationException c && c.InnerException is SocketException;
|
||||
ex = e.Exception;
|
||||
};
|
||||
|
||||
try
|
||||
@@ -29,8 +36,131 @@ namespace MQTTnet.Core.Tests
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
Assert.IsTrue(exceptionIsCorrect);
|
||||
|
||||
Assert.IsNotNull(ex);
|
||||
Assert.IsInstanceOfType(ex, typeof(MqttCommunicationException));
|
||||
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 Client_Cleanup_On_Authentification_Fails()
|
||||
{
|
||||
var channel = new TestMqttCommunicationAdapter();
|
||||
var channel2 = new TestMqttCommunicationAdapter();
|
||||
channel.Partner = channel2;
|
||||
channel2.Partner = channel;
|
||||
|
||||
Task.Run(async () => {
|
||||
var connect = await channel2.ReceivePacketAsync(TimeSpan.Zero, 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());
|
||||
|
||||
try
|
||||
{
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("any-server").Build());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Assert.IsInstanceOfType(ex, typeof(MqttConnectingFailedException));
|
||||
}
|
||||
|
||||
Assert.IsTrue(client._packetReceiverTask == null || client._packetReceiverTask.IsCompleted, "receive loop not completed");
|
||||
Assert.IsTrue(client._keepAliveMessageSenderTask == null || client._keepAliveMessageSenderTask.IsCompleted, "keepalive loop not completed");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace MQTTnet.Core.Tests
|
||||
{
|
||||
public string ClientId { get; }
|
||||
|
||||
public int StopCalledCount { get; set; }
|
||||
public int StopCalledCount { get; private set; }
|
||||
|
||||
public void FillStatus(MqttClientSessionStatus status)
|
||||
{
|
||||
@@ -77,7 +77,7 @@ namespace MQTTnet.Core.Tests
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public Task<bool> RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter)
|
||||
public Task RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
@@ -149,6 +150,53 @@ namespace MQTTnet.Core.Tests
|
||||
DeserializeAndCompare(p, "IAIABQ==", MqttProtocolVersion.V310);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_LargePacket()
|
||||
{
|
||||
var serializer = new MqttPacketSerializer { ProtocolVersion = MqttProtocolVersion.V311 };
|
||||
|
||||
const int payloadLength = 80000;
|
||||
|
||||
var payload = new byte[payloadLength];
|
||||
|
||||
var value = 0;
|
||||
for (var i = 0; i < payloadLength; i++)
|
||||
{
|
||||
if (value > 255)
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
|
||||
payload[i] = (byte)value;
|
||||
}
|
||||
|
||||
var publishPacket = new MqttPublishPacket
|
||||
{
|
||||
Topic = "abcdefghijklmnopqrstuvwxyz0123456789",
|
||||
Payload = payload
|
||||
};
|
||||
|
||||
var buffer = serializer.Serialize(publishPacket);
|
||||
var testChannel = new TestMqttChannel(new MemoryStream(buffer.Array, buffer.Offset, buffer.Count));
|
||||
|
||||
var header = MqttPacketReader.ReadFixedHeaderAsync(
|
||||
testChannel,
|
||||
new byte[2],
|
||||
new byte[1],
|
||||
CancellationToken.None).GetAwaiter().GetResult();
|
||||
|
||||
var eof = buffer.Offset + buffer.Count;
|
||||
|
||||
var receivedPacket = new ReceivedMqttPacket(
|
||||
header.Flags,
|
||||
new MqttPacketBodyReader(buffer.Array, eof - header.RemainingLength, buffer.Count + buffer.Offset));
|
||||
|
||||
var packet = (MqttPublishPacket)serializer.Deserialize(receivedPacket);
|
||||
|
||||
Assert.AreEqual(publishPacket.Topic, packet.Topic);
|
||||
Assert.IsTrue(publishPacket.Payload.SequenceEqual(packet.Payload));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SerializeV311_MqttDisconnectPacket()
|
||||
{
|
||||
@@ -463,7 +511,7 @@ namespace MQTTnet.Core.Tests
|
||||
{
|
||||
var serializer = new MqttPacketSerializer { ProtocolVersion = protocolVersion };
|
||||
var data = serializer.Serialize(packet);
|
||||
|
||||
|
||||
Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(Join(data)));
|
||||
}
|
||||
|
||||
@@ -479,10 +527,10 @@ namespace MQTTnet.Core.Tests
|
||||
var fixedHeader = new byte[2];
|
||||
var singleByteBuffer = new byte[1];
|
||||
var header = MqttPacketReader.ReadFixedHeaderAsync(channel, fixedHeader, singleByteBuffer, CancellationToken.None).GetAwaiter().GetResult();
|
||||
|
||||
|
||||
using (var bodyStream = new MemoryStream(Join(buffer1), (int)headerStream.Position, header.RemainingLength))
|
||||
{
|
||||
var deserializedPacket = serializer.Deserialize(new ReceivedMqttPacket(header.Flags, new MqttPacketBodyReader(bodyStream.ToArray(), 0)));
|
||||
var deserializedPacket = serializer.Deserialize(new ReceivedMqttPacket(header.Flags, new MqttPacketBodyReader(bodyStream.ToArray(), 0, (int)bodyStream.Length)));
|
||||
var buffer2 = serializer.Serialize(deserializedPacket);
|
||||
|
||||
Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(Join(buffer2)));
|
||||
@@ -507,7 +555,7 @@ namespace MQTTnet.Core.Tests
|
||||
|
||||
using (var bodyStream = new MemoryStream(Join(buffer1), (int)headerStream.Position, header.RemainingLength))
|
||||
{
|
||||
return (T)serializer.Deserialize(new ReceivedMqttPacket(header.Flags, new MqttPacketBodyReader(bodyStream.ToArray(), 0)));
|
||||
return (T)serializer.Deserialize(new ReceivedMqttPacket(header.Flags, new MqttPacketBodyReader(bodyStream.ToArray(), 0, (int)bodyStream.Length)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,13 @@ 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;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Core.Tests
|
||||
@@ -97,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) =>
|
||||
{
|
||||
@@ -115,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) =>
|
||||
{
|
||||
@@ -225,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,6 +270,167 @@ namespace MQTTnet.Core.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttServer_ShutdownDisconnectsClientsGracefully()
|
||||
{
|
||||
var serverAdapter = new MqttTcpServerAdapter(new MqttNetLogger().CreateChildLogger());
|
||||
var s = new MqttFactory().CreateMqttServer(new[] { serverAdapter }, new MqttNetLogger());
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("localhost")
|
||||
.Build();
|
||||
|
||||
var disconnectCalled = 0;
|
||||
|
||||
await s.StartAsync(new MqttServerOptions());
|
||||
|
||||
var c1 = new MqttFactory().CreateMqttClient();
|
||||
c1.Disconnected += (sender, args) => disconnectCalled++;
|
||||
|
||||
await c1.ConnectAsync(clientOptions);
|
||||
|
||||
await Task.Delay(100);
|
||||
|
||||
await s.StopAsync();
|
||||
|
||||
await Task.Delay(100);
|
||||
|
||||
Assert.AreEqual(1, disconnectCalled);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttServer_HandleCleanDisconnect()
|
||||
{
|
||||
var serverAdapter = new MqttTcpServerAdapter(new MqttNetLogger().CreateChildLogger());
|
||||
var s = new MqttFactory().CreateMqttServer(new[] { serverAdapter }, new MqttNetLogger());
|
||||
|
||||
var clientConnectedCalled = 0;
|
||||
var clientDisconnectedCalled = 0;
|
||||
|
||||
s.ClientConnected += (_, __) => clientConnectedCalled++;
|
||||
s.ClientDisconnected += (_, __) => clientDisconnectedCalled++;
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("localhost")
|
||||
.Build();
|
||||
|
||||
await s.StartAsync(new MqttServerOptions());
|
||||
|
||||
var c1 = new MqttFactory().CreateMqttClient();
|
||||
|
||||
await c1.ConnectAsync(clientOptions);
|
||||
|
||||
await Task.Delay(100);
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
await Task.Delay(100);
|
||||
|
||||
await s.StopAsync();
|
||||
|
||||
await Task.Delay(100);
|
||||
|
||||
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()
|
||||
{
|
||||
@@ -266,6 +463,8 @@ namespace MQTTnet.Core.Tests
|
||||
}
|
||||
|
||||
await c2.DisconnectAsync();
|
||||
|
||||
await s.StopAsync();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -304,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());
|
||||
@@ -314,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);
|
||||
@@ -324,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]
|
||||
@@ -375,6 +582,9 @@ namespace MQTTnet.Core.Tests
|
||||
var c1 = await serverAdapter.ConnectTestClient("c1");
|
||||
|
||||
await c1.PublishAndWaitForAsync(s, new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
|
||||
|
||||
await Task.Delay(250);
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
}
|
||||
finally
|
||||
@@ -396,7 +606,7 @@ namespace MQTTnet.Core.Tests
|
||||
c2.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
|
||||
await c2.SubscribeAsync(new TopicFilterBuilder().WithTopic("retained").Build());
|
||||
|
||||
await Task.Delay(500);
|
||||
await Task.Delay(250);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -481,6 +691,135 @@ namespace MQTTnet.Core.Tests
|
||||
Assert.IsTrue(bodyIsMatching);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttServer_ConnectionDenied()
|
||||
{
|
||||
var server = new MqttFactory().CreateMqttServer();
|
||||
var client = new MqttFactory().CreateMqttClient();
|
||||
|
||||
try
|
||||
{
|
||||
var options = new MqttServerOptionsBuilder().WithConnectionValidator(context =>
|
||||
{
|
||||
context.ReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized;
|
||||
}).Build();
|
||||
|
||||
await server.StartAsync(options);
|
||||
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("localhost").Build();
|
||||
|
||||
try
|
||||
{
|
||||
await client.ConnectAsync(clientOptions);
|
||||
Assert.Fail("An exception should be raised.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is MqttConnectingFailedException)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Fail("Wrong exception.");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await client.DisconnectAsync();
|
||||
await server.StopAsync();
|
||||
|
||||
client.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttServer_SameClientIdConnectDisconnectEventOrder()
|
||||
{
|
||||
var s = new MqttFactory().CreateMqttServer();
|
||||
|
||||
var events = new List<string>();
|
||||
|
||||
s.ClientConnected += (_, __) =>
|
||||
{
|
||||
lock (events)
|
||||
{
|
||||
events.Add("c");
|
||||
}
|
||||
};
|
||||
|
||||
s.ClientDisconnected += (_, __) =>
|
||||
{
|
||||
lock (events)
|
||||
{
|
||||
events.Add("d");
|
||||
}
|
||||
};
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("localhost")
|
||||
.WithClientId("same_id")
|
||||
.Build();
|
||||
|
||||
await s.StartAsync(new MqttServerOptions());
|
||||
|
||||
var c1 = new MqttFactory().CreateMqttClient();
|
||||
var c2 = new MqttFactory().CreateMqttClient();
|
||||
|
||||
await c1.ConnectAsync(clientOptions);
|
||||
|
||||
await Task.Delay(250);
|
||||
|
||||
await c2.ConnectAsync(clientOptions);
|
||||
|
||||
await Task.Delay(250);
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
await Task.Delay(250);
|
||||
|
||||
await c2.DisconnectAsync();
|
||||
|
||||
await Task.Delay(250);
|
||||
|
||||
await s.StopAsync();
|
||||
|
||||
var flow = string.Join(string.Empty, events);
|
||||
Assert.AreEqual("cdcd", flow);
|
||||
}
|
||||
|
||||
|
||||
[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
|
||||
{
|
||||
public IList<MqttApplicationMessage> Messages = new List<MqttApplicationMessage>();
|
||||
@@ -504,25 +843,24 @@ namespace MQTTnet.Core.Tests
|
||||
MqttQualityOfServiceLevel filterQualityOfServiceLevel,
|
||||
int expectedReceivedMessagesCount)
|
||||
{
|
||||
var serverAdapter = new TestMqttServerAdapter();
|
||||
var s = new MqttFactory().CreateMqttServer(new[] { serverAdapter }, new MqttNetLogger());
|
||||
var s = new MqttFactory().CreateMqttServer();
|
||||
|
||||
var receivedMessagesCount = 0;
|
||||
try
|
||||
{
|
||||
await s.StartAsync(new MqttServerOptions());
|
||||
|
||||
var c1 = await serverAdapter.ConnectTestClient("c1");
|
||||
var c2 = await serverAdapter.ConnectTestClient("c2");
|
||||
|
||||
var c1 = new MqttFactory().CreateMqttClient();
|
||||
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
|
||||
|
||||
await c1.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
|
||||
await c1.SubscribeAsync(new TopicFilterBuilder().WithTopic(topicFilter).WithQualityOfServiceLevel(filterQualityOfServiceLevel).Build());
|
||||
|
||||
var c2 = new MqttFactory().CreateMqttClient();
|
||||
await c2.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
|
||||
await c2.PublishAsync(builder => builder.WithTopic(topic).WithPayload(new byte[0]).WithQualityOfServiceLevel(qualityOfServiceLevel));
|
||||
|
||||
await Task.Delay(500);
|
||||
await c1.UnsubscribeAsync(topicFilter);
|
||||
|
||||
await Task.Delay(500);
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -22,13 +22,18 @@ namespace MQTTnet.TestApp.AspNetCore2
|
||||
.Build();
|
||||
services
|
||||
.AddHostedMqttServer(mqttServerOptions)
|
||||
.AddMqttConnectionHandler();
|
||||
.AddMqttConnectionHandler()
|
||||
.AddConnections();
|
||||
}
|
||||
|
||||
// In class _Startup_ of the ASP.NET Core 2.0 project.
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
app.UseMqttEndpoint();
|
||||
app.UseConnections(c => c.MapConnectionHandler<MqttConnectionHandler>("/mqtt", options => {
|
||||
options.WebSockets.SubProtocolSelector = MQTTnet.AspNetCore.ApplicationBuilderExtensions.SelectSubProtocol;
|
||||
}));
|
||||
|
||||
//app.UseMqttEndpoint();
|
||||
app.UseMqttServer(server =>
|
||||
{
|
||||
server.Started += async (sender, args) =>
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Protocol;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
@@ -12,7 +13,7 @@ namespace MQTTnet.TestApp.NetCore
|
||||
{
|
||||
public static async Task RunAsync()
|
||||
{
|
||||
//MqttNetGlobalLogger.LogMessagePublished += (s, e) => Console.WriteLine(e.TraceMessage);
|
||||
//MqttNetConsoleLogger.ForwardToConsole();
|
||||
|
||||
// iot.eclipse.org
|
||||
await ExecuteTestAsync("iot.eclipse.org TCP",
|
||||
@@ -35,7 +36,7 @@ namespace MQTTnet.TestApp.NetCore
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8080/mqtt").Build());
|
||||
|
||||
await ExecuteTestAsync("test.mosquitto.org WS TLS",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8081/mqtt").Build());
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8081/mqtt").WithTls().Build());
|
||||
|
||||
// broker.hivemq.com
|
||||
await ExecuteTestAsync("broker.hivemq.com TCP",
|
||||
@@ -78,7 +79,7 @@ namespace MQTTnet.TestApp.NetCore
|
||||
var topic = Guid.NewGuid().ToString();
|
||||
|
||||
MqttApplicationMessage receivedMessage = null;
|
||||
client.ApplicationMessageReceived += (s, e) => receivedMessage = e.ApplicationMessage;
|
||||
client.ApplicationMessageReceived += (s, e) => receivedMessage = e.ApplicationMessage;
|
||||
|
||||
await client.ConnectAsync(options);
|
||||
await client.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
@@ -142,10 +142,10 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>6.1.4</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)' < '14.0' ">
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:server="using:MQTTnet.Server"
|
||||
xmlns:interop="using:Windows.UI.Xaml.Interop"
|
||||
d:DesignHeight="800"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d">
|
||||
@@ -19,26 +18,55 @@
|
||||
<Pivot Grid.Row="0">
|
||||
<PivotItem Header="Connection">
|
||||
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<TextBlock>Server:</TextBlock>
|
||||
<TextBox x:Name="Server"></TextBox>
|
||||
<TextBlock>Port:</TextBlock>
|
||||
<TextBox x:Name="Port" Text="1883"></TextBox>
|
||||
<TextBlock>User:</TextBlock>
|
||||
<TextBox x:Name="User"></TextBox>
|
||||
<TextBlock>Password:</TextBlock>
|
||||
<TextBox x:Name="Password"></TextBox>
|
||||
<TextBlock>ClientId:</TextBlock>
|
||||
<TextBox x:Name="ClientId"></TextBox>
|
||||
<TextBlock>Clean session:</TextBlock>
|
||||
<CheckBox x:Name="CleanSession" IsChecked="True"></CheckBox>
|
||||
<TextBlock>Keep alive interval:</TextBlock>
|
||||
<TextBox x:Name="KeepAliveInterval" Text="5"></TextBox>
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="VerticalAlignment" Value="Center"></Setter>
|
||||
<Setter Property="Margin" Value="0, 0, 10, 0"></Setter>
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition></RowDefinition>
|
||||
<RowDefinition></RowDefinition>
|
||||
<RowDefinition></RowDefinition>
|
||||
<RowDefinition></RowDefinition>
|
||||
<RowDefinition></RowDefinition>
|
||||
<RowDefinition></RowDefinition>
|
||||
<RowDefinition></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<RadioButton x:Name="UseTcp" IsChecked="True" GroupName="connection">TCP</RadioButton>
|
||||
<RadioButton x:Name="UseWs" GroupName="connection">WS</RadioButton>
|
||||
<CheckBox x:Name="UseTls">Use TLS</CheckBox>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="0" Grid.Row="0">Server:</TextBlock>
|
||||
<TextBox Grid.Column="1" Grid.Row="0" x:Name="Server" Text="localhost"></TextBox>
|
||||
|
||||
<TextBlock Grid.Column="0" Grid.Row="1">Port:</TextBlock>
|
||||
<TextBox Grid.Column="1" Grid.Row="1" x:Name="Port" Text="1883"></TextBox>
|
||||
|
||||
<TextBlock Grid.Column="0" Grid.Row="2">Username:</TextBlock>
|
||||
<TextBox Grid.Column="1" Grid.Row="2" x:Name="User"></TextBox>
|
||||
|
||||
<TextBlock Grid.Column="0" Grid.Row="3">Password:</TextBlock>
|
||||
<TextBox Grid.Column="1" Grid.Row="3" x:Name="Password"></TextBox>
|
||||
|
||||
<TextBlock Grid.Column="0" Grid.Row="4">Client ID:</TextBlock>
|
||||
<TextBox Grid.Column="1" Grid.Row="4" x:Name="ClientId"></TextBox>
|
||||
|
||||
<TextBlock Grid.Column="0" Grid.Row="5">Keep Alive interval:</TextBlock>
|
||||
<TextBox Grid.Column="1" Grid.Row="5" x:Name="KeepAliveInterval" Text="5"></TextBox>
|
||||
|
||||
<TextBlock Grid.Column="0" Grid.Row="6">Protocol:</TextBlock>
|
||||
<StackPanel Grid.Column="1" Grid.Row="6" Orientation="Horizontal">
|
||||
<RadioButton x:Name="UseTcp" IsChecked="True" GroupName="connection">TCP</RadioButton>
|
||||
<RadioButton x:Name="UseWs" GroupName="connection">WS</RadioButton>
|
||||
<CheckBox x:Name="UseTls">Use TLS</CheckBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<CheckBox x:Name="CleanSession" IsChecked="True">Clean session</CheckBox>
|
||||
<CheckBox x:Name="UseManagedClient" IsChecked="False">Use managed client</CheckBox>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Click="Connect" Width="120" Margin="0,0,10,0">Connect</Button>
|
||||
@@ -49,25 +77,46 @@
|
||||
|
||||
<PivotItem Header="Publish">
|
||||
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<TextBlock>Topic:</TextBlock>
|
||||
<TextBox x:Name="Topic"></TextBox>
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="VerticalAlignment" Value="Center"></Setter>
|
||||
<Setter Property="Margin" Value="0, 0, 10, 0"></Setter>
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition></RowDefinition>
|
||||
<RowDefinition></RowDefinition>
|
||||
<RowDefinition></RowDefinition>
|
||||
<RowDefinition></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock>Payload:</TextBlock>
|
||||
<TextBox x:Name="Payload"></TextBox>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<RadioButton x:Name="Text" IsChecked="True" GroupName="payload">Text</RadioButton>
|
||||
<RadioButton x:Name="Base64" GroupName="payload">Base64</RadioButton>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="0" Grid.Row="0">Topic:</TextBlock>
|
||||
<TextBox Grid.Column="1" Grid.Row="0" x:Name="Topic"></TextBox>
|
||||
|
||||
<TextBlock>Retain:</TextBlock>
|
||||
<CheckBox x:Name="Retain"></CheckBox>
|
||||
<TextBlock Grid.Column="0" Grid.Row="1">Payload:</TextBlock>
|
||||
<TextBox Grid.Column="1" Grid.Row="1" x:Name="Payload"></TextBox>
|
||||
|
||||
<TextBlock Grid.Column="0" Grid.Row="2">Payload format:</TextBlock>
|
||||
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal">
|
||||
<RadioButton Margin="0,0,10,0" x:Name="PlainText" IsChecked="True" GroupName="payload">Plain text</RadioButton>
|
||||
<RadioButton Margin="0,0,10,0" x:Name="Base64" GroupName="payload">Base64 string</RadioButton>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Column="0" Grid.Row="3">QoS level:</TextBlock>
|
||||
<StackPanel Grid.Column="1" Grid.Row="3" Orientation="Horizontal">
|
||||
<RadioButton Margin="0,0,10,0" IsChecked="True" GroupName="qos">0 (At most once)</RadioButton>
|
||||
<RadioButton Margin="0,0,10,0" x:Name="QoS1" GroupName="qos">1 (At least once)</RadioButton>
|
||||
<RadioButton Margin="0,0,10,0" x:Name="QoS2" GroupName="qos">2 (Exactly once)</RadioButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<CheckBox x:Name="Retain">Retain</CheckBox>
|
||||
|
||||
<TextBlock>QoS:</TextBlock>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<RadioButton Margin="0,0,10,0" IsChecked="True" GroupName="qos">0 (At most once)</RadioButton>
|
||||
<RadioButton Margin="0,0,10,0" x:Name="QoS1" GroupName="qos">1 (At least once)</RadioButton>
|
||||
<RadioButton Margin="0,0,10,0" x:Name="QoS2" GroupName="qos">2 (Exactly once)</RadioButton>
|
||||
</StackPanel>
|
||||
<Button Click="Publish" Width="120">Publish</Button>
|
||||
</StackPanel>
|
||||
</PivotItem>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Security.Cryptography.Certificates;
|
||||
@@ -25,12 +26,15 @@ namespace MQTTnet.TestApp.UniversalWindows
|
||||
private readonly ObservableCollection<IMqttClientSessionStatus> _sessions = new ObservableCollection<IMqttClientSessionStatus>();
|
||||
|
||||
private IMqttClient _mqttClient;
|
||||
private IManagedMqttClient _managedMqttClient;
|
||||
private IMqttServer _mqttServer;
|
||||
|
||||
public MainPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
ClientId.Text = Guid.NewGuid().ToString("D");
|
||||
|
||||
MqttNetGlobalLogger.LogMessagePublished += OnTraceMessagePublished;
|
||||
}
|
||||
|
||||
@@ -81,7 +85,10 @@ namespace MQTTnet.TestApp.UniversalWindows
|
||||
AllowUntrustedCertificates = true
|
||||
};
|
||||
|
||||
var options = new MqttClientOptions { ClientId = ClientId.Text };
|
||||
var options = new MqttClientOptions
|
||||
{
|
||||
ClientId = ClientId.Text
|
||||
};
|
||||
|
||||
if (UseTcp.IsChecked == true)
|
||||
{
|
||||
@@ -127,12 +134,28 @@ namespace MQTTnet.TestApp.UniversalWindows
|
||||
}
|
||||
|
||||
var factory = new MqttFactory();
|
||||
_mqttClient = factory.CreateMqttClient();
|
||||
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
|
||||
_mqttClient.Connected += OnConnected;
|
||||
_mqttClient.Disconnected += OnDisconnected;
|
||||
|
||||
await _mqttClient.ConnectAsync(options);
|
||||
if (UseManagedClient.IsChecked == true)
|
||||
{
|
||||
_managedMqttClient = factory.CreateManagedMqttClient();
|
||||
_managedMqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
|
||||
_managedMqttClient.Connected += OnConnected;
|
||||
_managedMqttClient.Disconnected += OnDisconnected;
|
||||
|
||||
await _managedMqttClient.StartAsync(new ManagedMqttClientOptions
|
||||
{
|
||||
ClientOptions = options
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_mqttClient = factory.CreateMqttClient();
|
||||
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
|
||||
_mqttClient.Connected += OnConnected;
|
||||
_mqttClient.Disconnected += OnDisconnected;
|
||||
|
||||
await _mqttClient.ConnectAsync(options);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -171,11 +194,6 @@ namespace MQTTnet.TestApp.UniversalWindows
|
||||
|
||||
private async void Publish(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_mqttClient == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var qos = MqttQualityOfServiceLevel.AtMostOnce;
|
||||
@@ -190,7 +208,7 @@ namespace MQTTnet.TestApp.UniversalWindows
|
||||
}
|
||||
|
||||
var payload = new byte[0];
|
||||
if (Text.IsChecked == true)
|
||||
if (PlainText.IsChecked == true)
|
||||
{
|
||||
payload = Encoding.UTF8.GetBytes(Payload.Text);
|
||||
}
|
||||
@@ -207,7 +225,15 @@ namespace MQTTnet.TestApp.UniversalWindows
|
||||
.WithRetainFlag(Retain.IsChecked == true)
|
||||
.Build();
|
||||
|
||||
await _mqttClient.PublishAsync(message);
|
||||
if (_mqttClient != null)
|
||||
{
|
||||
await _mqttClient.PublishAsync(message);
|
||||
}
|
||||
|
||||
if (_managedMqttClient != null)
|
||||
{
|
||||
await _managedMqttClient.PublishAsync(message);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -219,7 +245,19 @@ namespace MQTTnet.TestApp.UniversalWindows
|
||||
{
|
||||
try
|
||||
{
|
||||
await _mqttClient.DisconnectAsync();
|
||||
if (_mqttClient != null)
|
||||
{
|
||||
await _mqttClient.DisconnectAsync();
|
||||
_mqttClient.Dispose();
|
||||
_mqttClient = null;
|
||||
}
|
||||
|
||||
if (_managedMqttClient != null)
|
||||
{
|
||||
await _managedMqttClient.StopAsync();
|
||||
_managedMqttClient.Dispose();
|
||||
_managedMqttClient = null;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -239,11 +277,6 @@ namespace MQTTnet.TestApp.UniversalWindows
|
||||
|
||||
private async void Subscribe(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_mqttClient == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var qos = MqttQualityOfServiceLevel.AtMostOnce;
|
||||
@@ -257,7 +290,15 @@ namespace MQTTnet.TestApp.UniversalWindows
|
||||
qos = MqttQualityOfServiceLevel.ExactlyOnce;
|
||||
}
|
||||
|
||||
await _mqttClient.SubscribeAsync(new TopicFilter(SubscribeTopic.Text, qos));
|
||||
if (_mqttClient != null)
|
||||
{
|
||||
await _mqttClient.SubscribeAsync(new TopicFilter(SubscribeTopic.Text, qos));
|
||||
}
|
||||
|
||||
if (_managedMqttClient != null)
|
||||
{
|
||||
await _managedMqttClient.SubscribeAsync(new TopicFilter(SubscribeTopic.Text, qos));
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -267,14 +308,17 @@ namespace MQTTnet.TestApp.UniversalWindows
|
||||
|
||||
private async void Unsubscribe(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_mqttClient == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _mqttClient.UnsubscribeAsync(SubscribeTopic.Text);
|
||||
if (_mqttClient != null)
|
||||
{
|
||||
await _mqttClient.UnsubscribeAsync(SubscribeTopic.Text);
|
||||
}
|
||||
|
||||
if (_managedMqttClient != null)
|
||||
{
|
||||
await _managedMqttClient.UnsubscribeAsync(SubscribeTopic.Text);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -398,6 +442,8 @@ namespace MQTTnet.TestApp.UniversalWindows
|
||||
ListViewSessions.DataContext = _sessions;
|
||||
}
|
||||
|
||||
#region Wiki Code
|
||||
|
||||
private async Task WikiCode()
|
||||
{
|
||||
{
|
||||
@@ -632,5 +678,7 @@ namespace MQTTnet.TestApp.UniversalWindows
|
||||
await mqttClient.StartAsync(options);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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/
|
||||
|
||||
|
||||
Reference in New Issue
Block a user