Browse Source

Add interfaces, optimize performance, fix QoS level 2

release/3.x.x
Christian Kratky 7 years ago
parent
commit
447ca6cabb
27 changed files with 198 additions and 147 deletions
  1. +1
    -1
      Frameworks/MQTTnet.NetFramework/MqttClientFactory.cs
  2. +1
    -1
      Frameworks/MQTTnet.NetFramework/MqttServerFactory.cs
  3. +1
    -1
      Frameworks/MQTTnet.NetStandard/MqttClientFactory.cs
  4. +1
    -1
      Frameworks/MQTTnet.NetStandard/MqttServerFactory.cs
  5. +1
    -1
      Frameworks/MQTTnet.UniversalWindows/MqttClientFactory.cs
  6. +1
    -1
      Frameworks/MQTTnet.UniversalWindows/MqttServerFactory.cs
  7. +24
    -0
      MQTTnet.Core/Client/IMqttClient.cs
  8. +21
    -26
      MQTTnet.Core/Client/MqttClient.cs
  9. +1
    -1
      MQTTnet.Core/Packets/IMqttPacketWithIdentifier.cs
  10. +1
    -20
      MQTTnet.Core/Packets/MqttBasePacket.cs
  11. +1
    -1
      MQTTnet.Core/Packets/MqttBasePublishPacket.cs
  12. +27
    -0
      MQTTnet.Core/Packets/MqttPacketExtensions.cs
  13. +1
    -1
      MQTTnet.Core/Packets/MqttSubAckPacket.cs
  14. +1
    -1
      MQTTnet.Core/Packets/MqttSubscribePacket.cs
  15. +1
    -1
      MQTTnet.Core/Packets/MqttUnsubAckPacket.cs
  16. +1
    -1
      MQTTnet.Core/Packets/MqttUnsubscribe.cs
  17. +18
    -0
      MQTTnet.Core/Server/IMqttServer.cs
  18. +3
    -4
      MQTTnet.Core/Server/MqttClientMessageQueue.cs
  19. +1
    -4
      MQTTnet.Core/Server/MqttClientPublishPacketContext.cs
  20. +35
    -33
      MQTTnet.Core/Server/MqttClientSession.cs
  21. +16
    -6
      MQTTnet.Core/Server/MqttClientSessionsManager.cs
  22. +27
    -18
      MQTTnet.Core/Server/MqttClientSubscriptionsManager.cs
  23. +9
    -1
      MQTTnet.Core/Server/MqttServer.cs
  24. +1
    -1
      MQTTnet.Core/Server/MqttServerDefaultEndpointOptions.cs
  25. +2
    -2
      MQTTnet.Core/Server/MqttServerOptions.cs
  26. +0
    -19
      MQTTnet.Core/Server/MqttServerTlsEndpointOptionsExtensions.cs
  27. +1
    -1
      Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs

+ 1
- 1
Frameworks/MQTTnet.NetFramework/MqttClientFactory.cs View File

