Compare commits

..
55 Commits
Author SHA1 Message Date
Christian Kratky ba62ca7306 Update docs. 2018-10-01 21:36:15 +02:00
Christian Kratky 56a1530cee Add a method for clearing all retained messages at the server. 2018-09-29 11:49:02 +02:00
Christian Kratky 699cd5fc23 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-09-28 22:51:27 +02:00
Christian Kratky 381c59d1e5 Update readme. 2018-09-28 22:51:16 +02:00
Christian Kratky 70f4dc9e10 Change log level of retained message changes. 2018-09-28 22:51:05 +02:00
Christian 0367ad31f1 Merge pull request #422 from ifit/client-cancel-task
Client cancel task
2018-09-28 22:49:11 +02:00
Christian 5c6bcdbca6 Merge branch 'develop' into client-cancel-task 2018-09-28 22:49:00 +02:00
Andre Crabb 400c0f05ad * Adds false param to Cancel call. 2018-09-28 14:07:04 -04:00
Andre Crabb 00df7c9b7b * More change tabs to spaces. 2018-09-28 11:01:07 -04:00
Andre Crabb a272ed7daa * Change tabs to spaces. 2018-09-28 11:00:18 -04:00
Andre Crabb b2e7a2e370 Change from logging Info to Verbose 2018-09-21 15:44:31 -04:00
Andre Crabb 39bc70cbf0 * Cancel the TokenSource when disposing or nulling it out for the MqttClient 2018-09-20 11:08:33 -04:00
Christian Kratky 682298b69f Fix issue in pending messages handling for managed client. 2018-09-13 09:29:26 +02:00
Christian Kratky 028a39ec25 Update docs. 2018-09-13 09:19:15 +02:00
Christian Kratky 3b104049b4 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-08-25 17:33:58 +02:00
Christian dd83a360b4 Merge pull request #402 from JanEggers/AspNetMqttServerOptionsBuilder
added AspNetMqttServerOptionsBuilder
2018-08-25 16:56:34 +02:00
JanEggers 976c62d9c2 added back the old function and added new overload 2018-08-25 12:08:42 +02:00
JanEggers 4cc9f12a03 added AspNetMqttServerOptionsBuilder 2018-08-21 21:32:03 +02:00
Christian Kratky 0b5a751c56 Add all methods of the factory to the factory interfaces. 2018-08-15 17:48:28 +02:00
Christian Kratky cdb05e5894 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-08-04 12:30:21 +02:00
Christian Kratky 6c08b5caf3 Update docs. 2018-08-04 12:30:08 +02:00
Christian d5b53f3712 Merge pull request #384 from cstichlberger/managed_client_fix_unsubscription_overtaking_subscription
Prevent unsubscription overtaking later subscription
2018-08-04 12:28:04 +02:00
Christoph Stichlberger 80fbb2f0b2 Prevent unsubscription overtaking later subscription 2018-08-02 16:25:14 +02:00
Christian Kratky b4f90decc7 Fix unit tests to allow release builds. 2018-07-27 17:20:19 +02:00
Christian Kratky 61b324043a Refactoring 2018-07-26 20:02:11 +02:00
Christian Kratky 2ced921008 Refactoring. 2018-07-26 14:48:40 +02:00
Christian 1e2fee0f33 Merge pull request #343 from JanEggers/ServerShutdown
Server shutdown
2018-07-26 14:45:44 +02:00
Christian ac36c9c406 Merge branch 'develop' into ServerShutdown 2018-07-26 14:45:25 +02:00
Christian Kratky f70c79aaf9 Refactor options. 2018-07-26 11:34:24 +02:00
Christian Kratky 0839fa5757 Update docs. 2018-07-26 10:43:40 +02:00
Christian Kratky 60bc4ebcee Refactor proxy usage and support. Refactor TLS parameter usage. 2018-07-26 10:35:52 +02:00
Christian 65e239652d Merge pull request #364 from PitySOFT/webproxy
Add proxy settings for WebSocket connections for .NET 451 and 461 and add option to specify the SSLProtocol and CertificateValidationCallback  in TLS options, SSLProtocol default is TLS1.2
2018-07-26 10:04:52 +02:00
Christian Kratky 6b9015a928 Fix order of ClientConnected and ClientDisconnected events. 2018-07-25 20:18:27 +02:00
Christian Kratky a740c38ee6 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-07-24 14:06:18 +02:00
Christian Kratky 603639126e Fix issue in processing of server messages. 2018-07-24 13:49:23 +02:00
JanEggers 043cfdb3f9 fixed deadlock and object disposed exception 2018-07-23 17:58:29 +02:00
Christian b0da4b2b2d Merge pull request #372 from jenscski/LogFix
Small fix for inconsistent client name in log
2018-07-22 21:10:05 +02:00
Christian 5402859a30 Merge pull request #368 from jenscski/ClientKeepAliveMonitor
Fix pause/resume of ClientKeepAliveMonitor
2018-07-22 21:09:21 +02:00
Jens-Christian Skibakk 2b10200101 Small fix for inconsistent client name in log 2018-07-22 20:09:37 +02:00
Jens-Christian Skibakk 55e8927ab7 Fix pause/resume of ClientKeepAliveMonitor 2018-07-20 14:13:21 +02:00
JanEggers d30fe61264 fixed missing waits 2018-07-19 21:13:29 +02:00
JanEggers f7ea2d29e1 prove its broken 2018-07-19 21:13:28 +02:00
JanEggers e6cfef5295 fixed disconnect to be triggered just once 2018-07-19 21:13:28 +02:00
JanEggers 0dea9e6a2b changed waiting condition 2018-07-19 21:13:28 +02:00
JanEggers ae80de26ec addressed feedback 2018-07-19 21:13:28 +02:00
JanEggers 53bd2a0960 fixed blocking code in networkstrem and reduced sleep in tests 2018-07-19 21:13:28 +02:00
JanEggers b55f5ff298 event is triggered but something is blocking 2018-07-19 21:13:27 +02:00
JanEggers 21048b51ae prove there is something wrong 2018-07-19 21:13:27 +02:00
Christian Kratky 773fd09166 Merge branch 'master' into develop 2018-07-19 21:02:37 +02:00
Christian 57532a290a Merge pull request #365 from jenscski/DotnetCliBuild
Skip build uap10.0 when using dotnet cli
2018-07-19 18:58:48 +02:00
Jens-Christian Skibakk 740a35b018 Skip build uap10.0 when using dotnet cli 2018-07-18 19:54:07 +02:00
Marius Ruginosu 6e995230a7 Created overload methods for WithTls default values 2018-07-18 16:54:19 +03:00
Marius Ruginosu 65a8b091b9 Add option to specify the SSLProtocol and CertificateValidationCallback in TLS options, SSLProtocol default is TLS1.2 2018-07-18 12:19:09 +03:00
Marius Ruginosu fa34908d82 Add proxy settings for WebSocket connections for .NET 451 and 461 2018-07-18 10:51:45 +03:00
Christian Kratky b09eb1213c Refactor logging. 2018-07-08 22:04:06 +02:00
33 changed files with 587 additions and 161 deletions
+2 -3
View File
@@ -10,13 +10,12 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl> <iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is a support library to integrate MQTTnet into AspNetCore.</description> <description>This is a support library to integrate MQTTnet into AspNetCore.</description>
<releaseNotes>* Updated to MQTTnet 2.8.1. <releaseNotes>* For more release notes please check the MQTTnet release notes.
* For more release notes please check the MQTTnet release notes.
</releaseNotes> </releaseNotes>
<copyright>Copyright Christian Kratky 2016-2018</copyright> <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> <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> <dependencies>
<dependency id="MQTTnet" version="2.8.1" /> <dependency id="MQTTnet" version="2.8.3" />
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.0" /> <dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.0" />
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.0.1" /> <dependency id="Microsoft.AspNetCore.WebSockets" version="2.0.1" />
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.0.1" /> <dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.0.1" />
@@ -10,13 +10,12 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl> <iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description> <description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description>
<releaseNotes>* Updated to MQTTnet 2.8.1. <releaseNotes>* For more release notes please check the MQTTnet release notes.
* For more release notes please check the MQTTnet release notes.
</releaseNotes> </releaseNotes>
<copyright>Copyright Christian Kratky 2016-2018</copyright> <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> <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> <dependencies>
<dependency id="MQTTnet" version="2.8.1" /> <dependency id="MQTTnet" version="2.8.3" />
</dependencies> </dependencies>
</metadata> </metadata>
@@ -34,6 +33,9 @@
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\"/> <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\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\"/> <file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net472\"/>
</files> </files>
</package> </package>
+5 -3
View File
@@ -10,13 +10,12 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl> <iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description> <description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
<releaseNotes>* Updated to MQTTnet 2.8.1. <releaseNotes>* For more release notes please check the MQTTnet release notes.
* For more release notes please check the MQTTnet release notes.
</releaseNotes> </releaseNotes>
<copyright>Copyright Christian Kratky 2016-2018</copyright> <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> <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> <dependencies>
<dependency id="MQTTnet" version="2.8.1" /> <dependency id="MQTTnet" version="2.8.3" />
</dependencies> </dependencies>
</metadata> </metadata>
@@ -35,5 +34,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\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\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> </files>
</package> </package>
+7 -6
View File
@@ -10,12 +10,10 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl> <iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <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> <description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
<releaseNotes>* [Core] Performance optimizations. <releaseNotes>* [Core] Added all factory methods to the factory interface.
* [Core] Fixed a bug which prevents receiving large packets (UWP only) * [Core] Fixed an issue with cancellation token handling (thanks to @acrabb).
* [Client] The ManagedClient options now allow configuring the interval for connection checks. * [Server] Added a new overload for configuring the ASP.net integration (thanks to @JanEggers).
* [Server] Added the Endpoint of the Adapter (remote IP and port) to the connection validation callback. * [Server] Added a method for clearing all retained messages.
* [Server] The ipv4 and ipv6 endpoint can be disabled now by setting the bound IP address to _None_.
* [Server] Fix a bug in the keep alive monitor which caused high CPU load (thanks to @GarageGadget).
</releaseNotes> </releaseNotes>
<copyright>Copyright Christian Kratky 2016-2018</copyright> <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> <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>
@@ -56,6 +54,9 @@
<file src="..\Source\MQTTnet\bin\Release\net452\MQTTnet.*" target="lib\net452\"/> <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\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\"/> <file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net472\"/>
</files> </files>
</package> </package>
+5 -2
View File
@@ -1,9 +1,11 @@
<p align="center"> <p align="center">
<img src="https://github.com/chkr1011/MQTTnet/blob/master/Images/Logo_128x128.png?raw=true" width="128"> <img src="https://github.com/chkr1011/MQTTnet/blob/master/Images/Logo_128x128.png?raw=true" width="128">
<br/>
<br/>
</p> </p>
[![NuGet Badge](https://buildstats.info/nuget/MQTTnet)](https://www.nuget.org/packages/MQTTnet) [![NuGet Badge](https://buildstats.info/nuget/MQTTnet)](https://www.nuget.org/packages/MQTTnet)
[![Build status](https://ci.appveyor.com/api/projects/status/ycit86voxfevm2aa/branch/master?svg=true)](https://ci.appveyor.com/project/chkr1011/mqttnet/branch/develop) [![Build status](https://ci.appveyor.com/api/projects/status/ycit86voxfevm2aa/branch/master?svg=true)](https://ci.appveyor.com/project/chkr1011/mqttnet)
[![BCH compliance](https://bettercodehub.com/edge/badge/chkr1011/MQTTnet?branch=master)](https://bettercodehub.com/) [![BCH compliance](https://bettercodehub.com/edge/badge/chkr1011/MQTTnet?branch=master)](https://bettercodehub.com/)
[![OpenCollective](https://opencollective.com/mqttnet/backers/badge.svg)](https://opencollective.com/mqttnet) [![OpenCollective](https://opencollective.com/mqttnet/backers/badge.svg)](https://opencollective.com/mqttnet)
[![OpenCollective](https://opencollective.com/mqttnet/sponsors/badge.svg)](https://opencollective.com/mqttnet) [![OpenCollective](https://opencollective.com/mqttnet/sponsors/badge.svg)](https://opencollective.com/mqttnet)
@@ -83,9 +85,10 @@ This project also listed at Open Collective (https://opencollective.com/mqttnet)
This library is used in the following projects: This library is used in the following projects:
* 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 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)) * 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 let me know.
@@ -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; }
}
}
@@ -23,11 +23,24 @@ namespace MQTTnet.AspNetCore
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action<MqttServerOptionsBuilder> configure) 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.AddHostedMqttServer();
services.AddSingleton<IMqttServerOptions>(builder.Build()); 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(); services.AddHostedMqttServer();
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461;uap10.0</TargetFrameworks> <TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);uap10.0</TargetFrameworks>
<Product /> <Product />
<Company /> <Company />
<Authors /> <Authors />
@@ -226,7 +226,7 @@ namespace MQTTnet.Extensions.ManagedClient
{ {
try try
{ {
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested && _mqttClient.IsConnected)
{ {
var message = _messageQueue.Take(cancellationToken); var message = _messageQueue.Take(cancellationToken);
if (message == null) if (message == null)
@@ -306,15 +306,15 @@ namespace MQTTnet.Extensions.ManagedClient
try try
{ {
if (subscriptions.Any())
{
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
}
if (unsubscriptions.Any()) if (unsubscriptions.Any())
{ {
await _mqttClient.UnsubscribeAsync(unsubscriptions).ConfigureAwait(false); await _mqttClient.UnsubscribeAsync(unsubscriptions).ConfigureAwait(false);
} }
if (subscriptions.Any())
{
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
}
} }
catch (Exception exception) catch (Exception exception)
{ {
@@ -367,7 +367,6 @@ namespace MQTTnet.Extensions.ManagedClient
} }
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
_publishingCancellationToken = cts; _publishingCancellationToken = cts;
Task.Factory.StartNew(() => PublishQueuedMessages(cts.Token), cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); Task.Factory.StartNew(() => PublishQueuedMessages(cts.Token), cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461;uap10.0</TargetFrameworks> <TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);uap10.0</TargetFrameworks>
<Product /> <Product />
<Company /> <Company />
<Authors /> <Authors />
@@ -7,5 +7,9 @@ namespace MQTTnet.Client
IMqttClient CreateMqttClient(); IMqttClient CreateMqttClient();
IMqttClient CreateMqttClient(IMqttNetLogger logger); IMqttClient CreateMqttClient(IMqttNetLogger logger);
IMqttClient CreateMqttClient(IMqttClientAdapterFactory adapterFactory);
IMqttClient CreateMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory adapterFactory);
} }
} }
+25 -11
View File
@@ -25,10 +25,11 @@ namespace MQTTnet.Client
private IMqttClientOptions _options; private IMqttClientOptions _options;
private CancellationTokenSource _cancellationTokenSource; private CancellationTokenSource _cancellationTokenSource;
private Task _packetReceiverTask; internal Task _packetReceiverTask;
private Task _keepAliveMessageSenderTask; internal Task _keepAliveMessageSenderTask;
private IMqttChannelAdapter _adapter; private IMqttChannelAdapter _adapter;
private bool _cleanDisconnectInitiated; private bool _cleanDisconnectInitiated;
private TaskCompletionSource<bool> _disconnectReason;
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger) public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
{ {
@@ -54,6 +55,7 @@ namespace MQTTnet.Client
try try
{ {
_cancellationTokenSource = new CancellationTokenSource(); _cancellationTokenSource = new CancellationTokenSource();
_disconnectReason = new TaskCompletionSource<bool>();
_options = options; _options = options;
_packetIdentifierProvider.Reset(); _packetIdentifierProvider.Reset();
_packetDispatcher.Reset(); _packetDispatcher.Reset();
@@ -85,8 +87,10 @@ namespace MQTTnet.Client
catch (Exception exception) catch (Exception exception)
{ {
_logger.Error(exception, "Error while connecting with server."); _logger.Error(exception, "Error while connecting with server.");
await DisconnectInternalAsync(null, exception).ConfigureAwait(false); if (_disconnectReason.TrySetException(exception))
{
await DisconnectInternalAsync(null, exception).ConfigureAwait(false);
}
throw; throw;
} }
} }
@@ -104,7 +108,10 @@ namespace MQTTnet.Client
} }
finally finally
{ {
await DisconnectInternalAsync(null, null).ConfigureAwait(false); if (_disconnectReason.TrySetCanceled())
{
await DisconnectInternalAsync(null, null).ConfigureAwait(false);
}
} }
} }
@@ -176,10 +183,12 @@ namespace MQTTnet.Client
public void Dispose() public void Dispose()
{ {
_cancellationTokenSource?.Cancel (false);
_cancellationTokenSource?.Dispose(); _cancellationTokenSource?.Dispose();
_cancellationTokenSource = null; _cancellationTokenSource = null;
_adapter?.Dispose(); _adapter?.Dispose();
_adapter = null;
} }
private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken) private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
@@ -238,10 +247,7 @@ namespace MQTTnet.Client
} }
finally finally
{ {
_adapter?.Dispose(); Dispose ();
_adapter = null;
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
_cleanDisconnectInitiated = false; _cleanDisconnectInitiated = false;
_logger.Info("Disconnected."); _logger.Info("Disconnected.");
@@ -352,7 +358,10 @@ namespace MQTTnet.Client
_logger.Error(exception, "Unhandled exception while sending/receiving keep alive packets."); _logger.Error(exception, "Unhandled exception while sending/receiving keep alive packets.");
} }
await DisconnectInternalAsync(_keepAliveMessageSenderTask, exception).ConfigureAwait(false); if (_disconnectReason.TrySetException(exception))
{
await DisconnectInternalAsync(_keepAliveMessageSenderTask, exception).ConfigureAwait(false);
}
} }
finally finally
{ {
@@ -384,6 +393,7 @@ namespace MQTTnet.Client
if (exception is OperationCanceledException) if (exception is OperationCanceledException)
{ {
_logger.Verbose ("MQTT OperationCanceled exception while receiving packets.");
} }
else if (exception is MqttCommunicationException) else if (exception is MqttCommunicationException)
{ {
@@ -395,7 +405,11 @@ namespace MQTTnet.Client
} }
_packetDispatcher.Dispatch(exception); _packetDispatcher.Dispatch(exception);
await DisconnectInternalAsync(_packetReceiverTask, exception).ConfigureAwait(false);
if (_disconnectReason.TrySetException(exception))
{
await DisconnectInternalAsync(_packetReceiverTask, exception).ConfigureAwait(false);
}
} }
finally finally
{ {
@@ -7,10 +7,11 @@ namespace MQTTnet.Client
public class MqttClientOptionsBuilder public class MqttClientOptionsBuilder
{ {
private readonly MqttClientOptions _options = new MqttClientOptions(); private readonly MqttClientOptions _options = new MqttClientOptions();
private MqttClientTcpOptions _tcpOptions; private MqttClientTcpOptions _tcpOptions;
private MqttClientWebSocketOptions _webSocketOptions; private MqttClientWebSocketOptions _webSocketOptions;
private MqttClientOptionsBuilderTlsParameters _tlsParameters;
private MqttClientTlsOptions _tlsOptions; private MqttClientWebSocketProxyOptions _proxyOptions;
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value) public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
{ {
@@ -76,6 +77,21 @@ namespace MQTTnet.Client
return this; 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) public MqttClientOptionsBuilder WithWebSocketServer(string uri)
{ {
_webSocketOptions = new MqttClientWebSocketOptions _webSocketOptions = new MqttClientWebSocketOptions
@@ -86,13 +102,25 @@ namespace MQTTnet.Client
return this; 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( public MqttClientOptionsBuilder WithTls(
bool allowUntrustedCertificates = false, bool allowUntrustedCertificates = false,
bool ignoreCertificateChainErrors = false, bool ignoreCertificateChainErrors = false,
bool ignoreCertificateRevocationErrors = false, bool ignoreCertificateRevocationErrors = false,
params byte[][] certificates) params byte[][] certificates)
{ {
_tlsOptions = new MqttClientTlsOptions _tlsParameters = new MqttClientOptionsBuilderTlsParameters
{ {
UseTls = true, UseTls = true,
AllowUntrustedCertificates = allowUntrustedCertificates, AllowUntrustedCertificates = allowUntrustedCertificates,
@@ -104,33 +132,47 @@ namespace MQTTnet.Client
return this; return this;
} }
public MqttClientOptionsBuilder WithTls()
{
_tlsOptions = new MqttClientTlsOptions
{
UseTls = true
};
return this;
}
public IMqttClientOptions Build() 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) _webSocketOptions.ProxyOptions = _proxyOptions;
{
_tcpOptions.TlsOptions = _tlsOptions;
}
else if (_webSocketOptions != null)
{
_webSocketOptions.TlsOptions = _tlsOptions;
}
} }
_options.ChannelOptions = (IMqttClientChannelOptions)_tcpOptions ?? _webSocketOptions; _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.Collections.Generic;
using System.Net.Security; using System.Net.Security;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Security.Authentication;
namespace MQTTnet.Client namespace MQTTnet.Client
{ {
@@ -17,6 +18,8 @@ namespace MQTTnet.Client
public List<byte[]> Certificates { get; set; } public List<byte[]> Certificates { get; set; }
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback { get; set; } public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback { get; set; }
} }
} }
@@ -13,6 +13,8 @@ namespace MQTTnet.Client
public CookieContainer CookieContainer { get; set; } public CookieContainer CookieContainer { get; set; }
public MqttClientWebSocketProxyOptions ProxyOptions { get; set; }
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions(); public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
public override string ToString() 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; }
}
}
@@ -63,7 +63,7 @@ namespace MQTTnet.Implementations
if (_options.TlsOptions.UseTls) if (_options.TlsOptions.UseTls)
{ {
sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback); 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); CreateStream(sslStream);
@@ -87,8 +87,14 @@ namespace MQTTnet.Implementations
public void Dispose() public void Dispose()
{ {
TryDispose(_stream, () => _stream = null); Cleanup(ref _stream, (s) => s.Dispose());
TryDispose(_socket, () => _socket = null); Cleanup(ref _socket, (s) => {
if (s.Connected)
{
s.Shutdown(SocketShutdown.Both);
}
s.Dispose();
});
} }
private bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) private bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
@@ -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 try
{ {
disposable?.Dispose(); if (temp != null)
{
handler(temp);
}
} }
catch (ObjectDisposedException) catch (ObjectDisposedException)
{ {
@@ -169,10 +180,6 @@ namespace MQTTnet.Implementations
catch (NullReferenceException) catch (NullReferenceException)
{ {
} }
finally
{
afterDispose();
}
} }
} }
} }
@@ -78,8 +78,10 @@ namespace MQTTnet.Implementations
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false); await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false);
} }
var protocol = _addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6"; _logger.Verbose("Client '{0}' accepted by TCP listener '{1}, {2}'.",
_logger.Verbose($"Client '{clientSocket.RemoteEndPoint}' accepted by TCP listener '{_socket.LocalEndPoint}, {protocol}'."); clientSocket.RemoteEndPoint,
_socket.LocalEndPoint,
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer(), _logger); var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer(), _logger);
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter)); ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
@@ -1,4 +1,5 @@
using System; using System;
using System.Net;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
@@ -45,6 +46,11 @@ namespace MQTTnet.Implementations
var clientWebSocket = new ClientWebSocket(); var clientWebSocket = new ClientWebSocket();
if (_options.ProxyOptions != null)
{
clientWebSocket.Options.Proxy = CreateProxy();
}
if (_options.RequestHeaders != null) if (_options.RequestHeaders != null)
{ {
foreach (var requestHeader in _options.RequestHeaders) foreach (var requestHeader in _options.RequestHeaders)
@@ -132,5 +138,31 @@ namespace MQTTnet.Implementations
_webSocket = null; _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
}
} }
} }
+3 -2
View File
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">netstandard1.3;netstandard2.0;net452;net461;uap10.0</TargetFrameworks> <TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">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> <AssemblyName>MQTTnet</AssemblyName>
<RootNamespace>MQTTnet</RootNamespace> <RootNamespace>MQTTnet</RootNamespace>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild> <GeneratePackageOnBuild>False</GeneratePackageOnBuild>
@@ -0,0 +1,5 @@
using System.Runtime.CompilerServices;
#if DEBUG
[assembly:InternalsVisibleTo("MQTTnet.Core.Tests")]
#endif
+1 -1
View File
@@ -14,7 +14,7 @@ namespace MQTTnet.Server
void EnqueueApplicationMessage(MqttClientSession senderClientSession, MqttPublishPacket publishPacket); void EnqueueApplicationMessage(MqttClientSession senderClientSession, MqttPublishPacket publishPacket);
void ClearPendingApplicationMessages(); void ClearPendingApplicationMessages();
Task<bool> RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter); Task RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter);
void Stop(MqttClientDisconnectType disconnectType); void Stop(MqttClientDisconnectType disconnectType);
Task SubscribeAsync(IList<TopicFilter> topicFilters); Task SubscribeAsync(IList<TopicFilter> topicFilters);
+2
View File
@@ -23,5 +23,7 @@ namespace MQTTnet.Server
Task StartAsync(IMqttServerOptions options); Task StartAsync(IMqttServerOptions options);
Task StopAsync(); Task StopAsync();
Task ClearRetainedMessagesAsync();
} }
} }
@@ -8,6 +8,8 @@ namespace MQTTnet.Server
{ {
IMqttServer CreateMqttServer(); IMqttServer CreateMqttServer();
IMqttServer CreateMqttServer(IMqttNetLogger logger);
IMqttServer CreateMqttServer(IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger); IMqttServer CreateMqttServer(IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger);
} }
} }
@@ -96,7 +96,7 @@ namespace MQTTnet.Server
} }
finally finally
{ {
_logger.Verbose("Client {0}: Stopped checking keep alive timeout.", _clientSession.ClientId); _logger.Verbose("Client '{0}': Stopped checking keep alive timeout.", _clientSession.ClientId);
} }
} }
} }
+69 -15
View File
@@ -29,6 +29,8 @@ namespace MQTTnet.Server
private MqttApplicationMessage _willMessage; private MqttApplicationMessage _willMessage;
private bool _wasCleanDisconnect; private bool _wasCleanDisconnect;
private IMqttChannelAdapter _adapter; private IMqttChannelAdapter _adapter;
private Task _workerTask;
private IDisposable _cleanupHandle;
public MqttClientSession( public MqttClientSession(
string clientId, string clientId,
@@ -65,7 +67,13 @@ namespace MQTTnet.Server
status.LastNonKeepAlivePacketReceived = _keepAliveMonitor.LastNonKeepAlivePacketReceived; status.LastNonKeepAlivePacketReceived = _keepAliveMonitor.LastNonKeepAlivePacketReceived;
} }
public async Task<bool> RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter) public Task RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter)
{
_workerTask = RunInternalAsync(connectPacket, adapter);
return _workerTask;
}
private async Task RunInternalAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter)
{ {
if (connectPacket == null) throw new ArgumentNullException(nameof(connectPacket)); if (connectPacket == null) throw new ArgumentNullException(nameof(connectPacket));
if (adapter == null) throw new ArgumentNullException(nameof(adapter)); if (adapter == null) throw new ArgumentNullException(nameof(adapter));
@@ -73,10 +81,18 @@ namespace MQTTnet.Server
try try
{ {
_adapter = adapter; _adapter = adapter;
adapter.ReadingPacketStarted += OnAdapterReadingPacketStarted; adapter.ReadingPacketStarted += OnAdapterReadingPacketStarted;
adapter.ReadingPacketCompleted += OnAdapterReadingPacketCompleted; adapter.ReadingPacketCompleted += OnAdapterReadingPacketCompleted;
_cancellationTokenSource = new CancellationTokenSource(); _cancellationTokenSource = new CancellationTokenSource();
//woraround for https://github.com/dotnet/corefx/issues/24430
#pragma warning disable 4014
_cleanupHandle = _cancellationTokenSource.Token.Register(() => CleanupAsync());
#pragma warning restore 4014
//endworkaround
_wasCleanDisconnect = false; _wasCleanDisconnect = false;
_willMessage = connectPacket.WillMessage; _willMessage = connectPacket.WillMessage;
@@ -114,26 +130,55 @@ namespace MQTTnet.Server
_logger.Error(exception, "Client '{0}': Unhandled exception while receiving client packets.", ClientId); _logger.Error(exception, "Client '{0}': Unhandled exception while receiving client packets.", ClientId);
} }
Stop(MqttClientDisconnectType.NotClean); Stop(MqttClientDisconnectType.NotClean, true);
} }
finally finally
{ {
if (_adapter != null) await CleanupAsync().ConfigureAwait(false);
_cleanupHandle?.Dispose();
_cleanupHandle = null;
_adapter = null;
_cancellationTokenSource?.Cancel(false);
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
}
}
private async Task CleanupAsync()
{
var adapter = _adapter;
try
{
if (adapter == null)
{ {
_adapter.ReadingPacketStarted -= OnAdapterReadingPacketStarted; return;
_adapter.ReadingPacketCompleted -= OnAdapterReadingPacketCompleted;
} }
_adapter = null; _adapter = null;
_cancellationTokenSource?.Dispose(); adapter.ReadingPacketStarted -= OnAdapterReadingPacketStarted;
_cancellationTokenSource = null; adapter.ReadingPacketCompleted -= OnAdapterReadingPacketCompleted;
}
return _wasCleanDisconnect; await adapter.DisconnectAsync(_options.DefaultCommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.Error(exception, exception.Message);
}
finally
{
adapter?.Dispose();
}
} }
public void Stop(MqttClientDisconnectType type) public void Stop(MqttClientDisconnectType type)
{
Stop(type, false);
}
private void Stop(MqttClientDisconnectType type, bool isInsideSession)
{ {
try try
{ {
@@ -153,10 +198,17 @@ namespace MQTTnet.Server
} }
_willMessage = null; _willMessage = null;
if (!isInsideSession)
{
_workerTask?.GetAwaiter().GetResult();
}
} }
finally finally
{ {
_logger.Info("Client '{0}': Session stopped.", ClientId); _logger.Info("Client '{0}': Session stopped.", ClientId);
_sessionsManager.Server.OnClientDisconnected(ClientId, _wasCleanDisconnect);
} }
} }
@@ -240,7 +292,9 @@ namespace MQTTnet.Server
{ {
_pendingPacketsQueue?.Dispose(); _pendingPacketsQueue?.Dispose();
_cancellationTokenSource?.Cancel ();
_cancellationTokenSource?.Dispose(); _cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
} }
private void ProcessReceivedPacket(IMqttChannelAdapter adapter, MqttBasePacket packet, CancellationToken cancellationToken) private void ProcessReceivedPacket(IMqttChannelAdapter adapter, MqttBasePacket packet, CancellationToken cancellationToken)
@@ -298,18 +352,18 @@ namespace MQTTnet.Server
if (packet is MqttDisconnectPacket) if (packet is MqttDisconnectPacket)
{ {
Stop(MqttClientDisconnectType.Clean); Stop(MqttClientDisconnectType.Clean, true);
return; return;
} }
if (packet is MqttConnectPacket) if (packet is MqttConnectPacket)
{ {
Stop(MqttClientDisconnectType.NotClean); Stop(MqttClientDisconnectType.NotClean, true);
return; return;
} }
_logger.Warning(null, "Client '{0}': Received not supported packet ({1}). Closing connection.", ClientId, packet); _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) private void EnqueueSubscribedRetainedMessages(ICollection<TopicFilter> topicFilters)
@@ -328,7 +382,7 @@ namespace MQTTnet.Server
if (subscribeResult.CloseConnection) if (subscribeResult.CloseConnection)
{ {
Stop(MqttClientDisconnectType.NotClean); Stop(MqttClientDisconnectType.NotClean, true);
return; return;
} }
@@ -405,12 +459,12 @@ namespace MQTTnet.Server
private void OnAdapterReadingPacketCompleted(object sender, EventArgs e) private void OnAdapterReadingPacketCompleted(object sender, EventArgs e)
{ {
_keepAliveMonitor?.Pause(); _keepAliveMonitor?.Resume();
} }
private void OnAdapterReadingPacketStarted(object sender, EventArgs e) private void OnAdapterReadingPacketStarted(object sender, EventArgs e)
{ {
_keepAliveMonitor?.Resume(); _keepAliveMonitor?.Pause();
} }
} }
} }
@@ -44,7 +44,7 @@ namespace MQTTnet.Server
public void Start() 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() public void Stop()
@@ -125,6 +125,7 @@ namespace MQTTnet.Server
{ {
_sessions.Remove(clientId); _sessions.Remove(clientId);
} }
_logger.Verbose("Session for client '{0}' deleted.", clientId); _logger.Verbose("Session for client '{0}' deleted.", clientId);
} }
@@ -133,54 +134,69 @@ namespace MQTTnet.Server
_messageQueue?.Dispose(); _messageQueue?.Dispose();
} }
private void ProcessQueuedApplicationMessages(CancellationToken cancellationToken) private void TryProcessQueuedApplicationMessages(CancellationToken cancellationToken)
{ {
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
try try
{ {
var enqueuedApplicationMessage = _messageQueue.Take(cancellationToken); TryProcessNextQueuedApplicationMessage(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());
}
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
} }
catch (Exception exception) 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;
}
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());
}
}
catch (OperationCanceledException)
{
}
catch (Exception exception)
{
_logger.Error(exception, "Unhandled exception while processing next queued application message.");
}
}
private List<MqttClientSession> GetSessions() private List<MqttClientSession> GetSessions()
{ {
lock (_sessions) lock (_sessions)
@@ -192,7 +208,6 @@ namespace MQTTnet.Server
private async Task RunSession(IMqttChannelAdapter clientAdapter, CancellationToken cancellationToken) private async Task RunSession(IMqttChannelAdapter clientAdapter, CancellationToken cancellationToken)
{ {
var clientId = string.Empty; var clientId = string.Empty;
var wasCleanDisconnect = false;
try try
{ {
@@ -238,7 +253,7 @@ namespace MQTTnet.Server
Server.OnClientConnected(clientId); Server.OnClientConnected(clientId);
wasCleanDisconnect = await clientSession.RunAsync(connectPacket, clientAdapter).ConfigureAwait(false); await clientSession.RunAsync(connectPacket, clientAdapter).ConfigureAwait(false);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@@ -249,22 +264,10 @@ namespace MQTTnet.Server
} }
finally finally
{ {
try
{
await clientAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
clientAdapter.Dispose();
}
catch (Exception exception)
{
_logger.Error(exception, exception.Message);
}
if (!_options.EnablePersistentSessions) if (!_options.EnablePersistentSessions)
{ {
DeleteSession(clientId); DeleteSession(clientId);
} }
Server.OnClientDisconnected(clientId, wasCleanDisconnect);
} }
} }
@@ -84,6 +84,16 @@ namespace MQTTnet.Server
return retainedMessages; return retainedMessages;
} }
public Task ClearMessagesAsync()
{
lock (_messages)
{
_messages.Clear();
}
return _options.Storage.SaveRetainedMessagesAsync(new List<MqttApplicationMessage>());
}
private async Task HandleMessageInternalAsync(string clientId, MqttApplicationMessage applicationMessage) private async Task HandleMessageInternalAsync(string clientId, MqttApplicationMessage applicationMessage)
{ {
var saveIsRequired = false; var saveIsRequired = false;
@@ -93,7 +103,7 @@ namespace MQTTnet.Server
if (applicationMessage.Payload?.Length == 0) if (applicationMessage.Payload?.Length == 0)
{ {
saveIsRequired = _messages.Remove(applicationMessage.Topic); saveIsRequired = _messages.Remove(applicationMessage.Topic);
_logger.Info("Client '{0}' cleared retained message for topic '{1}'.", clientId, applicationMessage.Topic); _logger.Verbose("Client '{0}' cleared retained message for topic '{1}'.", clientId, applicationMessage.Topic);
} }
else else
{ {
@@ -111,7 +121,7 @@ namespace MQTTnet.Server
} }
} }
_logger.Info("Client '{0}' set retained message for topic '{1}'.", clientId, applicationMessage.Topic); _logger.Verbose("Client '{0}' set retained message for topic '{1}'.", clientId, applicationMessage.Topic);
} }
} }
+7 -2
View File
@@ -106,14 +106,14 @@ namespace MQTTnet.Server
_cancellationTokenSource.Cancel(false); _cancellationTokenSource.Cancel(false);
_clientSessionsManager.Stop();
foreach (var adapter in _adapters) foreach (var adapter in _adapters)
{ {
adapter.ClientAccepted -= OnClientAccepted; adapter.ClientAccepted -= OnClientAccepted;
await adapter.StopAsync().ConfigureAwait(false); await adapter.StopAsync().ConfigureAwait(false);
} }
_clientSessionsManager.Stop();
_logger.Info("Stopped."); _logger.Info("Stopped.");
Stopped?.Invoke(this, EventArgs.Empty); Stopped?.Invoke(this, EventArgs.Empty);
} }
@@ -129,6 +129,11 @@ namespace MQTTnet.Server
} }
} }
public Task ClearRetainedMessagesAsync()
{
return _retainedMessagesManager?.ClearMessagesAsync();
}
internal void OnClientConnected(string clientId) internal void OnClientConnected(string clientId)
{ {
_logger.Info("Client '{0}': Connected.", clientId); _logger.Info("Client '{0}': Connected.", clientId);
+45 -5
View File
@@ -1,25 +1,29 @@
using System.Net.Sockets; using System;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Adapter;
using MQTTnet.Client; using MQTTnet.Client;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
using MQTTnet.Packets;
namespace MQTTnet.Core.Tests namespace MQTTnet.Core.Tests
{ {
[TestClass] [TestClass]
public class MqttClientTests public class MqttClientTests
{ {
[TestMethod] [TestMethod]
public async Task ClientDisconnectException() public async Task ClientDisconnectException()
{ {
var factory = new MqttFactory(); var factory = new MqttFactory();
var client = factory.CreateMqttClient(); var client = factory.CreateMqttClient();
var exceptionIsCorrect = false; Exception ex = null;
client.Disconnected += (s, e) => client.Disconnected += (s, e) =>
{ {
exceptionIsCorrect = e.Exception is MqttCommunicationException c && c.InnerException is SocketException; ex = e.Exception;
}; };
try try
@@ -30,7 +34,43 @@ namespace MQTTnet.Core.Tests
{ {
} }
Assert.IsTrue(exceptionIsCorrect); Assert.IsNotNull(ex);
Assert.IsInstanceOfType(ex, typeof(MqttCommunicationException));
Assert.IsInstanceOfType(ex.InnerException, typeof(SocketException));
} }
#if DEBUG
[TestMethod]
public async Task ClientCleanupOnAuthentificationFails()
{
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 string ClientId { get; }
public int StopCalledCount { get; set; } public int StopCalledCount { get; private set; }
public void FillStatus(MqttClientSessionStatus status) public void FillStatus(MqttClientSessionStatus status)
{ {
@@ -77,7 +77,7 @@ namespace MQTTnet.Core.Tests
throw new NotSupportedException(); throw new NotSupportedException();
} }
public Task<bool> RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter) public Task RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
+116
View File
@@ -234,6 +234,74 @@ 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()
{
MqttNetGlobalLogger.LogMessagePublished += (_, e) =>
{
System.Diagnostics.Debug.WriteLine($"[{e.TraceMessage.Timestamp:s}] {e.TraceMessage.Source} {e.TraceMessage.Message}");
};
var serverAdapter = new MqttTcpServerAdapter(new MqttNetLogger().CreateChildLogger());
var s = new MqttFactory().CreateMqttServer(new[] { serverAdapter }, new MqttNetLogger());
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] [TestMethod]
public async Task MqttServer_RetainedMessagesFlow() public async Task MqttServer_RetainedMessagesFlow()
{ {
@@ -484,6 +552,54 @@ namespace MQTTnet.Core.Tests
Assert.IsTrue(bodyIsMatching); Assert.IsTrue(bodyIsMatching);
} }
[TestMethod]
public async Task MqttServer_SameClientIdConnectDisconnectEventOrder()
{
var serverAdapter = new MqttTcpServerAdapter(new MqttNetLogger().CreateChildLogger());
var s = new MqttFactory().CreateMqttServer(new[] { serverAdapter }, new MqttNetLogger());
var connectedClient = false;
var connecteCalledBeforeConnectedClients = false;
s.ClientConnected += (_, __) =>
{
connecteCalledBeforeConnectedClients |= connectedClient;
connectedClient = true;
};
s.ClientDisconnected += (_, __) =>
{
connectedClient = false;
};
var clientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("localhost")
.WithClientId(Guid.NewGuid().ToString())
.Build();
await s.StartAsync(new MqttServerOptions());
var c1 = new MqttFactory().CreateMqttClient();
var c2 = new MqttFactory().CreateMqttClient();
await c1.ConnectAsync(clientOptions);
await Task.Delay(100);
await c2.ConnectAsync(clientOptions);
await Task.Delay(100);
await c1.DisconnectAsync();
await c2.DisconnectAsync();
await s.StopAsync();
await Task.Delay(100);
Assert.IsFalse(connecteCalledBeforeConnectedClients, "ClientConnected was called before ClientDisconnect was called");
}
private class TestStorage : IMqttServerStorage private class TestStorage : IMqttServerStorage
{ {
public IList<MqttApplicationMessage> Messages = new List<MqttApplicationMessage>(); public IList<MqttApplicationMessage> Messages = new List<MqttApplicationMessage>();