mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-21 04:03:05 +08:00
Add server implementation
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using MQTTnet.Core.Packets;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public class MqttClientPublishPacketContext
|
||||
{
|
||||
public MqttClientPublishPacketContext(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
|
||||
{
|
||||
SenderClientSession = senderClientSession ?? throw new ArgumentNullException(nameof(senderClientSession));
|
||||
PublishPacket = publishPacket ?? throw new ArgumentNullException(nameof(publishPacket));
|
||||
}
|
||||
|
||||
public MqttClientSession SenderClientSession { get; }
|
||||
|
||||
public MqttPublishPacket PublishPacket { get; }
|
||||
|
||||
public int SendTries { get; set; }
|
||||
|
||||
public bool IsSent { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Diagnostics;
|
||||
using MQTTnet.Core.Exceptions;
|
||||
using MQTTnet.Core.Internal;
|
||||
using MQTTnet.Core.Packets;
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public class MqttClientSession
|
||||
{
|
||||
private readonly ConcurrentDictionary<ushort, MqttPublishPacket> _pendingIncomingPublications = new ConcurrentDictionary<ushort, MqttPublishPacket>();
|
||||
|
||||
private readonly MqttClientSubscriptionsManager _subscriptionsManager = new MqttClientSubscriptionsManager();
|
||||
private readonly MqttOutgoingPublicationsManager _outgoingPublicationsManager;
|
||||
private readonly Action<MqttClientSession, MqttPublishPacket> _publishPacketReceivedCallback;
|
||||
private readonly MqttServerOptions _options;
|
||||
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private IMqttCommunicationAdapter _adapter;
|
||||
private string _identifier;
|
||||
private MqttApplicationMessage _willApplicationMessage;
|
||||
|
||||
public MqttClientSession(MqttServerOptions options, Action<MqttClientSession, MqttPublishPacket> publishPacketReceivedCallback)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_publishPacketReceivedCallback = publishPacketReceivedCallback ?? throw new ArgumentNullException(nameof(publishPacketReceivedCallback));
|
||||
|
||||
_outgoingPublicationsManager = new MqttOutgoingPublicationsManager(options);
|
||||
}
|
||||
|
||||
public async Task RunAsync(string identifier, MqttApplicationMessage willApplicationMessage, IMqttCommunicationAdapter adapter)
|
||||
{
|
||||
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
|
||||
|
||||
_willApplicationMessage = willApplicationMessage;
|
||||
|
||||
try
|
||||
{
|
||||
_identifier = identifier;
|
||||
_adapter = adapter;
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
_outgoingPublicationsManager.Start(adapter);
|
||||
while (!_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
var packet = await adapter.ReceivePacketAsync(TimeSpan.Zero);
|
||||
await HandleIncomingPacketAsync(packet);
|
||||
}
|
||||
}
|
||||
catch (MqttCommunicationException)
|
||||
{
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttClientSession), exception, $"Client '{_identifier}': Unhandled exception while processing client packets.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (willApplicationMessage != null)
|
||||
{
|
||||
_publishPacketReceivedCallback(this, _willApplicationMessage.ToPublishPacket());
|
||||
}
|
||||
|
||||
_outgoingPublicationsManager.Stop();
|
||||
_cancellationTokenSource.Cancel();
|
||||
_adapter = null;
|
||||
|
||||
MqttTrace.Information(nameof(MqttClientSession), $"Client '{_identifier}': Disconnected.");
|
||||
}
|
||||
}
|
||||
|
||||
public void DispatchPublishPacket(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (senderClientSession == null) throw new ArgumentNullException(nameof(senderClientSession));
|
||||
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
||||
|
||||
if (!_subscriptionsManager.IsTopicSubscribed(publishPacket))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_outgoingPublicationsManager.Enqueue(senderClientSession, publishPacket);
|
||||
MqttTrace.Verbose(nameof(MqttClientSession), $"Client '{_identifier}: Enqueued pending publish packet.");
|
||||
}
|
||||
|
||||
private async Task HandleIncomingPacketAsync(MqttBasePacket packet)
|
||||
{
|
||||
var subscribePacket = packet as MqttSubscribePacket;
|
||||
if (subscribePacket != null)
|
||||
{
|
||||
await _adapter.SendPacketAsync(_subscriptionsManager.Subscribe(subscribePacket), _options.DefaultCommunicationTimeout);
|
||||
return;
|
||||
}
|
||||
|
||||
var unsubscribePacket = packet as MqttUnsubscribePacket;
|
||||
if (unsubscribePacket != null)
|
||||
{
|
||||
await _adapter.SendPacketAsync(_subscriptionsManager.Unsubscribe(unsubscribePacket), _options.DefaultCommunicationTimeout);
|
||||
return;
|
||||
}
|
||||
|
||||
var publishPacket = packet as MqttPublishPacket;
|
||||
if (publishPacket != null)
|
||||
{
|
||||
await HandleIncomingPublishPacketAsync(publishPacket);
|
||||
return;
|
||||
}
|
||||
|
||||
var pubRelPacket = packet as MqttPubRelPacket;
|
||||
if (pubRelPacket != null)
|
||||
{
|
||||
await HandleIncomingPubRelPacketAsync(pubRelPacket);
|
||||
return;
|
||||
}
|
||||
|
||||
var pubAckPacket = packet as MqttPubAckPacket;
|
||||
if (pubAckPacket != null)
|
||||
{
|
||||
await HandleIncomingPubAckPacketAsync(pubAckPacket);
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet is MqttPingReqPacket)
|
||||
{
|
||||
await _adapter.SendPacketAsync(new MqttPingRespPacket(), _options.DefaultCommunicationTimeout);
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet is MqttDisconnectPacket || packet is MqttConnectPacket)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
MqttTrace.Warning(nameof(MqttClientSession), $"Client '{_identifier}': Received not supported packet ({packet}). Closing connection.");
|
||||
_cancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
private async Task HandleIncomingPubAckPacketAsync(MqttPubAckPacket pubAckPacket)
|
||||
{
|
||||
await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task HandleIncomingPublishPacketAsync(MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||
{
|
||||
_publishPacketReceivedCallback(this, publishPacket);
|
||||
}
|
||||
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
{
|
||||
await _adapter.SendPacketAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
|
||||
_publishPacketReceivedCallback(this, publishPacket);
|
||||
}
|
||||
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
||||
{
|
||||
_pendingIncomingPublications[publishPacket.PacketIdentifier] = publishPacket;
|
||||
await _adapter.SendPacketAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleIncomingPubRelPacketAsync(MqttPubRelPacket pubRelPacket)
|
||||
{
|
||||
MqttPublishPacket publishPacket;
|
||||
if (!_pendingIncomingPublications.TryRemove(pubRelPacket.PacketIdentifier, out publishPacket))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _adapter.SendPacketAsync(new MqttPubCompPacket { PacketIdentifier = publishPacket.PacketIdentifier }, _options.DefaultCommunicationTimeout);
|
||||
_publishPacketReceivedCallback(this, publishPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Diagnostics;
|
||||
using MQTTnet.Core.Exceptions;
|
||||
using MQTTnet.Core.Packets;
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public class MqttClientSessionManager
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, MqttClientSession> _clientSessions = new ConcurrentDictionary<string, MqttClientSession>();
|
||||
private readonly MqttServerOptions _options;
|
||||
|
||||
public MqttClientSessionManager(MqttServerOptions options)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
public async Task RunClientSessionAsync(MqttClientConnectedEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var connectPacket = await eventArgs.ClientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout) as MqttConnectPacket;
|
||||
if (connectPacket == null)
|
||||
{
|
||||
throw new MqttProtocolViolationException("The first packet from a client must be a 'Connect' packet [MQTT-3.1.0-1].");
|
||||
}
|
||||
|
||||
var connectReturnCode = MqttConnectReturnCode.ConnectionAccepted;
|
||||
if (_options.ConnectionValidator != null)
|
||||
{
|
||||
connectReturnCode = _options.ConnectionValidator(connectPacket);
|
||||
}
|
||||
|
||||
MqttClientSession clientSession = null;
|
||||
var isSessionPresent = _clientSessions.ContainsKey(connectPacket.ClientId);
|
||||
if (isSessionPresent && connectPacket.CleanSession)
|
||||
{
|
||||
MqttClientSession _;
|
||||
_clientSessions.TryRemove(connectPacket.ClientId, out _);
|
||||
}
|
||||
else if (!connectPacket.CleanSession)
|
||||
{
|
||||
_clientSessions.TryGetValue(connectPacket.ClientId, out clientSession);
|
||||
}
|
||||
|
||||
await eventArgs.ClientAdapter.SendPacketAsync(new MqttConnAckPacket
|
||||
{
|
||||
ConnectReturnCode = connectReturnCode,
|
||||
IsSessionPresent = clientSession != null
|
||||
}, _options.DefaultCommunicationTimeout);
|
||||
|
||||
if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (clientSession == null)
|
||||
{
|
||||
clientSession = new MqttClientSession(_options, DispatchPublishPacket);
|
||||
_clientSessions.TryAdd(connectPacket.ClientId, clientSession);
|
||||
}
|
||||
|
||||
await clientSession.RunAsync(eventArgs.Identifier, connectPacket.WillMessage, eventArgs.ClientAdapter);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttServer), exception, exception.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await eventArgs.ClientAdapter.DisconnectAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_clientSessions.Clear();
|
||||
}
|
||||
|
||||
private void DispatchPublishPacket(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
|
||||
{
|
||||
foreach (var clientSession in _clientSessions.Values.ToList())
|
||||
{
|
||||
clientSession.DispatchPublishPacket(senderClientSession, publishPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using MQTTnet.Core.Packets;
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public class MqttClientSubscriptionsManager
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, MqttQualityOfServiceLevel> _subscribedTopics = new ConcurrentDictionary<string, MqttQualityOfServiceLevel>();
|
||||
|
||||
public MqttSubAckPacket Subscribe(MqttSubscribePacket subscribePacket)
|
||||
{
|
||||
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
|
||||
|
||||
var responsePacket = subscribePacket.CreateResponse<MqttSubAckPacket>();
|
||||
foreach (var topicFilter in subscribePacket.TopicFilters)
|
||||
{
|
||||
_subscribedTopics[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
|
||||
responsePacket.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS1); // TODO: Add support for QoS 2.
|
||||
}
|
||||
|
||||
return responsePacket;
|
||||
}
|
||||
|
||||
public MqttUnsubAckPacket Unsubscribe(MqttUnsubscribePacket unsubscribePacket)
|
||||
{
|
||||
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
|
||||
|
||||
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
||||
{
|
||||
MqttQualityOfServiceLevel _;
|
||||
_subscribedTopics.TryRemove(topicFilter, out _);
|
||||
}
|
||||
|
||||
return unsubscribePacket.CreateResponse<MqttUnsubAckPacket>();
|
||||
}
|
||||
|
||||
public bool IsTopicSubscribed(MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
||||
|
||||
foreach (var subscribedTopic in _subscribedTopics)
|
||||
{
|
||||
if (!MqttTopicFilterComparer.IsMatch(publishPacket.Topic, subscribedTopic.Key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (subscribedTopic.Value < publishPacket.QualityOfServiceLevel)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Diagnostics;
|
||||
using MQTTnet.Core.Exceptions;
|
||||
using MQTTnet.Core.Internal;
|
||||
using MQTTnet.Core.Packets;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public class MqttOutgoingPublicationsManager
|
||||
{
|
||||
private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false);
|
||||
private readonly List<MqttClientPublishPacketContext> _pendingPublishPackets = new List<MqttClientPublishPacketContext>();
|
||||
|
||||
private readonly MqttServerOptions _options;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private IMqttCommunicationAdapter _adapter;
|
||||
|
||||
public MqttOutgoingPublicationsManager(MqttServerOptions options)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
public void Start(IMqttCommunicationAdapter adapter)
|
||||
{
|
||||
if (_cancellationTokenSource != null)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(MqttOutgoingPublicationsManager)} already started.");
|
||||
}
|
||||
|
||||
_adapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
Task.Run(async () => await SendPendingPublishPacketsAsync(_cancellationTokenSource.Token)).Forget();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_cancellationTokenSource = null;
|
||||
}
|
||||
|
||||
public void Enqueue(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (senderClientSession == null) throw new ArgumentNullException(nameof(senderClientSession));
|
||||
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
||||
|
||||
lock (_pendingPublishPackets)
|
||||
{
|
||||
_pendingPublishPackets.Add(new MqttClientPublishPacketContext(senderClientSession, publishPacket));
|
||||
_resetEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendPendingPublishPacketsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
_resetEvent.WaitOne();
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<MqttClientPublishPacketContext> pendingPublishPackets;
|
||||
lock (_pendingPublishPackets)
|
||||
{
|
||||
pendingPublishPackets = _pendingPublishPackets.ToList();
|
||||
}
|
||||
|
||||
foreach (var publishPacket in pendingPublishPackets)
|
||||
{
|
||||
await TrySendPendingPublishPacketAsync(publishPacket);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttOutgoingPublicationsManager), e, "Error while sending pending publish packets.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TrySendPendingPublishPacketAsync(MqttClientPublishPacketContext publishPacketContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_adapter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
publishPacketContext.PublishPacket.Dup = publishPacketContext.SendTries > 0;
|
||||
await _adapter.SendPacketAsync(publishPacketContext.PublishPacket, _options.DefaultCommunicationTimeout);
|
||||
|
||||
publishPacketContext.IsSent = true;
|
||||
}
|
||||
catch (MqttCommunicationException exception)
|
||||
{
|
||||
MqttTrace.Warning(nameof(MqttOutgoingPublicationsManager), exception, "Sending publish packet failed.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttOutgoingPublicationsManager), exception, "Sending publish packet failed.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
publishPacketContext.SendTries++;
|
||||
}
|
||||
}
|
||||
|
||||
private void Cleanup()
|
||||
{
|
||||
lock (_pendingPublishPackets)
|
||||
{
|
||||
_pendingPublishPackets.RemoveAll(p => p.IsSent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Diagnostics;
|
||||
using MQTTnet.Core.Internal;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public class MqttServer
|
||||
{
|
||||
private readonly MqttClientSessionManager _clientSessionManager;
|
||||
private readonly IMqttServerAdapter _adapter;
|
||||
private readonly MqttServerOptions _options;
|
||||
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public MqttServer(MqttServerOptions options, IMqttServerAdapter adapter)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_adapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
|
||||
|
||||
_clientSessionManager = new MqttClientSessionManager(options);
|
||||
}
|
||||
|
||||
public void InjectClient(string identifier, IMqttCommunicationAdapter adapter)
|
||||
{
|
||||
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
|
||||
|
||||
OnClientConnected(this, new MqttClientConnectedEventArgs(identifier, adapter));
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_cancellationTokenSource != null)
|
||||
{
|
||||
throw new InvalidOperationException("The server is already started.");
|
||||
}
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
_adapter.ClientConnected += OnClientConnected;
|
||||
_adapter.Start(_options);
|
||||
|
||||
MqttTrace.Information(nameof(MqttServer), "Started.");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_cancellationTokenSource = null;
|
||||
|
||||
_adapter.ClientConnected -= OnClientConnected;
|
||||
_adapter.Stop();
|
||||
|
||||
_clientSessionManager.Clear();
|
||||
|
||||
MqttTrace.Information(nameof(MqttServer), "Stopped.");
|
||||
}
|
||||
|
||||
private void OnClientConnected(object sender, MqttClientConnectedEventArgs eventArgs)
|
||||
{
|
||||
MqttTrace.Information(nameof(MqttServer), $"Client '{eventArgs.Identifier}': Connected.");
|
||||
Task.Run(async () => await _clientSessionManager.RunClientSessionAsync(eventArgs), _cancellationTokenSource.Token).Forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using MQTTnet.Core.Packets;
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public class MqttServerOptions
|
||||
{
|
||||
public int Port { get; set; } = 1883;
|
||||
|
||||
public int ConnectionBacklog { get; set; } = 10;
|
||||
|
||||
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
||||
|
||||
public Func<MqttConnectPacket, MqttConnectReturnCode> ConnectionValidator { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public static class MqttTopicFilterComparer
|
||||
{
|
||||
private const char TopicLevelSeparator = '/';
|
||||
|
||||
public static bool IsMatch(string topic, string filter)
|
||||
{
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
if (filter == null) throw new ArgumentNullException(nameof(filter));
|
||||
|
||||
if (string.Equals(topic, filter, StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var fragmentsTopic = topic.Split(new[] { TopicLevelSeparator }, StringSplitOptions.None);
|
||||
var fragmentsFilter = filter.Split(new[] { TopicLevelSeparator }, StringSplitOptions.None);
|
||||
|
||||
for (var i = 0; i < fragmentsFilter.Length; i++)
|
||||
{
|
||||
if (fragmentsFilter[i] == "+")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fragmentsFilter[i] == "#" && i == fragmentsFilter.Length - 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (i >= fragmentsTopic.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.Equals(fragmentsFilter[i], fragmentsTopic[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (fragmentsTopic.Length > fragmentsFilter.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user