@@ -8,7 +8,7 @@ namespace MQTTnet
{ {
public class MqttClientFactory public class MqttClientFactory
{ {
public MqttClient CreateMqttClient(MqttClientOptions options)
public IMqttClient CreateMqttClient(MqttClientOptions options)
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));




+ 1
- 1
Frameworks/MQTTnet.NetFramework/MqttServerFactory.cs View File

@@ -8,7 +8,7 @@ namespace MQTTnet
{ {
public class MqttServerFactory public class MqttServerFactory
{ {
public MqttServer CreateMqttServer(MqttServerOptions options)
public IMqttServer CreateMqttServer(MqttServerOptions options)
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));




+ 1
- 1
Frameworks/MQTTnet.NetStandard/MqttClientFactory.cs View File

@@ -8,7 +8,7 @@ namespace MQTTnet
{ {
public class MqttClientFactory public class MqttClientFactory
{ {
public MqttClient CreateMqttClient(MqttClientOptions options)
public IMqttClient CreateMqttClient(MqttClientOptions options)
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));




+ 1
- 1
Frameworks/MQTTnet.NetStandard/MqttServerFactory.cs View File

@@ -8,7 +8,7 @@ namespace MQTTnet
{ {
public class MqttServerFactory public class MqttServerFactory
{ {
public MqttServer CreateMqttServer(MqttServerOptions options)
public IMqttServer CreateMqttServer(MqttServerOptions options)
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));




+ 1
- 1
Frameworks/MQTTnet.UniversalWindows/MqttClientFactory.cs View File

@@ -8,7 +8,7 @@ namespace MQTTnet
{ {
public class MqttClientFactory public class MqttClientFactory
{ {
public MqttClient CreateMqttClient(MqttClientOptions options)
public IMqttClient CreateMqttClient(MqttClientOptions options)
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));




+ 1
- 1
Frameworks/MQTTnet.UniversalWindows/MqttServerFactory.cs View File

@@ -8,7 +8,7 @@ namespace MQTTnet
{ {
public class MqttServerFactory public class MqttServerFactory
{ {
public MqttServer CreateMqttServer(MqttServerOptions options)
public IMqttServer CreateMqttServer(MqttServerOptions options)
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));




+ 24
- 0
MQTTnet.Core/Client/IMqttClient.cs View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MQTTnet.Core.Packets;

namespace MQTTnet.Core.Client
{
public interface IMqttClient
{
bool IsConnected { get; }

event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
event EventHandler Connected;
event EventHandler Disconnected;

Task ConnectAsync(MqttApplicationMessage willApplicationMessage = null);
Task DisconnectAsync();
Task PublishAsync(MqttApplicationMessage applicationMessage);
Task<IList<MqttSubscribeResult>> SubscribeAsync(IList<TopicFilter> topicFilters);
Task<IList<MqttSubscribeResult>> SubscribeAsync(params TopicFilter[] topicFilters);
Task Unsubscribe(IList<string> topicFilters);
Task Unsubscribe(params string[] topicFilters);
}
}

+ 21
- 26
MQTTnet.Core/Client/MqttClient.cs View File

@@ -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;
@@ -13,10 +12,9 @@ using MQTTnet.Core.Protocol;


namespace MQTTnet.Core.Client namespace MQTTnet.Core.Client
{ {
public class MqttClient
public class MqttClient : IMqttClient
{ {
private readonly ConcurrentDictionary<ushort, MqttPublishPacket> _pendingExactlyOncePublishPackets = new ConcurrentDictionary<ushort, MqttPublishPacket>();
private readonly HashSet<ushort> _processedPublishPackets = new HashSet<ushort>();
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();


private readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher(); private readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
private readonly MqttClientOptions _options; private readonly MqttClientOptions _options;
@@ -63,7 +61,6 @@ namespace MQTTnet.Core.Client


_cancellationTokenSource = new CancellationTokenSource(); _cancellationTokenSource = new CancellationTokenSource();
_latestPacketIdentifier = 0; _latestPacketIdentifier = 0;
_processedPublishPackets.Clear();
_packetDispatcher.Reset(); _packetDispatcher.Reset();
IsConnected = true; IsConnected = true;


@@ -105,6 +102,7 @@ namespace MQTTnet.Core.Client
{ {
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters)); if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
if (!topicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3]."); if (!topicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");

ThrowIfNotConnected(); ThrowIfNotConnected();


var subscribePacket = new MqttSubscribePacket var subscribePacket = new MqttSubscribePacket
@@ -154,6 +152,7 @@ namespace MQTTnet.Core.Client


if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce) if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{ {
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
await SendAsync(publishPacket); await SendAsync(publishPacket);
} }
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
@@ -164,8 +163,8 @@ namespace MQTTnet.Core.Client
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{ {
publishPacket.PacketIdentifier = GetNewPacketIdentifier(); publishPacket.PacketIdentifier = GetNewPacketIdentifier();
await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket);
await SendAsync(publishPacket.CreateResponse<MqttPubCompPacket>());
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket);
await SendAndReceiveAsync<MqttPubCompPacket>(pubRecPacket.CreateResponse<MqttPubRelPacket>());
} }
} }


@@ -208,14 +207,12 @@ namespace MQTTnet.Core.Client
return DisconnectAsync(); return DisconnectAsync();
} }


var publishPacket = mqttPacket as MqttPublishPacket;
if (publishPacket != null)
if (mqttPacket is MqttPublishPacket publishPacket)
{ {
return ProcessReceivedPublishPacket(publishPacket); return ProcessReceivedPublishPacket(publishPacket);
} }


var pubRelPacket = mqttPacket as MqttPubRelPacket;
if (pubRelPacket != null)
if (mqttPacket is MqttPubRelPacket pubRelPacket)
{ {
return ProcessReceivedPubRelPacket(pubRelPacket); return ProcessReceivedPubRelPacket(pubRelPacket);
} }
@@ -232,11 +229,6 @@ namespace MQTTnet.Core.Client


private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket) private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket)
{ {
if (publishPacket.QualityOfServiceLevel != MqttQualityOfServiceLevel.AtMostOnce)
{
_processedPublishPackets.Add(publishPacket.PacketIdentifier);
}

var applicationMessage = publishPacket.ToApplicationMessage(); var applicationMessage = publishPacket.ToApplicationMessage();


try try
@@ -265,7 +257,13 @@ namespace MQTTnet.Core.Client


if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{ {
_pendingExactlyOncePublishPackets[publishPacket.PacketIdentifier] = publishPacket;
// QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
lock (_unacknowledgedPublishPackets)
{
_unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
}

FireApplicationMessageReceivedEvent(publishPacket);
return SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }); return SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier });
} }


@@ -274,15 +272,12 @@ namespace MQTTnet.Core.Client


private async Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket) private async Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket)
{ {
MqttPublishPacket originalPublishPacket;
if (!_pendingExactlyOncePublishPackets.TryRemove(pubRelPacket.PacketIdentifier, out originalPublishPacket))
lock (_unacknowledgedPublishPackets)
{ {
throw new MqttCommunicationException();
_unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
} }

await SendAsync(originalPublishPacket.CreateResponse<MqttPubCompPacket>());

FireApplicationMessageReceivedEvent(originalPublishPacket);
await SendAsync(pubRelPacket.CreateResponse<MqttPubCompPacket>());
} }


private Task SendAsync(MqttBasePacket packet) private Task SendAsync(MqttBasePacket packet)
@@ -300,8 +295,8 @@ namespace MQTTnet.Core.Client
return false; return false;
} }


