Merge from develop.

This commit is contained in:
Christian Kratky
2018-11-16 22:16:21 +01:00
50 changed files with 845 additions and 168 deletions
+5 -6
View File
@@ -10,15 +10,14 @@
<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>* For more release notes please check the MQTTnet release notes. <releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</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.2" /> <dependency id="MQTTnet" version="2.8.5-rc3" />
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.0" /> <dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.3" />
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.0.1" /> <dependency id="Microsoft.AspNetCore.WebSockets" version="2.1.1" />
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.0.1" /> <dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.1.1" />
</dependencies> </dependencies>
</metadata> </metadata>
@@ -10,12 +10,11 @@
<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>* For more release notes please check the MQTTnet release notes. <releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</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.2" /> <dependency id="MQTTnet" version="2.8.5-rc3" />
</dependencies> </dependencies>
</metadata> </metadata>
+2 -3
View File
@@ -10,12 +10,11 @@
<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>* For more release notes please check the MQTTnet release notes. <releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</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.2" /> <dependency id="MQTTnet" version="2.8.5-rc3" />
</dependencies> </dependencies>
</metadata> </metadata>
+10 -11
View File
@@ -10,15 +10,14 @@
<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] Updated nuget packages due to security issues.
* [Client] Added support for proxies when using web socket connections (thanks to PitySOFT). * [Client] Fixed wrong behavior of publish method when client is disconnecting (thanks to @PaulFake).
* [Client] Refactored TLS parameter usage and added more parameters (thanks to PitySOFT). * [ManagedClient] Added max pending messages count option.
* [Client] Fixed a deadlock when clients are disconnecting (thanks to @JanEggers). * [ManagedClient] Add pending messages overflow strategy option.
* [ManagedClient] Unsubscriptions are now pushed before subscriptions to support re-subscribing (thanks to @cstichlberger). * [Server] Added new method which exposes all retained messages.
* [Server] Fixed an issue in client keep alive checks (thanks to @jenscski). * [Server] Removed (wrong) setter from the server options interface.
* [Server] Changed the order of _ClientConnected_ and _ClientDisconnected_ events so that _ClientConnected_ is fired at first (thanks to @jenscski). * [Server] fixed cpu spike in case a client disconnectes (issue 421).
* [Server] Fixed an iussue which lets the server stop processing messages when using the application message interceptor (thanks to @alamsor). * [Server] fixed concurrent sends with Aspnetcore.Connections.Abstractions based transport.
* [Server] Fixed a deadlock when clients are disconnecting (thanks to @JanEggers).
</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>
@@ -27,7 +26,7 @@
<dependency id="NETStandard.Library" version="1.3.0" /> <dependency id="NETStandard.Library" version="1.3.0" />
<dependency id="System.Net.Security" version="4.3.2" /> <dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" /> <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>
<group targetFramework="netstandard2.0"> <group targetFramework="netstandard2.0">
<dependency id="NETStandard.Library" version="2.0.0" /> <dependency id="NETStandard.Library" version="2.0.0" />
@@ -36,7 +35,7 @@
<dependency id="System.Net.WebSockets.Client" version="4.3.2" /> <dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group> </group>
<group targetFramework="uap10.0"> <group targetFramework="uap10.0">
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.4" /> <dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.9" />
</group> </group>
<group targetFramework="net452"> <group targetFramework="net452">
</group> </group>
+4 -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)
@@ -23,7 +25,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
* Performance optimized (processing ~70.000 messages / second)* * Performance optimized (processing ~70.000 messages / second)*
* Interfaces included for mocking and testing * Interfaces included for mocking and testing
* Access to internal trace messages * 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_. \* 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_.
@@ -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> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.3" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.1" /> <PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using MQTTnet.Adapter; using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp; using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Exceptions;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Serializer; using MQTTnet.Serializer;
using System; using System;
@@ -24,6 +25,8 @@ namespace MQTTnet.AspNetCore
public event EventHandler ReadingPacketStarted; public event EventHandler ReadingPacketStarted;
public event EventHandler ReadingPacketCompleted; public event EventHandler ReadingPacketCompleted;
private readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1);
public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken) public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
{ {
if (Connection is TcpConnection tcp && !tcp.IsConnected) if (Connection is TcpConnection tcp && !tcp.IsConnected)
@@ -57,7 +60,7 @@ namespace MQTTnet.AspNetCore
} }
else else
{ {
readResult = await readTask; readResult = await readTask.ConfigureAwait(false);
} }
var buffer = readResult.Buffer; var buffer = readResult.Buffer;
@@ -81,7 +84,7 @@ namespace MQTTnet.AspNetCore
} }
else if (readResult.IsCompleted) else if (readResult.IsCompleted)
{ {
break; throw new MqttCommunicationException("Connection Aborted");
} }
} }
finally finally
@@ -102,10 +105,20 @@ namespace MQTTnet.AspNetCore
return null; return null;
} }
public Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken) public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{ {
var buffer = PacketSerializerAdapter.Serialize(packet); var buffer = PacketSerializer.Serialize(packet).AsMemory();
return Connection.Transport.Output.WriteAsync(buffer.AsMemory(), cancellationToken).AsTask(); 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() public void Dispose()
@@ -20,14 +20,27 @@ namespace MQTTnet.AspNetCore
return services; return services;
} }
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();
@@ -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; }
}
}
@@ -15,6 +15,7 @@ namespace MQTTnet.Extensions.ManagedClient
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected; event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed; event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
event EventHandler<ApplicationMessageSkippedEventArgs> ApplicationMessageSkipped;
event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed; event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed;
event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed; event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed;
@@ -1,5 +1,6 @@
using System; using System;
using MQTTnet.Client; using MQTTnet.Client;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient namespace MQTTnet.Extensions.ManagedClient
{ {
@@ -12,5 +13,9 @@ namespace MQTTnet.Extensions.ManagedClient
TimeSpan ConnectionCheckInterval { get; } TimeSpan ConnectionCheckInterval { get; }
IManagedMqttClientStorage Storage { get; } IManagedMqttClientStorage Storage { get; }
int MaxPendingMessages { get; }
MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; }
} }
} }
@@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@@ -7,13 +6,17 @@ using System.Threading.Tasks;
using MQTTnet.Client; using MQTTnet.Client;
using MQTTnet.Diagnostics; using MQTTnet.Diagnostics;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using MQTTnet.Server;
using MqttClientConnectedEventArgs = MQTTnet.Client.MqttClientConnectedEventArgs;
using MqttClientDisconnectedEventArgs = MQTTnet.Client.MqttClientDisconnectedEventArgs;
namespace MQTTnet.Extensions.ManagedClient namespace MQTTnet.Extensions.ManagedClient
{ {
public class ManagedMqttClient : IManagedMqttClient 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 Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
private readonly HashSet<string> _unsubscriptions = new HashSet<string>(); private readonly HashSet<string> _unsubscriptions = new HashSet<string>();
@@ -50,6 +53,7 @@ namespace MQTTnet.Extensions.ManagedClient
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived; public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
public event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed; public event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
public event EventHandler<ApplicationMessageSkippedEventArgs> ApplicationMessageSkipped;
public event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed; public event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed;
public event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed; public event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed;
@@ -75,7 +79,7 @@ namespace MQTTnet.Extensions.ManagedClient
foreach (var message in messages) foreach (var message in messages)
{ {
_messageQueue.Add(message); _messageQueue.Enqueue(message);
} }
} }
@@ -93,10 +97,7 @@ namespace MQTTnet.Extensions.ManagedClient
StopPublishing(); StopPublishing();
StopMaintainingConnection(); StopMaintainingConnection();
while (_messageQueue.Any()) _messageQueue.Clear();
{
_messageQueue.Take();
}
return Task.FromResult(0); return Task.FromResult(0);
} }
@@ -112,12 +113,38 @@ namespace MQTTnet.Extensions.ManagedClient
{ {
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
if (_storageManager != null) ManagedMqttApplicationMessage removedMessage = null;
lock (_messageQueue)
{ {
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false); if (_messageQueue.Count >= _options.MaxPendingMessages)
{
if (_options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
{
_logger.Verbose("Skipping publish of new application message because internal queue is full.");
ApplicationMessageSkipped?.Invoke(this, new ApplicationMessageSkippedEventArgs(applicationMessage));
return;
}
if (_options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage)
{
removedMessage = _messageQueue.RemoveFirst();
_logger.Verbose("Removed oldest application message from internal queue because it is full.");
ApplicationMessageSkipped?.Invoke(this, new ApplicationMessageSkippedEventArgs(removedMessage));
}
}
_messageQueue.Enqueue(applicationMessage);
} }
_messageQueue.Add(applicationMessage); if (_storageManager != null)
{
if (removedMessage != null)
{
await _storageManager.RemoveAsync(removedMessage).ConfigureAwait(false);
}
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
}
} }
public Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters) public Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
@@ -157,7 +184,6 @@ namespace MQTTnet.Extensions.ManagedClient
public void Dispose() public void Dispose()
{ {
_messageQueue?.Dispose();
_connectionCancellationToken?.Dispose(); _connectionCancellationToken?.Dispose();
_publishingCancellationToken?.Dispose(); _publishingCancellationToken?.Dispose();
} }
@@ -226,9 +252,9 @@ namespace MQTTnet.Extensions.ManagedClient
{ {
try try
{ {
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested && _mqttClient.IsConnected)
{ {
var message = _messageQueue.Take(cancellationToken); var message = _messageQueue.Dequeue();
if (message == null) if (message == null)
{ {
continue; continue;
@@ -268,7 +294,7 @@ namespace MQTTnet.Extensions.ManagedClient
if (message.ApplicationMessage.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) if (message.ApplicationMessage.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{ {
_messageQueue.Add(message); _messageQueue.Enqueue(message);
} }
} }
catch (Exception exception) catch (Exception exception)
@@ -367,7 +393,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,5 +1,6 @@
using System; using System;
using MQTTnet.Client; using MQTTnet.Client;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient namespace MQTTnet.Extensions.ManagedClient
{ {
@@ -12,5 +13,9 @@ namespace MQTTnet.Extensions.ManagedClient
public TimeSpan ConnectionCheckInterval { get; set; } = TimeSpan.FromSeconds(1); public TimeSpan ConnectionCheckInterval { get; set; } = TimeSpan.FromSeconds(1);
public IManagedMqttClientStorage Storage { get; set; } 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 System;
using MQTTnet.Client; using MQTTnet.Client;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient namespace MQTTnet.Extensions.ManagedClient
{ {
@@ -8,6 +9,18 @@ namespace MQTTnet.Extensions.ManagedClient
private readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions(); private readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions();
private MqttClientOptionsBuilder _clientOptionsBuilder; 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) public ManagedMqttClientOptionsBuilder WithAutoReconnectDelay(TimeSpan value)
{ {
_options.AutoReconnectDelay = value; _options.AutoReconnectDelay = value;
@@ -28,6 +28,8 @@ namespace MQTTnet.Extensions.ManagedClient
public async Task AddAsync(ManagedMqttApplicationMessage applicationMessage) public async Task AddAsync(ManagedMqttApplicationMessage applicationMessage)
{ {
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false)) using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
{ {
_messages.Add(applicationMessage); _messages.Add(applicationMessage);
@@ -37,6 +39,8 @@ namespace MQTTnet.Extensions.ManagedClient
public async Task RemoveAsync(ManagedMqttApplicationMessage applicationMessage) public async Task RemoveAsync(ManagedMqttApplicationMessage applicationMessage)
{ {
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false)) using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
{ {
var index = _messages.IndexOf(applicationMessage); var index = _messages.IndexOf(applicationMessage);
+5 -1
View File
@@ -154,6 +154,9 @@ namespace MQTTnet.Adapter
return packet; return packet;
} }
catch (OperationCanceledException)
{
}
catch (Exception exception) catch (Exception exception)
{ {
if (IsWrappedException(exception)) if (IsWrappedException(exception))
@@ -241,7 +244,8 @@ namespace MQTTnet.Adapter
{ {
if (exception is IOException && exception.InnerException is SocketException socketException) 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(); throw new OperationCanceledException();
} }
@@ -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);
} }
} }
+38 -25
View File
@@ -30,7 +30,7 @@ namespace MQTTnet.Client
internal Task _keepAliveMessageSenderTask; internal Task _keepAliveMessageSenderTask;
private IMqttChannelAdapter _adapter; private IMqttChannelAdapter _adapter;
private bool _cleanDisconnectInitiated; private bool _cleanDisconnectInitiated;
private TaskCompletionSource<bool> _disconnectReason; private long _disconnectGate;
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger) public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
{ {
@@ -55,12 +55,12 @@ namespace MQTTnet.Client
try try
{ {
_cancellationTokenSource = new CancellationTokenSource();
_disconnectReason = new TaskCompletionSource<bool>();
_options = options; _options = options;
_packetIdentifierProvider.Reset(); _packetIdentifierProvider.Reset();
_packetDispatcher.Reset(); _packetDispatcher.Reset();
_cancellationTokenSource = new CancellationTokenSource();
_disconnectGate = 0;
_adapter = _adapterFactory.CreateClientAdapter(options, _logger); _adapter = _adapterFactory.CreateClientAdapter(options, _logger);
_logger.Verbose($"Trying to connect with server ({_options.ChannelOptions})."); _logger.Verbose($"Trying to connect with server ({_options.ChannelOptions}).");
@@ -88,10 +88,12 @@ 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.");
if (_disconnectReason.TrySetException(exception))
if (!DisconnectIsPending())
{ {
await DisconnectInternalAsync(null, exception).ConfigureAwait(false); await DisconnectInternalAsync(null, exception).ConfigureAwait(false);
} }
throw; throw;
} }
} }
@@ -109,7 +111,7 @@ namespace MQTTnet.Client
} }
finally finally
{ {
if (_disconnectReason.TrySetCanceled()) if (!DisconnectIsPending())
{ {
await DisconnectInternalAsync(null, null).ConfigureAwait(false); await DisconnectInternalAsync(null, null).ConfigureAwait(false);
} }
@@ -184,10 +186,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<MqttV3ConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken) private async Task<MqttV3ConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
@@ -213,7 +217,7 @@ namespace MQTTnet.Client
private void ThrowIfNotConnected() 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) private void ThrowIfConnected(string message)
@@ -223,9 +227,10 @@ namespace MQTTnet.Client
private async Task DisconnectInternalAsync(Task sender, Exception exception) private async Task DisconnectInternalAsync(Task sender, Exception exception)
{ {
InitiateDisconnect();
var clientWasConnected = IsConnected; var clientWasConnected = IsConnected;
InitiateDisconnect();
IsConnected = false; IsConnected = false;
try try
@@ -246,10 +251,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.");
@@ -263,16 +265,16 @@ namespace MQTTnet.Client
{ {
try try
{ {
if (_cancellationTokenSource == null || _cancellationTokenSource.IsCancellationRequested) if (_cancellationTokenSource?.IsCancellationRequested == true)
{ {
return; 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.");
} }
} }
} }
@@ -360,7 +362,7 @@ 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.");
} }
if (_disconnectReason.TrySetException(exception)) if (!DisconnectIsPending())
{ {
await DisconnectInternalAsync(_keepAliveMessageSenderTask, exception).ConfigureAwait(false); await DisconnectInternalAsync(_keepAliveMessageSenderTask, exception).ConfigureAwait(false);
} }
@@ -379,8 +381,10 @@ namespace MQTTnet.Client
{ {
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false); var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken)
if (packet != null) .ConfigureAwait(false);
if (packet != null && !cancellationToken.IsCancellationRequested)
{ {
await ProcessReceivedPacketAsync(packet, cancellationToken).ConfigureAwait(false); await ProcessReceivedPacketAsync(packet, cancellationToken).ConfigureAwait(false);
} }
@@ -407,7 +411,7 @@ namespace MQTTnet.Client
_packetDispatcher.Dispatch(exception); _packetDispatcher.Dispatch(exception);
if (_disconnectReason.TrySetException(exception)) if (!DisconnectIsPending())
{ {
await DisconnectInternalAsync(_packetReceiverTask, exception).ConfigureAwait(false); await DisconnectInternalAsync(_packetReceiverTask, exception).ConfigureAwait(false);
} }
@@ -493,16 +497,20 @@ namespace MQTTnet.Client
private void StartReceivingPackets(CancellationToken cancellationToken) private void StartReceivingPackets(CancellationToken cancellationToken)
{ {
_packetReceiverTask = Task.Run( _packetReceiverTask = Task.Factory.StartNew(
() => ReceivePacketsAsync(cancellationToken), () => ReceivePacketsAsync(cancellationToken),
cancellationToken); cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
} }
private void StartSendingKeepAliveMessages(CancellationToken cancellationToken) private void StartSendingKeepAliveMessages(CancellationToken cancellationToken)
{ {
_keepAliveMessageSenderTask = Task.Run( _keepAliveMessageSenderTask = Task.Factory.StartNew(
() => SendKeepAliveMessagesAsync(cancellationToken), () => SendKeepAliveMessagesAsync(cancellationToken),
cancellationToken); cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
} }
private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket) private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket)
@@ -538,5 +546,10 @@ namespace MQTTnet.Client
{ {
} }
} }
private bool DisconnectIsPending()
{
return Interlocked.CompareExchange(ref _disconnectGate, 1, 0) != 0;
}
} }
} }
@@ -116,7 +116,7 @@ namespace MQTTnet.Implementations
return true; 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) if (!_options.TlsOptions.IgnoreCertificateRevocationErrors)
{ {
@@ -20,8 +20,8 @@ namespace MQTTnet.Implementations
private readonly CancellationToken _cancellationToken; private readonly CancellationToken _cancellationToken;
private readonly AddressFamily _addressFamily; private readonly AddressFamily _addressFamily;
private readonly MqttServerTcpEndpointBaseOptions _options; private readonly MqttServerTcpEndpointBaseOptions _options;
private readonly MqttServerTlsTcpEndpointOptions _tlsOptions;
private readonly X509Certificate2 _tlsCertificate; private readonly X509Certificate2 _tlsCertificate;
private Socket _socket; private Socket _socket;
public MqttTcpServerListener( public MqttTcpServerListener(
@@ -36,6 +36,11 @@ namespace MQTTnet.Implementations
_tlsCertificate = tlsCertificate; _tlsCertificate = tlsCertificate;
_cancellationToken = cancellationToken; _cancellationToken = cancellationToken;
_logger = logger.CreateChildLogger(nameof(MqttTcpServerListener)); _logger = logger.CreateChildLogger(nameof(MqttTcpServerListener));
if (_options is MqttServerTlsTcpEndpointOptions tlsOptions)
{
_tlsOptions = tlsOptions;
}
} }
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted; public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
@@ -75,7 +80,7 @@ namespace MQTTnet.Implementations
if (_tlsCertificate != null) if (_tlsCertificate != null)
{ {
sslStream = new SslStream(new NetworkStream(clientSocket), false); sslStream = new SslStream(new NetworkStream(clientSocket), false);
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false); await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, _tlsOptions.SslProtocol, false).ConfigureAwait(false);
} }
_logger.Verbose("Client '{0}' accepted by TCP listener '{1}, {2}'.", _logger.Verbose("Client '{0}' accepted by TCP listener '{1}, {2}'.",
+78
View File
@@ -0,0 +1,78 @@
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 RemoveFirst()
{
lock (_syncRoot)
{
var item = _items.First;
_items.RemoveFirst();
return item.Value;
}
}
public void Clear()
{
lock (_syncRoot)
{
_items.Clear();
}
}
}
}
+1 -1
View File
@@ -54,7 +54,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='uap10.0'"> <ItemGroup Condition="'$(TargetFramework)'=='uap10.0'">
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.1.5" /> <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.1.9" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net452'"> <ItemGroup Condition="'$(TargetFramework)'=='net452'">
+6
View File
@@ -16,8 +16,14 @@ namespace MQTTnet.Server
IMqttServerOptions Options { get; } IMqttServerOptions Options { get; }
[Obsolete("This method is no longer async. Use the not async method.")]
Task<IList<IMqttClientSessionStatus>> GetClientSessionsStatusAsync(); Task<IList<IMqttClientSessionStatus>> GetClientSessionsStatusAsync();
IList<IMqttClientSessionStatus> GetClientSessionsStatus();
IList<MqttApplicationMessage> GetRetainedMessages();
Task ClearRetainedMessagesAsync();
Task SubscribeAsync(string clientId, IList<TopicFilter> topicFilters); Task SubscribeAsync(string clientId, IList<TopicFilter> topicFilters);
Task UnsubscribeAsync(string clientId, IList<string> topicFilters); Task UnsubscribeAsync(string clientId, IList<string> topicFilters);
@@ -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);
} }
} }
+1 -1
View File
@@ -14,7 +14,7 @@ namespace MQTTnet.Server
Action<MqttConnectionValidatorContext> ConnectionValidator { get; } Action<MqttConnectionValidatorContext> ConnectionValidator { get; }
Action<MqttSubscriptionInterceptorContext> SubscriptionInterceptor { get; } Action<MqttSubscriptionInterceptorContext> SubscriptionInterceptor { get; }
Action<MqttApplicationMessageInterceptorContext> ApplicationMessageInterceptor { get; } Action<MqttApplicationMessageInterceptorContext> ApplicationMessageInterceptor { get; }
Action<MqttClientMessageQueueInterceptorContext> ClientMessageQueueInterceptor { get; set; } Action<MqttClientMessageQueueInterceptorContext> ClientMessageQueueInterceptor { get; }
MqttServerTcpEndpointOptions DefaultEndpointOptions { get; } MqttServerTcpEndpointOptions DefaultEndpointOptions { get; }
MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; } MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; }
+5 -1
View File
@@ -137,7 +137,9 @@ namespace MQTTnet.Server
_cleanupHandle?.Dispose(); _cleanupHandle?.Dispose();
_cleanupHandle = null; _cleanupHandle = null;
_adapter = null;
_cancellationTokenSource?.Cancel(false);
_cancellationTokenSource?.Dispose(); _cancellationTokenSource?.Dispose();
_cancellationTokenSource = null; _cancellationTokenSource = null;
} }
@@ -289,7 +291,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)
@@ -66,7 +66,7 @@ namespace MQTTnet.Server
return Task.Run(() => RunSession(clientAdapter, _cancellationToken), _cancellationToken); return Task.Run(() => RunSession(clientAdapter, _cancellationToken), _cancellationToken);
} }
public Task<IList<IMqttClientSessionStatus>> GetClientStatusAsync() public IList<IMqttClientSessionStatus> GetClientStatus()
{ {
var result = new List<IMqttClientSessionStatus>(); var result = new List<IMqttClientSessionStatus>();
@@ -78,7 +78,7 @@ namespace MQTTnet.Server
result.Add(status); result.Add(status);
} }
return Task.FromResult((IList<IMqttClientSessionStatus>)result); return result;
} }
public void EnqueueApplicationMessage(MqttClientSession senderClientSession, MqttPublishPacket publishPacket) public void EnqueueApplicationMessage(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
@@ -52,7 +52,51 @@ namespace MQTTnet.Server
try 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) catch (Exception exception)
{ {
@@ -84,52 +128,27 @@ namespace MQTTnet.Server
return retainedMessages; return retainedMessages;
} }
private async Task HandleMessageInternalAsync(string clientId, MqttApplicationMessage applicationMessage) public IList<MqttApplicationMessage> GetMessages()
{ {
var saveIsRequired = false;
lock (_messages) lock (_messages)
{ {
if (applicationMessage.Payload?.Length == 0) return _messages.Values.ToList();
{
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);
}
} }
}
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; return _options.Storage.SaveRetainedMessagesAsync(new List<MqttApplicationMessage>());
lock (_messages)
{
messages = _messages.Values.ToList();
}
await _options.Storage.SaveRetainedMessagesAsync(messages).ConfigureAwait(false);
} }
return Task.FromResult((object)null);
} }
} }
} }
+16 -1
View File
@@ -41,7 +41,17 @@ namespace MQTTnet.Server
public Task<IList<IMqttClientSessionStatus>> GetClientSessionsStatusAsync() 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) public Task SubscribeAsync(string clientId, IList<TopicFilter> topicFilters)
@@ -129,6 +139,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);
@@ -1,5 +1,6 @@
using System; using System;
using System.Net; using System.Net;
using System.Security.Authentication;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
@@ -80,6 +81,12 @@ namespace MQTTnet.Server
return this; return this;
} }
public MqttServerOptionsBuilder WithEncryptionSslProtocol(SslProtocols value)
{
_options.TlsEndpointOptions.SslProtocol = value;
return this;
}
public MqttServerOptionsBuilder WithoutEncryptedEndpoint() public MqttServerOptionsBuilder WithoutEncryptedEndpoint()
{ {
_options.TlsEndpointOptions.IsEnabled = false; _options.TlsEndpointOptions.IsEnabled = false;
@@ -1,4 +1,6 @@
namespace MQTTnet.Server using System.Security.Authentication;
namespace MQTTnet.Server
{ {
public class MqttServerTlsTcpEndpointOptions : MqttServerTcpEndpointBaseOptions public class MqttServerTlsTcpEndpointOptions : MqttServerTcpEndpointBaseOptions
{ {
@@ -8,5 +10,8 @@
} }
public byte[] Certificate { get; set; } 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> <PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
@@ -7,9 +7,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.2.1" /> <PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
<PackageReference Include="MSTest.TestFramework" Version="1.2.1" /> <PackageReference Include="MSTest.TestFramework" Version="1.3.2" />
</ItemGroup> </ItemGroup>
<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;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using MQTTnet.Diagnostics; using MQTTnet.Diagnostics;
namespace MQTTnet.Benchmarks namespace MQTTnet.Benchmarks
@@ -8,9 +8,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.10.14" /> <PackageReference Include="BenchmarkDotNet" Version="0.11.1" />
<PackageReference Include="System.IO.Pipelines" Version="4.5.0" /> <PackageReference Include="System.IO.Pipelines" Version="4.5.2" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -1,7 +1,4 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using MQTTnet.Client; using MQTTnet.Client;
using MQTTnet.Server; using MQTTnet.Server;
@@ -2,8 +2,6 @@
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Serializer; using MQTTnet.Serializer;
using MQTTnet.Internal; using MQTTnet.Internal;
using BenchmarkDotNet.Attributes.Jobs;
using BenchmarkDotNet.Attributes.Exporters;
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -1,6 +1,4 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using MQTTnet.Server; using MQTTnet.Server;
using System; using System;
@@ -0,0 +1,108 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Internal;
namespace MQTTnet.Core.Tests
{
[TestClass]
public class BlockingQueueTests
{
[TestMethod]
public void Preserve_Order()
{
var queue = new BlockingQueue<string>();
queue.Enqueue("a");
queue.Enqueue("b");
queue.Enqueue("c");
Assert.AreEqual(3, queue.Count);
Assert.AreEqual("a", queue.Dequeue());
Assert.AreEqual("b", queue.Dequeue());
Assert.AreEqual("c", queue.Dequeue());
}
[TestMethod]
public void Remove_First_Items()
{
var queue = new BlockingQueue<string>();
queue.Enqueue("a");
queue.Enqueue("b");
queue.Enqueue("c");
Assert.AreEqual("a", queue.RemoveFirst());
Assert.AreEqual("b", queue.RemoveFirst());
Assert.AreEqual(1, queue.Count);
Assert.AreEqual("c", queue.Dequeue());
}
[TestMethod]
public void Clear_Items()
{
var queue = new BlockingQueue<string>();
queue.Enqueue("a");
queue.Enqueue("b");
queue.Enqueue("c");
Assert.AreEqual(3, queue.Count);
queue.Clear();
Assert.AreEqual(0, queue.Count);
}
[TestMethod]
public async Task Wait_For_Item()
{
var queue = new BlockingQueue<string>();
string item = null;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(() =>
{
item = queue.Dequeue();
});
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
await Task.Delay(100);
Assert.AreEqual(0, queue.Count);
Assert.AreEqual(null, item);
queue.Enqueue("x");
await Task.Delay(100);
Assert.AreEqual("x", item);
Assert.AreEqual(0, queue.Count);
}
[TestMethod]
public void Wait_For_Times()
{
var number = 0;
var queue = new BlockingQueue<int>();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(async () =>
{
while (true)
{
queue.Enqueue(1);
await Task.Delay(100);
}
});
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
while (number < 50)
{
queue.Dequeue();
Interlocked.Increment(ref number);
}
}
}
}
@@ -3,16 +3,17 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<DebugType>Full</DebugType> <DebugType>Full</DebugType>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MSTest.TestAdapter" Version="1.3.2" /> <PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
<PackageReference Include="MSTest.TestFramework" 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>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj" />
<ProjectReference Include="..\..\Source\MQTTnet\MQTTnet.csproj" /> <ProjectReference Include="..\..\Source\MQTTnet\MQTTnet.csproj" />
</ItemGroup> </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);
}
}
}
+137 -11
View File
@@ -5,9 +5,11 @@ using MQTTnet.Protocol;
using MQTTnet.Server; using MQTTnet.Server;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Implementations; using MQTTnet.Implementations;
namespace MQTTnet.Core.Tests namespace MQTTnet.Core.Tests
@@ -97,11 +99,11 @@ namespace MQTTnet.Core.Tests
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++; c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithAtLeastOnceQoS().Build(); var message = new MqttApplicationMessageBuilder().WithTopic("a").WithAtLeastOnceQoS().Build();
await c2.PublishAsync(message); await c2.PublishAsync(message);
await Task.Delay(1000); await Task.Delay(1000);
Assert.AreEqual(0, receivedMessagesCount); Assert.AreEqual(0, receivedMessagesCount);
var subscribeEventCalled = false; var subscribeEventCalled = false;
s.ClientSubscribedTopic += (_, e) => s.ClientSubscribedTopic += (_, e) =>
{ {
@@ -115,7 +117,7 @@ namespace MQTTnet.Core.Tests
await c2.PublishAsync(message); await c2.PublishAsync(message);
await Task.Delay(500); await Task.Delay(500);
Assert.AreEqual(1, receivedMessagesCount); Assert.AreEqual(1, receivedMessagesCount);
var unsubscribeEventCalled = false; var unsubscribeEventCalled = false;
s.ClientUnsubscribedTopic += (_, e) => s.ClientUnsubscribedTopic += (_, e) =>
{ {
@@ -233,7 +235,7 @@ namespace MQTTnet.Core.Tests
await c1.PublishAsync(message); await c1.PublishAsync(message);
} }
} }
[TestMethod] [TestMethod]
public async Task MqttServer_ShutdownDisconnectsClientsGracefully() public async Task MqttServer_ShutdownDisconnectsClientsGracefully()
{ {
@@ -302,6 +304,59 @@ namespace MQTTnet.Core.Tests
Assert.AreEqual(clientConnectedCalled, clientDisconnectedCalled); Assert.AreEqual(clientConnectedCalled, clientDisconnectedCalled);
} }
[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] [TestMethod]
public async Task MqttServer_RetainedMessagesFlow() public async Task MqttServer_RetainedMessagesFlow()
{ {
@@ -552,6 +607,51 @@ namespace MQTTnet.Core.Tests
Assert.IsTrue(bodyIsMatching); 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] [TestMethod]
public async Task MqttServer_SameClientIdConnectDisconnectEventOrder() public async Task MqttServer_SameClientIdConnectDisconnectEventOrder()
{ {
@@ -600,6 +700,33 @@ namespace MQTTnet.Core.Tests
Assert.IsFalse(connecteCalledBeforeConnectedClients, "ClientConnected was called before ClientDisconnect was called"); Assert.IsFalse(connecteCalledBeforeConnectedClients, "ClientConnected was called before ClientDisconnect was called");
} }
[TestMethod]
public async Task MqttServer_StopAndRestart()
{
var server = new MqttFactory().CreateMqttServer();
await server.StartAsync(new MqttServerOptions());
var client = new MqttFactory().CreateMqttClient();
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
await server.StopAsync();
try
{
var client2 = new MqttFactory().CreateMqttClient();
await client2.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
Assert.Fail("Connecting should fail.");
}
catch (Exception)
{
}
await server.StartAsync(new MqttServerOptions());
var client3 = new MqttFactory().CreateMqttClient();
await client3.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
}
private class TestStorage : IMqttServerStorage private class TestStorage : IMqttServerStorage
{ {
public IList<MqttApplicationMessage> Messages = new List<MqttApplicationMessage>(); public IList<MqttApplicationMessage> Messages = new List<MqttApplicationMessage>();
@@ -623,25 +750,24 @@ namespace MQTTnet.Core.Tests
MqttQualityOfServiceLevel filterQualityOfServiceLevel, MqttQualityOfServiceLevel filterQualityOfServiceLevel,
int expectedReceivedMessagesCount) int expectedReceivedMessagesCount)
{ {
var serverAdapter = new TestMqttServerAdapter(); var s = new MqttFactory().CreateMqttServer();
var s = new MqttFactory().CreateMqttServer(new[] { serverAdapter }, new MqttNetLogger());
var receivedMessagesCount = 0; var receivedMessagesCount = 0;
try try
{ {
await s.StartAsync(new MqttServerOptions()); await s.StartAsync(new MqttServerOptions());
var c1 = await serverAdapter.ConnectTestClient("c1"); var c1 = new MqttFactory().CreateMqttClient();
var c2 = await serverAdapter.ConnectTestClient("c2");
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++; c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
await c1.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
await c1.SubscribeAsync(new TopicFilterBuilder().WithTopic(topicFilter).WithQualityOfServiceLevel(filterQualityOfServiceLevel).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 c2.PublishAsync(builder => builder.WithTopic(topic).WithPayload(new byte[0]).WithQualityOfServiceLevel(qualityOfServiceLevel));
await Task.Delay(500); await Task.Delay(500);
await c1.UnsubscribeAsync(topicFilter); await c1.UnsubscribeAsync(topicFilter);
await Task.Delay(500); await Task.Delay(500);
} }
finally finally
@@ -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 /// publishes a message with a client and waits in the server until a message with the same topic is received
/// </summary> /// </summary>
/// <returns></returns> /// <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>(); var tcs = new TaskCompletionSource<object>();
EventHandler<MqttApplicationMessageReceivedEventArgs> handler = (sender, args) => void Handler(object sender, MqttApplicationMessageReceivedEventArgs args)
{ {
if (args.ApplicationMessage.Topic == message.Topic) if (args.ApplicationMessage.Topic == message.Topic)
{ {
tcs.SetResult(true); tcs.SetResult(true);
} }
}; }
server.ApplicationMessageReceived += handler;
server.ApplicationMessageReceived += Handler;
try try
{ {
await client.PublishAsync(message).ConfigureAwait(false); await client.PublishAsync(message).ConfigureAwait(false);
@@ -31,7 +32,7 @@ namespace MQTTnet.Core.Tests
} }
finally finally
{ {
server.ApplicationMessageReceived -= handler; server.ApplicationMessageReceived -= Handler;
} }
} }
} }
@@ -10,8 +10,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore" Version="2.1.4" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" /> <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>
<ItemGroup> <ItemGroup>
@@ -3,11 +3,11 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<DebugType>Full</DebugType> <DebugType>Full</DebugType>
<TargetFrameworks>netcoreapp2.0;net452;net461</TargetFrameworks> <TargetFrameworks>netcoreapp2.1;net452;net461</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netcoreapp2.0|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netcoreapp2.1|AnyCPU'">
<DefineConstants>RELEASE;NETCOREAPP2_0</DefineConstants> <DefineConstants>RELEASE;NETCOREAPP2_1</DefineConstants>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -142,10 +142,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform"> <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.1.5</Version> <Version>6.1.9</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls"> <PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls">
<Version>3.0.0</Version> <Version>4.0.0</Version>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' "> <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">