mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-20 19:58:57 +08:00
Add interfaces, optimize performance, fix QoS level 2
This commit is contained in:
@@ -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));
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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> _unacknowledgedPublishPackets = new HashSet<ushort>();
|
||||||
private readonly HashSet<ushort> _processedPublishPackets = 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);
|
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket);
|
||||||
await SendAsync(publishPacket.CreateResponse<MqttPubCompPacket>());
|
await SendAndReceiveAsync<MqttPubCompPacket>(pubRecPacket.CreateResponse<MqttPubRelPacket>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,14 +207,12 @@ namespace MQTTnet.Core.Client
|
|||||||
return DisconnectAsync();
|
return DisconnectAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
var publishPacket = mqttPacket as MqttPublishPacket;
|
if (mqttPacket is MqttPublishPacket publishPacket)
|
||||||
if (publishPacket != null)
|
|
||||||
{
|
{
|
||||||
return ProcessReceivedPublishPacket(publishPacket);
|
return ProcessReceivedPublishPacket(publishPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pubRelPacket = mqttPacket as MqttPubRelPacket;
|
if (mqttPacket is MqttPubRelPacket pubRelPacket)
|
||||||
if (pubRelPacket != null)
|
|
||||||
{
|
{
|
||||||
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;
|
lock (_unacknowledgedPublishPackets)
|
||||||
if (!_pendingExactlyOncePublishPackets.TryRemove(pubRelPacket.PacketIdentifier, out originalPublishPacket))
|
|
||||||
{
|
{
|
||||||
throw new MqttCommunicationException();
|
_unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
await SendAsync(originalPublishPacket.CreateResponse<MqttPubCompPacket>());
|
await SendAsync(pubRelPacket.CreateResponse<MqttPubCompPacket>());
|
||||||
|
|
||||||
FireApplicationMessageReceivedEvent(originalPublishPacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 pi1 = requestPacket as IMqttPacketWithIdentifier;
|
||||||
var pi2 = p as IPacketWithIdentifier;
|
var pi2 = p as IMqttPacketWithIdentifier;
|
||||||
|
|
||||||
if (pi1 != null && pi2 != null)
|
if (pi1 != null && pi2 != null)
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -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,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,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; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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; }
|
||||||
|
|
||||||
|
|||||||
@@ -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,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; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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 (packet is MqttSubscribePacket subscribePacket)
|
||||||
if (subscribePacket != null)
|
|
||||||
{
|
{
|
||||||
return _adapter.SendPacketAsync(_subscriptionsManager.Subscribe(subscribePacket), _options.DefaultCommunicationTimeout);
|
return _adapter.SendPacketAsync(_subscriptionsManager.Subscribe(subscribePacket), _options.DefaultCommunicationTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
var unsubscribePacket = packet as MqttUnsubscribePacket;
|
if (packet is MqttUnsubscribePacket unsubscribePacket)
|
||||||
if (unsubscribePacket != null)
|
|
||||||
{
|
{
|
||||||
return _adapter.SendPacketAsync(_subscriptionsManager.Unsubscribe(unsubscribePacket), _options.DefaultCommunicationTimeout);
|
return _adapter.SendPacketAsync(_subscriptionsManager.Unsubscribe(unsubscribePacket), _options.DefaultCommunicationTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
var publishPacket = packet as MqttPublishPacket;
|
if (packet is MqttPublishPacket publishPacket)
|
||||||
if (publishPacket != null)
|
|
||||||
{
|
{
|
||||||
return HandleIncomingPublishPacketAsync(publishPacket);
|
return HandleIncomingPublishPacketAsync(publishPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pubRelPacket = packet as MqttPubRelPacket;
|
if (packet is MqttPubRelPacket pubRelPacket)
|
||||||
if (pubRelPacket != null)
|
|
||||||
{
|
{
|
||||||
return HandleIncomingPubRelPacketAsync(pubRelPacket);
|
return HandleIncomingPubRelPacketAsync(pubRelPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pubAckPacket = packet as MqttPubAckPacket;
|
if (packet is MqttPubRecPacket pubRecPacket)
|
||||||
if (pubAckPacket != null)
|
|
||||||
{
|
{
|
||||||
return HandleIncomingPubAckPacketAsync(pubAckPacket);
|
return _adapter.SendPacketAsync(pubRecPacket.CreateResponse<MqttPubRelPacket>(), _options.DefaultCommunicationTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet is MqttPubAckPacket || packet is MqttPubCompPacket)
|
||||||
|
{
|
||||||
|
// 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)
|
private Task HandleIncomingPublishPacketAsync(MqttPublishPacket publishPacket)
|
||||||
{
|
|
||||||
await Task.FromResult((object)null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async 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;
|
// QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
|
||||||
await _adapter.SendPacketAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
|
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;
|
lock (_unacknowledgedPublishPackets)
|
||||||
if (!_pendingIncomingPublications.TryRemove(pubRelPacket.PacketIdentifier, out publishPacket))
|
|
||||||
{
|
{
|
||||||
return;
|
_unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _adapter.SendPacketAsync(new MqttPubCompPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
|
return _adapter.SendPacketAsync(new MqttPubCompPacket { PacketIdentifier = pubRelPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
|
||||||
_publishPacketReceivedCallback(this, publishPacket);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
try
|
||||||
ApplicationMessageReceived?.Invoke(this, eventArgs);
|
|
||||||
|
|
||||||
foreach (var clientSession in _clientSessions.Values.ToList())
|
|
||||||
{
|
{
|
||||||
clientSession.EnqueuePublishPacket(senderClientSession, publishPacket);
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
foreach (var clientSession in _clientSessions.Values.ToList())
|
||||||
|
{
|
||||||
|
clientSession.EnqueuePublishPacket(publishPacket);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
foreach (var topicFilter in subscribePacket.TopicFilters)
|
||||||
responsePacket.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS1); // TODO: Add support for QoS 2.
|
{
|
||||||
|
_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 _;
|
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
||||||
_subscribedTopics.TryRemove(topicFilter, out _);
|
{
|
||||||
|
_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)
|
if (!MqttTopicFilterComparer.IsMatch(publishPacket.Topic, subscribedTopic.Key))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
+1
-1
@@ -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;
|
||||||
|
|
||||||
@@ -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; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user