var pi1 = requestPacket as IPacketWithIdentifier;
var pi2 = p as IPacketWithIdentifier;
var pi1 = requestPacket as IMqttPacketWithIdentifier;
var pi2 = p as IMqttPacketWithIdentifier;


if (pi1 != null && pi2 != null) if (pi1 != null && pi2 != null)
{ {


MQTTnet.Core/Packets/IPacketWithPacketIdentifier.cs → MQTTnet.Core/Packets/IMqttPacketWithIdentifier.cs View File

@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Core.Packets
{ {
public interface IPacketWithIdentifier
public interface IMqttPacketWithIdentifier
{ {
ushort PacketIdentifier { get; set; } ushort PacketIdentifier { get; set; }
} }

+ 1
- 20
MQTTnet.Core/Packets/MqttBasePacket.cs View File

@@ -1,25 +1,6 @@
using System;

namespace MQTTnet.Core.Packets
namespace MQTTnet.Core.Packets
{ {
public abstract class MqttBasePacket public abstract class MqttBasePacket
{ {
public TResponsePacket CreateResponse<TResponsePacket>()
{
var responsePacket = Activator.CreateInstance<TResponsePacket>();
var responsePacketWithIdentifier = responsePacket as IPacketWithIdentifier;
if (responsePacketWithIdentifier != null)
{
var requestPacketWithIdentifier = this as IPacketWithIdentifier;
if (requestPacketWithIdentifier == null)
{
throw new InvalidOperationException("Response packet has PacketIdentifier but request packet does not.");
}

responsePacketWithIdentifier.PacketIdentifier = requestPacketWithIdentifier.PacketIdentifier;
}

return responsePacket;
}
} }
} }

+ 1
- 1
MQTTnet.Core/Packets/MqttBasePublishPacket.cs View File

@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Core.Packets
{ {
public class MqttBasePublishPacket : MqttBasePacket, IPacketWithIdentifier
public class MqttBasePublishPacket : MqttBasePacket, IMqttPacketWithIdentifier
{ {
public ushort PacketIdentifier { get; set; } public ushort PacketIdentifier { get; set; }
} }


+ 27
- 0
MQTTnet.Core/Packets/MqttPacketExtensions.cs View File

@@ -0,0 +1,27 @@
using System;

namespace MQTTnet.Core.Packets
{
public static class MqttPacketExtensions
{
public static TResponsePacket CreateResponse<TResponsePacket>(this MqttBasePacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));

var responsePacket = Activator.CreateInstance<TResponsePacket>();

if (responsePacket is IMqttPacketWithIdentifier responsePacketWithIdentifier)
{
var requestPacketWithIdentifier = packet as IMqttPacketWithIdentifier;
if (requestPacketWithIdentifier == null)
{
throw new InvalidOperationException("Response packet has PacketIdentifier but request packet does not.");
}

responsePacketWithIdentifier.PacketIdentifier = requestPacketWithIdentifier.PacketIdentifier;
}

return responsePacket;
}
}
}

+ 1
- 1
MQTTnet.Core/Packets/MqttSubAckPacket.cs View File

@@ -4,7 +4,7 @@ using MQTTnet.Core.Protocol;


namespace MQTTnet.Core.Packets namespace MQTTnet.Core.Packets
{ {
public sealed class MqttSubAckPacket : MqttBasePacket, IPacketWithIdentifier
public sealed class MqttSubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
{ {
public ushort PacketIdentifier { get; set; } public ushort PacketIdentifier { get; set; }




+ 1
- 1
MQTTnet.Core/Packets/MqttSubscribePacket.cs View File

@@ -3,7 +3,7 @@ using System.Linq;


namespace MQTTnet.Core.Packets namespace MQTTnet.Core.Packets
{ {
public sealed class MqttSubscribePacket : MqttBasePacket, IPacketWithIdentifier
public sealed class MqttSubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
{ {
public ushort PacketIdentifier { get; set; } public ushort PacketIdentifier { get; set; }


+ 1
- 1
MQTTnet.Core/Packets/MqttUnsubAckPacket.cs View File

@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Packets namespace MQTTnet.Core.Packets
{ {
public sealed class MqttUnsubAckPacket : MqttBasePacket, IPacketWithIdentifier
public sealed class MqttUnsubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
{ {
public ushort PacketIdentifier { get; set; } public ushort PacketIdentifier { get; set; }
} }


+ 1
- 1
MQTTnet.Core/Packets/MqttUnsubscribe.cs View File

@@ -2,7 +2,7 @@


namespace MQTTnet.Core.Packets namespace MQTTnet.Core.Packets
{ {
public sealed class MqttUnsubscribePacket : MqttBasePacket, IPacketWithIdentifier
public sealed class MqttUnsubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
{ {
public ushort PacketIdentifier { get; set; } public ushort PacketIdentifier { get; set; }


+ 18
- 0
MQTTnet.Core/Server/IMqttServer.cs View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using MQTTnet.Core.Adapter;

namespace MQTTnet.Core.Server
{
public interface IMqttServer
{
event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
event EventHandler<MqttClientConnectedEventArgs> ClientConnected;

IList<string> GetConnectedClients();
void InjectClient(string identifier, IMqttCommunicationAdapter adapter);
void Publish(MqttApplicationMessage applicationMessage);
void Start();
void Stop();
}
}

+ 3
- 4
MQTTnet.Core/Server/MqttClientMessageQueue.cs View File

@@ -35,7 +35,7 @@ namespace MQTTnet.Core.Server
_adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); _adapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
_cancellationTokenSource = new CancellationTokenSource(); _cancellationTokenSource = new CancellationTokenSource();


Task.Run(() => SendPendingPublishPacketsAsync(_cancellationTokenSource.Token));
Task.Run(() => SendPendingPublishPacketsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
} }


public void Stop() public void Stop()
@@ -45,14 +45,13 @@ namespace MQTTnet.Core.Server
_cancellationTokenSource = null; _cancellationTokenSource = null;
} }


public void Enqueue(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
public void Enqueue(MqttPublishPacket publishPacket)
{ {
if (senderClientSession == null) throw new ArgumentNullException(nameof(senderClientSession));
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket)); if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));


lock (_pendingPublishPackets) lock (_pendingPublishPackets)
{ {
_pendingPublishPackets.Add(new MqttClientPublishPacketContext(senderClientSession, publishPacket));
_pendingPublishPackets.Add(new MqttClientPublishPacketContext(publishPacket));
_gate.Set(); _gate.Set();
} }
} }


+ 1
- 4
MQTTnet.Core/Server/MqttClientPublishPacketContext.cs View File

@@ -5,14 +5,11 @@ namespace MQTTnet.Core.Server
{ {
public sealed class MqttClientPublishPacketContext public sealed class MqttClientPublishPacketContext
{ {
public MqttClientPublishPacketContext(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
public MqttClientPublishPacketContext(MqttPublishPacket publishPacket)
{ {
SenderClientSession = senderClientSession ?? throw new ArgumentNullException(nameof(senderClientSession));
PublishPacket = publishPacket ?? throw new ArgumentNullException(nameof(publishPacket)); PublishPacket = publishPacket ?? throw new ArgumentNullException(nameof(publishPacket));
} }


public MqttClientSession SenderClientSession { get; }

public MqttPublishPacket PublishPacket { get; } public MqttPublishPacket PublishPacket { get; }


public int SendTries { get; set; } public int SendTries { get; set; }


+ 35
- 33
MQTTnet.Core/Server/MqttClientSession.cs View File

@@ -1,5 +1,5 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Adapter; using MQTTnet.Core.Adapter;
@@ -13,13 +13,13 @@ namespace MQTTnet.Core.Server
{ {
public sealed class MqttClientSession : IDisposable public sealed class MqttClientSession : IDisposable
{ {
private readonly ConcurrentDictionary<ushort, MqttPublishPacket> _pendingIncomingPublications = new ConcurrentDictionary<ushort, MqttPublishPacket>();
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();


private readonly MqttClientSubscriptionsManager _subscriptionsManager = new MqttClientSubscriptionsManager(); private readonly MqttClientSubscriptionsManager _subscriptionsManager = new MqttClientSubscriptionsManager();
private readonly MqttClientMessageQueue _messageQueue; private readonly MqttClientMessageQueue _messageQueue;
private readonly Action<MqttClientSession, MqttPublishPacket> _publishPacketReceivedCallback; private readonly Action<MqttClientSession, MqttPublishPacket> _publishPacketReceivedCallback;
private readonly MqttServerOptions _options; private readonly MqttServerOptions _options;
private CancellationTokenSource _cancellationTokenSource; private CancellationTokenSource _cancellationTokenSource;
private IMqttCommunicationAdapter _adapter; private IMqttCommunicationAdapter _adapter;
private string _identifier; private string _identifier;
@@ -79,17 +79,16 @@ namespace MQTTnet.Core.Server
} }
} }


public void EnqueuePublishPacket(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
public void EnqueuePublishPacket(MqttPublishPacket publishPacket)
{ {
if (senderClientSession == null) throw new ArgumentNullException(nameof(senderClientSession));
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket)); if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));


if (!_subscriptionsManager.IsTopicSubscribed(publishPacket))
if (!_subscriptionsManager.IsSubscribed(publishPacket))
{ {
return; return;
} }


_messageQueue.Enqueue(senderClientSession, publishPacket);
_messageQueue.Enqueue(publishPacket);
MqttTrace.Verbose(nameof(MqttClientSession), $"Client '{_identifier}: Enqueued pending publish packet."); MqttTrace.Verbose(nameof(MqttClientSession), $"Client '{_identifier}: Enqueued pending publish packet.");
} }


@@ -101,34 +100,35 @@ namespace MQTTnet.Core.Server


private Task HandleIncomingPacketAsync(MqttBasePacket packet) private Task HandleIncomingPacketAsync(MqttBasePacket packet)
{ {
var subscribePacket = packet as MqttSubscribePacket;
if (subscribePacket != null)
if (packet is MqttSubscribePacket subscribePacket)
{ {
return _adapter.SendPacketAsync(_subscriptionsManager.Subscribe(subscribePacket), _options.DefaultCommunicationTimeout); return _adapter.SendPacketAsync(_subscriptionsManager.Subscribe(subscribePacket), _options.DefaultCommunicationTimeout);
} }


var unsubscribePacket = packet as MqttUnsubscribePacket;
if (unsubscribePacket != null)
if (packet is MqttUnsubscribePacket unsubscribePacket)
{ {
return _adapter.SendPacketAsync(_subscriptionsManager.Unsubscribe(unsubscribePacket), _options.DefaultCommunicationTimeout); return _adapter.SendPacketAsync(_subscriptionsManager.Unsubscribe(unsubscribePacket), _options.DefaultCommunicationTimeout);
} }


var publishPacket = packet as MqttPublishPacket;
if (publishPacket != null)
if (packet is MqttPublishPacket publishPacket)
{ {
return HandleIncomingPublishPacketAsync(publishPacket); return HandleIncomingPublishPacketAsync(publishPacket);
} }


var pubRelPacket = packet as MqttPubRelPacket;
if (pubRelPacket != null)
if (packet is MqttPubRelPacket pubRelPacket)
{ {
return HandleIncomingPubRelPacketAsync(pubRelPacket); return HandleIncomingPubRelPacketAsync(pubRelPacket);
} }


var pubAckPacket = packet as MqttPubAckPacket;
if (pubAckPacket != null)
if (packet is MqttPubRecPacket pubRecPacket)
{
return _adapter.SendPacketAsync(pubRecPacket.CreateResponse<MqttPubRelPacket>(), _options.DefaultCommunicationTimeout);
}

if (packet is MqttPubAckPacket || packet is MqttPubCompPacket)
{ {
return HandleIncomingPubAckPacketAsync(pubAckPacket);
// Discard message.
return Task.FromResult((object)null);
} }


if (packet is MqttPingReqPacket) if (packet is MqttPingReqPacket)
@@ -148,12 +148,7 @@ namespace MQTTnet.Core.Server
return Task.FromResult((object)null); return Task.FromResult((object)null);
} }


private async Task HandleIncomingPubAckPacketAsync(MqttPubAckPacket pubAckPacket)
{
await Task.FromResult((object)null);
}

private async Task HandleIncomingPublishPacketAsync(MqttPublishPacket publishPacket)
private Task HandleIncomingPublishPacketAsync(MqttPublishPacket publishPacket)
{ {
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce) if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{ {
@@ -161,26 +156,33 @@ namespace MQTTnet.Core.Server
} }
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{ {
await _adapter.SendPacketAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
_publishPacketReceivedCallback(this, publishPacket); _publishPacketReceivedCallback(this, publishPacket);
return _adapter.SendPacketAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
} }
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{ {
_pendingIncomingPublications[publishPacket.PacketIdentifier] = publishPacket;
await _adapter.SendPacketAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
// QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
lock (_unacknowledgedPublishPackets)
{
_unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
}

_publishPacketReceivedCallback(this, publishPacket);

return _adapter.SendPacketAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
} }

throw new MqttCommunicationException("Received not supported QoS level.");
} }


private async Task HandleIncomingPubRelPacketAsync(MqttPubRelPacket pubRelPacket)
private Task HandleIncomingPubRelPacketAsync(MqttPubRelPacket pubRelPacket)
{ {
MqttPublishPacket publishPacket;
if (!_pendingIncomingPublications.TryRemove(pubRelPacket.PacketIdentifier, out publishPacket))
lock (_unacknowledgedPublishPackets)
{ {
return;
_unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
} }


await _adapter.SendPacketAsync(new MqttPubCompPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
_publishPacketReceivedCallback(this, publishPacket);
return _adapter.SendPacketAsync(new MqttPubCompPacket { PacketIdentifier = pubRelPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
} }
} }
} }

+ 16
- 6
MQTTnet.Core/Server/MqttClientSessionsManager.cs View File

@@ -22,7 +22,7 @@ namespace MQTTnet.Core.Server
_options = options ?? throw new ArgumentNullException(nameof(options)); _options = options ?? throw new ArgumentNullException(nameof(options));
} }


public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;


public async Task RunClientSessionAsync(MqttClientConnectedEventArgs eventArgs) public async Task RunClientSessionAsync(MqttClientConnectedEventArgs eventArgs)
{ {
@@ -127,14 +127,24 @@ namespace MQTTnet.Core.Server
} }
} }


private void DispatchPublishPacket(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
public void DispatchPublishPacket(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
{ {
var eventArgs = new MqttApplicationMessageReceivedEventArgs(senderClientSession.ClientId, publishPacket.ToApplicationMessage());
ApplicationMessageReceived?.Invoke(this, eventArgs);
try
{
var eventArgs = new MqttApplicationMessageReceivedEventArgs(senderClientSession?.ClientId, publishPacket.ToApplicationMessage());
ApplicationMessageReceived?.Invoke(this, eventArgs);
}
catch (Exception exception)
{
MqttTrace.Error(nameof(MqttClientSessionsManager), exception, "Error while processing application message");
}


foreach (var clientSession in _clientSessions.Values.ToList())
lock (_syncRoot)
{ {
clientSession.EnqueuePublishPacket(senderClientSession, publishPacket);
foreach (var clientSession in _clientSessions.Values.ToList())
{
clientSession.EnqueuePublishPacket(publishPacket);
}
} }
} }
} }

+ 27
- 18
MQTTnet.Core/Server/MqttClientSubscriptionsManager.cs View File

@@ -1,5 +1,5 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using MQTTnet.Core.Packets; using MQTTnet.Core.Packets;
using MQTTnet.Core.Protocol; using MQTTnet.Core.Protocol;


@@ -7,17 +7,21 @@ namespace MQTTnet.Core.Server
{ {
public sealed class MqttClientSubscriptionsManager public sealed class MqttClientSubscriptionsManager
{ {
private readonly ConcurrentDictionary<string, MqttQualityOfServiceLevel> _subscribedTopics = new ConcurrentDictionary<string, MqttQualityOfServiceLevel>();
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscribedTopics = new Dictionary<string, MqttQualityOfServiceLevel>();


public MqttSubAckPacket Subscribe(MqttSubscribePacket subscribePacket) public MqttSubAckPacket Subscribe(MqttSubscribePacket subscribePacket)
{ {
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket)); if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));


var responsePacket = subscribePacket.CreateResponse<MqttSubAckPacket>(); var responsePacket = subscribePacket.CreateResponse<MqttSubAckPacket>();
foreach (var topicFilter in subscribePacket.TopicFilters)

lock (_subscribedTopics)
{ {
_subscribedTopics[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
responsePacket.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS1); // TODO: Add support for QoS 2.
foreach (var topicFilter in subscribePacket.TopicFilters)
{
_subscribedTopics[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
responsePacket.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS1); // TODO: Add support for QoS 2.
}
} }


return responsePacket; return responsePacket;
@@ -27,32 +31,37 @@ namespace MQTTnet.Core.Server
{ {
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket)); if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));


foreach (var topicFilter in unsubscribePacket.TopicFilters)
lock (_subscribedTopics)
{ {
MqttQualityOfServiceLevel _;
_subscribedTopics.TryRemove(topicFilter, out _);
foreach (var topicFilter in unsubscribePacket.TopicFilters)
{
_subscribedTopics.Remove(topicFilter);
}
} }


return unsubscribePacket.CreateResponse<MqttUnsubAckPacket>(); return unsubscribePacket.CreateResponse<MqttUnsubAckPacket>();
} }


public bool IsTopicSubscribed(MqttPublishPacket publishPacket)
public bool IsSubscribed(MqttPublishPacket publishPacket)
{ {
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket)); if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));


foreach (var subscribedTopic in _subscribedTopics)
lock (_subscribedTopics)
{ {
if (!MqttTopicFilterComparer.IsMatch(publishPacket.Topic, subscribedTopic.Key))
foreach (var subscribedTopic in _subscribedTopics)
{ {
continue;
}
if (publishPacket.QualityOfServiceLevel > subscribedTopic.Value)
{
continue;
}


if (subscribedTopic.Value < publishPacket.QualityOfServiceLevel)
{
continue;
}
if (!MqttTopicFilterComparer.IsMatch(publishPacket.Topic, subscribedTopic.Key))
{
continue;
}


return true;
return true;
}
} }


return false; return false;


+ 9
- 1
MQTTnet.Core/Server/MqttServer.cs View File

@@ -4,10 +4,11 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Core.Adapter; using MQTTnet.Core.Adapter;
using MQTTnet.Core.Diagnostics; using MQTTnet.Core.Diagnostics;
using MQTTnet.Core.Internal;


namespace MQTTnet.Core.Server namespace MQTTnet.Core.Server
{ {
public sealed class MqttServer
public sealed class MqttServer : IMqttServer
{ {
private readonly MqttClientSessionsManager _clientSessionsManager; private readonly MqttClientSessionsManager _clientSessionsManager;
private readonly ICollection<IMqttServerAdapter> _adapters; private readonly ICollection<IMqttServerAdapter> _adapters;
@@ -33,6 +34,13 @@ namespace MQTTnet.Core.Server


public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived; public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;


public void Publish(MqttApplicationMessage applicationMessage)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));

_clientSessionsManager.DispatchPublishPacket(null, applicationMessage.ToPublishPacket());
}

public void InjectClient(string identifier, IMqttCommunicationAdapter adapter) public void InjectClient(string identifier, IMqttCommunicationAdapter adapter)
{ {
if (adapter == null) throw new ArgumentNullException(nameof(adapter)); if (adapter == null) throw new ArgumentNullException(nameof(adapter));


MQTTnet.Core/Server/DefaultEndpointOptions.cs → MQTTnet.Core/Server/MqttServerDefaultEndpointOptions.cs View File

@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Server namespace MQTTnet.Core.Server
{ {
public sealed class DefaultEndpointOptions
public sealed class MqttServerDefaultEndpointOptions
{ {
public bool IsEnabled { get; set; } = true; public bool IsEnabled { get; set; } = true;



+ 2
- 2
MQTTnet.Core/Server/MqttServerOptions.cs View File

@@ -6,13 +6,13 @@ namespace MQTTnet.Core.Server
{ {
public sealed class MqttServerOptions public sealed class MqttServerOptions
{ {
public DefaultEndpointOptions DefaultEndpointOptions { get; } = new DefaultEndpointOptions();
public MqttServerDefaultEndpointOptions DefaultEndpointOptions { get; } = new MqttServerDefaultEndpointOptions();


public MqttServerTlsEndpointOptions TlsEndpointOptions { get; } = new MqttServerTlsEndpointOptions(); public MqttServerTlsEndpointOptions TlsEndpointOptions { get; } = new MqttServerTlsEndpointOptions();
public int ConnectionBacklog { get; set; } = 10; public int ConnectionBacklog { get; set; } = 10;


public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10);
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(15);


public Func<MqttConnectPacket, MqttConnectReturnCode> ConnectionValidator { get; set; } public Func<MqttConnectPacket, MqttConnectReturnCode> ConnectionValidator { get; set; }
} }


+ 0
- 19
MQTTnet.Core/Server/MqttServerTlsEndpointOptionsExtensions.cs View File

@@ -1,19 +0,0 @@
using System;

namespace MQTTnet.Core.Server
{
public static class MqttServerTlsEndpointOptionsExtensions
{
public static int GetPort(this DefaultEndpointOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));

if (!options.Port.HasValue)
{
return 1883;
}

return options.Port.Value;
}
}
}

+ 1
- 1
Tests/MQTTnet.TestApp.UniversalWindows/MainPage.xaml.cs View File

@@ -8,7 +8,7 @@ namespace MQTTnet.TestApp.UniversalWindows
{ {
public sealed partial class MainPage public sealed partial class MainPage
{ {
private MqttClient _mqttClient;
private IMqttClient _mqttClient;


public MainPage() public MainPage()
{ {


Loading…
Cancel
Save