mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-26 16:24:10 +08:00
Merge branch 'master' into Managed-Client-V2
This commit is contained in:
@@ -5,3 +5,4 @@
|
|||||||
* [Server] Exposed MQTT v5 sent properties from the affected client in _ClientDisconnectedAsync_ event.
|
* [Server] Exposed MQTT v5 sent properties from the affected client in _ClientDisconnectedAsync_ event.
|
||||||
* [Server] Fixed wrong client ID passed to _InterceptingUnsubscriptionEventArgs_ (#1631, thanks to @ghord).
|
* [Server] Fixed wrong client ID passed to _InterceptingUnsubscriptionEventArgs_ (#1631, thanks to @ghord).
|
||||||
* [Server] Exposed socket settings for TCP keep alive in TCP options (#1544).
|
* [Server] Exposed socket settings for TCP keep alive in TCP options (#1544).
|
||||||
|
* [Server] Exposed server session items at server level and allow custom session items for injected application messages (#1638).
|
||||||
|
|||||||
@@ -33,8 +33,7 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
readonly AsyncLock _syncRoot = new AsyncLock();
|
readonly AsyncLock _syncRoot = new AsyncLock();
|
||||||
|
|
||||||
long _bytesReceived;
|
Statistics _statistics; // mutable struct, don't make readonly!
|
||||||
long _bytesSent;
|
|
||||||
|
|
||||||
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, MqttPacketInspector packetInspector, IMqttNetLogger logger)
|
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, MqttPacketInspector packetInspector, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
@@ -51,9 +50,9 @@ namespace MQTTnet.Adapter
|
|||||||
_logger = logger.WithSource(nameof(MqttChannelAdapter));
|
_logger = logger.WithSource(nameof(MqttChannelAdapter));
|
||||||
}
|
}
|
||||||
|
|
||||||
public long BytesReceived => Interlocked.Read(ref _bytesReceived);
|
public long BytesReceived => Volatile.Read(ref _statistics._bytesReceived);
|
||||||
|
|
||||||
public long BytesSent => Interlocked.Read(ref _bytesSent);
|
public long BytesSent => Volatile.Read(ref _statistics._bytesSent);
|
||||||
|
|
||||||
public X509Certificate2 ClientCertificate => _channel.ClientCertificate;
|
public X509Certificate2 ClientCertificate => _channel.ClientCertificate;
|
||||||
|
|
||||||
@@ -147,7 +146,7 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
_packetInspector?.EndReceivePacket();
|
_packetInspector?.EndReceivePacket();
|
||||||
|
|
||||||
Interlocked.Add(ref _bytesSent, receivedPacket.TotalLength);
|
Interlocked.Add(ref _statistics._bytesSent, receivedPacket.TotalLength);
|
||||||
|
|
||||||
if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown)
|
if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown)
|
||||||
{
|
{
|
||||||
@@ -181,11 +180,7 @@ namespace MQTTnet.Adapter
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetStatistics()
|
public void ResetStatistics() => _statistics.Reset();
|
||||||
{
|
|
||||||
Interlocked.Exchange(ref _bytesReceived, 0L);
|
|
||||||
Interlocked.Exchange(ref _bytesSent, 0L);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -216,7 +211,7 @@ namespace MQTTnet.Adapter
|
|||||||
await _channel.WriteAsync(packetBuffer.Packet, true, cancellationToken).ConfigureAwait(false);
|
await _channel.WriteAsync(packetBuffer.Packet, true, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Interlocked.Add(ref _bytesReceived, packetBuffer.Length);
|
Interlocked.Add(ref _statistics._bytesReceived, packetBuffer.Length);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
@@ -459,5 +454,17 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
throw new MqttCommunicationException(exception);
|
throw new MqttCommunicationException(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct Statistics
|
||||||
|
{
|
||||||
|
public long _bytesReceived;
|
||||||
|
public long _bytesSent;
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
Volatile.Write(ref _bytesReceived, 0);
|
||||||
|
Volatile.Write(ref _bytesSent, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,12 +82,7 @@ namespace MQTTnet.Adapter
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var eventArgs = new InspectMqttPacketEventArgs
|
var eventArgs = new InspectMqttPacketEventArgs(direction, buffer);
|
||||||
{
|
|
||||||
Buffer = buffer,
|
|
||||||
Direction = direction
|
|
||||||
};
|
|
||||||
|
|
||||||
_asyncEvent.InvokeAsync(eventArgs).GetAwaiter().GetResult();
|
_asyncEvent.InvokeAsync(eventArgs).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
|
|||||||
@@ -9,9 +9,18 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
public sealed class MqttClientPublishResult
|
public sealed class MqttClientPublishResult
|
||||||
{
|
{
|
||||||
|
public MqttClientPublishResult(ushort? packetIdentifier, MqttClientPublishReasonCode reasonCode, string reasonString, IReadOnlyCollection<MqttUserProperty> userProperties)
|
||||||
|
{
|
||||||
|
PacketIdentifier = packetIdentifier;
|
||||||
|
ReasonCode = reasonCode;
|
||||||
|
ReasonString = reasonString;
|
||||||
|
UserProperties = userProperties;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns if the overall status of the publish is a success. This can be the reason code _Success_ or
|
/// Returns if the overall status of the publish is a success. This can be the reason code _Success_ or
|
||||||
/// _NoMatchingSubscribers_. _NoMatchingSubscribers_ usually indicates only that no other client is interested in the topic but overall transmit
|
/// _NoMatchingSubscribers_. _NoMatchingSubscribers_ usually indicates only that no other client is interested in the
|
||||||
|
/// topic but overall transmit
|
||||||
/// to the server etc. was a success.
|
/// to the server etc. was a success.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsSuccess => ReasonCode == MqttClientPublishReasonCode.Success || ReasonCode == MqttClientPublishReasonCode.NoMatchingSubscribers;
|
public bool IsSuccess => ReasonCode == MqttClientPublishReasonCode.Success || ReasonCode == MqttClientPublishReasonCode.NoMatchingSubscribers;
|
||||||
@@ -19,19 +28,19 @@ namespace MQTTnet.Client
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the packet identifier which was used for this publish.
|
/// Gets the packet identifier which was used for this publish.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort? PacketIdentifier { get; set; }
|
public ushort? PacketIdentifier { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the reason code.
|
/// Gets or sets the reason code.
|
||||||
/// Hint: MQTT 5 feature only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MqttClientPublishReasonCode ReasonCode { get; set; } = MqttClientPublishReasonCode.Success;
|
public MqttClientPublishReasonCode ReasonCode { get; } = MqttClientPublishReasonCode.Success;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the reason string.
|
/// Gets or sets the reason string.
|
||||||
/// Hint: MQTT 5 feature only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ReasonString { get; set; }
|
public string ReasonString { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the user properties.
|
/// Gets or sets the user properties.
|
||||||
@@ -40,8 +49,8 @@ namespace MQTTnet.Client
|
|||||||
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add
|
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add
|
||||||
/// metadata to MQTT messages and pass information between publisher, broker, and subscriber.
|
/// metadata to MQTT messages and pass information between publisher, broker, and subscriber.
|
||||||
/// The feature is very similar to the HTTP header concept.
|
/// The feature is very similar to the HTTP header concept.
|
||||||
/// Hint: MQTT 5 feature only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; internal set; }
|
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,25 +10,23 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
public sealed class MqttClientPublishResultFactory
|
public sealed class MqttClientPublishResultFactory
|
||||||
{
|
{
|
||||||
static readonly MqttClientPublishResult EmptySuccessResult = new MqttClientPublishResult();
|
|
||||||
static readonly IReadOnlyCollection<MqttUserProperty> EmptyUserProperties = new List<MqttUserProperty>();
|
static readonly IReadOnlyCollection<MqttUserProperty> EmptyUserProperties = new List<MqttUserProperty>();
|
||||||
|
static readonly MqttClientPublishResult AtMostOnceSuccessResult = new MqttClientPublishResult(null, MqttClientPublishReasonCode.Success, null, EmptyUserProperties);
|
||||||
|
|
||||||
public MqttClientPublishResult Create(MqttPubAckPacket pubAckPacket)
|
public MqttClientPublishResult Create(MqttPubAckPacket pubAckPacket)
|
||||||
{
|
{
|
||||||
// QoS 0 has no response. So we treat it as a success always.
|
// QoS 0 has no response. So we treat it as a success always.
|
||||||
if (pubAckPacket == null)
|
if (pubAckPacket == null)
|
||||||
{
|
{
|
||||||
return EmptySuccessResult;
|
return AtMostOnceSuccessResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = new MqttClientPublishResult
|
var result = new MqttClientPublishResult(
|
||||||
{
|
pubAckPacket.PacketIdentifier,
|
||||||
// Both enums have the same values. So it can be easily converted.
|
// Both enums have the same values. So it can be easily converted.
|
||||||
ReasonCode = (MqttClientPublishReasonCode)(int)pubAckPacket.ReasonCode,
|
(MqttClientPublishReasonCode)(int)pubAckPacket.ReasonCode,
|
||||||
PacketIdentifier = pubAckPacket.PacketIdentifier,
|
pubAckPacket.ReasonString,
|
||||||
ReasonString = pubAckPacket.ReasonString,
|
pubAckPacket.UserProperties ?? EmptyUserProperties);
|
||||||
UserProperties = pubAckPacket.UserProperties ?? EmptyUserProperties
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -37,43 +35,28 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
if (pubRecPacket == null || pubCompPacket == null)
|
if (pubRecPacket == null || pubCompPacket == null)
|
||||||
{
|
{
|
||||||
return new MqttClientPublishResult
|
var packetIdentifier = pubRecPacket?.PacketIdentifier ?? pubCompPacket?.PacketIdentifier;
|
||||||
{
|
return new MqttClientPublishResult(packetIdentifier, MqttClientPublishReasonCode.ImplementationSpecificError, null, EmptyUserProperties);
|
||||||
ReasonCode = MqttClientPublishReasonCode.UnspecifiedError
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttClientPublishResult result;
|
|
||||||
|
|
||||||
// The PUBCOMP is the last packet in QoS 2. So we use the results from that instead of PUBREC.
|
// The PUBCOMP is the last packet in QoS 2. So we use the results from that instead of PUBREC.
|
||||||
if (pubCompPacket.ReasonCode == MqttPubCompReasonCode.PacketIdentifierNotFound)
|
if (pubCompPacket.ReasonCode == MqttPubCompReasonCode.PacketIdentifierNotFound)
|
||||||
{
|
{
|
||||||
result = new MqttClientPublishResult
|
return new MqttClientPublishResult(
|
||||||
{
|
pubCompPacket.PacketIdentifier,
|
||||||
PacketIdentifier = pubCompPacket.PacketIdentifier,
|
MqttClientPublishReasonCode.UnspecifiedError,
|
||||||
ReasonCode = MqttClientPublishReasonCode.UnspecifiedError,
|
pubCompPacket.ReasonString,
|
||||||
ReasonString = pubCompPacket.ReasonString,
|
pubCompPacket.UserProperties ?? EmptyUserProperties);
|
||||||
UserProperties = pubCompPacket.UserProperties ?? EmptyUserProperties
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = new MqttClientPublishResult
|
var reasonCode = MqttClientPublishReasonCode.Success;
|
||||||
{
|
|
||||||
PacketIdentifier = pubCompPacket.PacketIdentifier,
|
|
||||||
ReasonCode = MqttClientPublishReasonCode.Success,
|
|
||||||
ReasonString = pubCompPacket.ReasonString,
|
|
||||||
UserProperties = pubCompPacket.UserProperties ?? EmptyUserProperties
|
|
||||||
};
|
|
||||||
|
|
||||||
if (pubRecPacket.ReasonCode != MqttPubRecReasonCode.Success)
|
if (pubRecPacket.ReasonCode != MqttPubRecReasonCode.Success)
|
||||||
{
|
{
|
||||||
// Both enums share the same values.
|
// Both enums share the same values.
|
||||||
result.ReasonCode = (MqttClientPublishReasonCode)pubRecPacket.ReasonCode;
|
reasonCode = (MqttClientPublishReasonCode)pubRecPacket.ReasonCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return new MqttClientPublishResult(pubCompPacket.PacketIdentifier, reasonCode, null, pubCompPacket.UserProperties ?? EmptyUserProperties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
|
|
||||||
@@ -9,23 +10,35 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
public sealed class MqttClientSubscribeResult
|
public sealed class MqttClientSubscribeResult
|
||||||
{
|
{
|
||||||
public IReadOnlyCollection<MqttClientSubscribeResultItem> Items { get; internal set; }
|
public MqttClientSubscribeResult(ushort packetIdentifier, IReadOnlyCollection<MqttClientSubscribeResultItem> items, string reasonString, IReadOnlyCollection<MqttUserProperty> userProperties)
|
||||||
|
{
|
||||||
|
PacketIdentifier = packetIdentifier;
|
||||||
|
Items = items ?? throw new ArgumentNullException(nameof(items));
|
||||||
|
ReasonString = reasonString;
|
||||||
|
UserProperties = userProperties ?? throw new ArgumentNullException(nameof(userProperties));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the packet identifier which was used.
|
/// Gets the result for every topic filter item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort PacketIdentifier { get; internal set; }
|
public IReadOnlyCollection<MqttClientSubscribeResultItem> Items { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the reason string.
|
/// Gets the user properties which were part of the SUBACK packet.
|
||||||
/// MQTTv5 only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ReasonString { get; internal set; }
|
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the reason string.
|
||||||
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
|
/// </summary>
|
||||||
|
public string ReasonString { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user properties which were part of the SUBACK packet.
|
/// Gets the user properties which were part of the SUBACK packet.
|
||||||
/// MQTTv5 only.
|
/// MQTTv5 only.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; internal set; }
|
public ushort PacketIdentifier { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,14 +16,20 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
public MqttClientSubscribeResult Create(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket)
|
public MqttClientSubscribeResult Create(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket)
|
||||||
{
|
{
|
||||||
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
|
if (subscribePacket == null)
|
||||||
if (subAckPacket == null) throw new ArgumentNullException(nameof(subAckPacket));
|
{
|
||||||
|
throw new ArgumentNullException(nameof(subscribePacket));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subAckPacket == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(subAckPacket));
|
||||||
|
}
|
||||||
|
|
||||||
// MQTTv5.0.0 handling.
|
// MQTTv5.0.0 handling.
|
||||||
if (subAckPacket.ReasonCodes.Any() && subAckPacket.ReasonCodes.Count != subscribePacket.TopicFilters.Count)
|
if (subAckPacket.ReasonCodes.Any() && subAckPacket.ReasonCodes.Count != subscribePacket.TopicFilters.Count)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException(
|
throw new MqttProtocolViolationException("The reason codes are not matching the topic filters [MQTT-3.9.3-1].");
|
||||||
"The reason codes are not matching the topic filters [MQTT-3.9.3-1].");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var items = new List<MqttClientSubscribeResultItem>();
|
var items = new List<MqttClientSubscribeResultItem>();
|
||||||
@@ -32,26 +38,13 @@ namespace MQTTnet.Client
|
|||||||
items.Add(CreateSubscribeResultItem(i, subscribePacket, subAckPacket));
|
items.Add(CreateSubscribeResultItem(i, subscribePacket, subAckPacket));
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = new MqttClientSubscribeResult
|
return new MqttClientSubscribeResult(subAckPacket.PacketIdentifier, items, subAckPacket.ReasonString, subAckPacket.UserProperties ?? EmptyUserProperties);
|
||||||
{
|
|
||||||
PacketIdentifier = subAckPacket.PacketIdentifier,
|
|
||||||
ReasonString = subAckPacket.ReasonString,
|
|
||||||
UserProperties = subAckPacket.UserProperties ?? EmptyUserProperties,
|
|
||||||
Items = items
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static MqttClientSubscribeResultItem CreateSubscribeResultItem(int index, MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket)
|
static MqttClientSubscribeResultItem CreateSubscribeResultItem(int index, MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket)
|
||||||
{
|
{
|
||||||
var resultCode = (MqttClientSubscribeResultCode) subAckPacket.ReasonCodes[index];
|
var resultCode = (MqttClientSubscribeResultCode)subAckPacket.ReasonCodes[index];
|
||||||
|
return new MqttClientSubscribeResultItem(subscribePacket.TopicFilters[index], resultCode);
|
||||||
return new MqttClientSubscribeResultItem
|
|
||||||
{
|
|
||||||
TopicFilter = subscribePacket.TopicFilters[index],
|
|
||||||
ResultCode = resultCode,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,22 +2,29 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
namespace MQTTnet.Client
|
||||||
{
|
{
|
||||||
public sealed class MqttClientSubscribeResultItem
|
public sealed class MqttClientSubscribeResultItem
|
||||||
{
|
{
|
||||||
/// <summary>
|
public MqttClientSubscribeResultItem(MqttTopicFilter topicFilter, MqttClientSubscribeResultCode resultCode)
|
||||||
/// Gets or sets the result code.
|
{
|
||||||
/// Hint: MQTT 5 feature only.
|
TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter));
|
||||||
/// </summary>
|
ResultCode = resultCode;
|
||||||
public MqttClientSubscribeResultCode ResultCode { get; internal set; }
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the topic filter.
|
/// Gets or sets the topic filter.
|
||||||
/// The topic filter can contain topics and wildcards.
|
/// The topic filter can contain topics and wildcards.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MqttTopicFilter TopicFilter { get; internal set; }
|
public MqttTopicFilter TopicFilter { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the result code.
|
||||||
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
|
/// </summary>
|
||||||
|
public MqttClientSubscribeResultCode ResultCode { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
|
|
||||||
@@ -9,23 +10,38 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
public sealed class MqttClientUnsubscribeResult
|
public sealed class MqttClientUnsubscribeResult
|
||||||
{
|
{
|
||||||
public IReadOnlyCollection<MqttClientUnsubscribeResultItem> Items { get; internal set; }
|
public MqttClientUnsubscribeResult(
|
||||||
|
ushort packetIdentifier,
|
||||||
|
IReadOnlyCollection<MqttClientUnsubscribeResultItem> items,
|
||||||
|
string reasonString,
|
||||||
|
IReadOnlyCollection<MqttUserProperty> userProperties)
|
||||||
|
{
|
||||||
|
PacketIdentifier = packetIdentifier;
|
||||||
|
Items = items ?? throw new ArgumentNullException(nameof(items));
|
||||||
|
ReasonString = reasonString;
|
||||||
|
UserProperties = userProperties ?? throw new ArgumentNullException(nameof(userProperties));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the result for every topic filter item.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyCollection<MqttClientUnsubscribeResultItem> Items { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the packet identifier which was used.
|
/// Gets the packet identifier which was used.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort PacketIdentifier { get; internal set; }
|
public ushort PacketIdentifier { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the reason string.
|
/// Gets the reason string.
|
||||||
/// MQTTv5 only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ReasonString { get; internal set; }
|
public string ReasonString { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user properties which were part of the UNSUBACK packet.
|
/// Gets the user properties which were part of the UNSUBACK packet.
|
||||||
/// MQTTv5 only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; internal set; }
|
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,14 +15,20 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
public MqttClientUnsubscribeResult Create(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket)
|
public MqttClientUnsubscribeResult Create(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket)
|
||||||
{
|
{
|
||||||
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
|
if (unsubscribePacket == null)
|
||||||
if (unsubAckPacket == null) throw new ArgumentNullException(nameof(unsubAckPacket));
|
{
|
||||||
|
throw new ArgumentNullException(nameof(unsubscribePacket));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unsubAckPacket == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(unsubAckPacket));
|
||||||
|
}
|
||||||
|
|
||||||
// MQTTv3.1.1 has no reason code at all!
|
// MQTTv3.1.1 has no reason code at all!
|
||||||
if (unsubAckPacket.ReasonCodes != null && unsubAckPacket.ReasonCodes.Count != unsubscribePacket.TopicFilters.Count)
|
if (unsubAckPacket.ReasonCodes != null && unsubAckPacket.ReasonCodes.Count != unsubscribePacket.TopicFilters.Count)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException(
|
throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1].");
|
||||||
"The return codes are not matching the topic filters [MQTT-3.9.3-1].");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var items = new List<MqttClientUnsubscribeResultItem>();
|
var items = new List<MqttClientUnsubscribeResultItem>();
|
||||||
@@ -31,15 +37,7 @@ namespace MQTTnet.Client
|
|||||||
items.Add(CreateUnsubscribeResultItem(i, unsubscribePacket, unsubAckPacket));
|
items.Add(CreateUnsubscribeResultItem(i, unsubscribePacket, unsubAckPacket));
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = new MqttClientUnsubscribeResult
|
return new MqttClientUnsubscribeResult(unsubscribePacket.PacketIdentifier, items, unsubAckPacket.ReasonString, unsubAckPacket.UserProperties ?? EmptyUserProperties);
|
||||||
{
|
|
||||||
PacketIdentifier = unsubAckPacket.PacketIdentifier,
|
|
||||||
ReasonString = unsubAckPacket.ReasonString,
|
|
||||||
UserProperties = unsubAckPacket.UserProperties ?? EmptyUserProperties,
|
|
||||||
Items = items
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static MqttClientUnsubscribeResultItem CreateUnsubscribeResultItem(int index, MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket)
|
static MqttClientUnsubscribeResultItem CreateUnsubscribeResultItem(int index, MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket)
|
||||||
@@ -49,14 +47,10 @@ namespace MQTTnet.Client
|
|||||||
if (unsubAckPacket.ReasonCodes != null)
|
if (unsubAckPacket.ReasonCodes != null)
|
||||||
{
|
{
|
||||||
// MQTTv3.1.1 has no reason code and no return code!.
|
// MQTTv3.1.1 has no reason code and no return code!.
|
||||||
resultCode = (MqttClientUnsubscribeResultCode) unsubAckPacket.ReasonCodes[index];
|
resultCode = (MqttClientUnsubscribeResultCode)unsubAckPacket.ReasonCodes[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MqttClientUnsubscribeResultItem
|
return new MqttClientUnsubscribeResultItem(unsubscribePacket.TopicFilters[index], resultCode);
|
||||||
{
|
|
||||||
TopicFilter = unsubscribePacket.TopicFilters[index],
|
|
||||||
ResultCode = resultCode
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,20 +2,28 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
namespace MQTTnet.Client
|
||||||
{
|
{
|
||||||
public sealed class MqttClientUnsubscribeResultItem
|
public sealed class MqttClientUnsubscribeResultItem
|
||||||
{
|
{
|
||||||
|
public MqttClientUnsubscribeResultItem(string topicFilter, MqttClientUnsubscribeResultCode resultCode)
|
||||||
|
{
|
||||||
|
TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter));
|
||||||
|
ResultCode = resultCode;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the result code.
|
/// Gets or sets the result code.
|
||||||
/// Hint: MQTT 5 feature only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MqttClientUnsubscribeResultCode ResultCode { get; internal set; }
|
public MqttClientUnsubscribeResultCode ResultCode { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the topic filter.
|
/// Gets or sets the topic filter.
|
||||||
/// The topic filter can contain topics and wildcards.
|
/// The topic filter can contain topics and wildcards.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string TopicFilter { get; internal set; }
|
public string TopicFilter { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,8 +8,14 @@ namespace MQTTnet.Diagnostics
|
|||||||
{
|
{
|
||||||
public sealed class InspectMqttPacketEventArgs : EventArgs
|
public sealed class InspectMqttPacketEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public MqttPacketFlowDirection Direction { get; internal set; }
|
public InspectMqttPacketEventArgs(MqttPacketFlowDirection direction, byte[] buffer)
|
||||||
|
{
|
||||||
|
Direction = direction;
|
||||||
|
Buffer = buffer ?? throw new ArgumentNullException(nameof(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] Buffer { get; set; }
|
public byte[] Buffer { get; }
|
||||||
|
|
||||||
|
public MqttPacketFlowDirection Direction { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,11 @@ namespace MQTTnet.Internal
|
|||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
|
#if NETCOREAPP3_1_OR_GREATER
|
||||||
|
_queue.Clear();
|
||||||
|
#else
|
||||||
Interlocked.Exchange(ref _queue, new ConcurrentQueue<TItem>());
|
Interlocked.Exchange(ref _queue, new ConcurrentQueue<TItem>());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -32,6 +36,16 @@ namespace MQTTnet.Internal
|
|||||||
_signal.Dispose();
|
_signal.Dispose();
|
||||||
|
|
||||||
_isDisposed = true;
|
_isDisposed = true;
|
||||||
|
|
||||||
|
#if !NETSTANDARD1_3
|
||||||
|
if (typeof(IDisposable).IsAssignableFrom(typeof(TItem)))
|
||||||
|
{
|
||||||
|
while (_queue.TryDequeue(out TItem item))
|
||||||
|
{
|
||||||
|
(item as IDisposable).Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
public sealed class PreparingSessionEventArgs : EventArgs
|
public sealed class PreparingSessionEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public string Id { get; internal set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
// TODO: Allow adding of packets to the queue etc.
|
// TODO: Allow adding of packets to the queue etc.
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using System.Security.Cryptography.X509Certificates;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
@@ -20,10 +19,11 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
readonly MqttConnectPacket _connectPacket;
|
readonly MqttConnectPacket _connectPacket;
|
||||||
|
|
||||||
public ValidatingConnectionEventArgs(MqttConnectPacket connectPacket, IMqttChannelAdapter clientAdapter)
|
public ValidatingConnectionEventArgs(MqttConnectPacket connectPacket, IMqttChannelAdapter clientAdapter, IDictionary sessionItems)
|
||||||
{
|
{
|
||||||
_connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
|
_connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
|
||||||
ChannelAdapter = clientAdapter ?? throw new ArgumentNullException(nameof(clientAdapter));
|
ChannelAdapter = clientAdapter ?? throw new ArgumentNullException(nameof(clientAdapter));
|
||||||
|
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -34,13 +34,13 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the authentication data.
|
/// Gets or sets the authentication data.
|
||||||
/// Hint: MQTT 5 feature only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte[] AuthenticationData => _connectPacket.AuthenticationData;
|
public byte[] AuthenticationData => _connectPacket.AuthenticationData;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the authentication method.
|
/// Gets or sets the authentication method.
|
||||||
/// Hint: MQTT 5 feature only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string AuthenticationMethod => _connectPacket.AuthenticationMethod;
|
public string AuthenticationMethod => _connectPacket.AuthenticationMethod;
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ namespace MQTTnet.Server
|
|||||||
/// Gets or sets the reason code. When a MQTTv3 client connects the enum value must be one which is
|
/// Gets or sets the reason code. When a MQTTv3 client connects the enum value must be one which is
|
||||||
/// also supported in MQTTv3. Otherwise the connection attempt will fail because not all codes can be
|
/// also supported in MQTTv3. Otherwise the connection attempt will fail because not all codes can be
|
||||||
/// converted properly.
|
/// converted properly.
|
||||||
/// MQTTv5 only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MqttConnectReasonCode ReasonCode { get; set; } = MqttConnectReasonCode.Success;
|
public MqttConnectReasonCode ReasonCode { get; set; } = MqttConnectReasonCode.Success;
|
||||||
|
|
||||||
@@ -114,19 +114,19 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the request problem information.
|
/// Gets the request problem information.
|
||||||
/// Hint: MQTT 5 feature only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RequestProblemInformation => _connectPacket.RequestProblemInformation;
|
public bool RequestProblemInformation => _connectPacket.RequestProblemInformation;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the request response information.
|
/// Gets the request response information.
|
||||||
/// Hint: MQTT 5 feature only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RequestResponseInformation => _connectPacket.RequestResponseInformation;
|
public bool RequestResponseInformation => _connectPacket.RequestResponseInformation;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the response authentication data.
|
/// Gets or sets the response authentication data.
|
||||||
/// MQTTv5 only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte[] ResponseAuthenticationData { get; set; }
|
public byte[] ResponseAuthenticationData { get; set; }
|
||||||
|
|
||||||
@@ -137,14 +137,14 @@ namespace MQTTnet.Server
|
|||||||
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add
|
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add
|
||||||
/// metadata to MQTT messages and pass information between publisher, broker, and subscriber.
|
/// metadata to MQTT messages and pass information between publisher, broker, and subscriber.
|
||||||
/// The feature is very similar to the HTTP header concept.
|
/// The feature is very similar to the HTTP header concept.
|
||||||
/// Hint: MQTT 5 feature only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<MqttUserProperty> ResponseUserProperties { get; set; }
|
public List<MqttUserProperty> ResponseUserProperties { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the server reference. This can be used together with i.e. "Server Moved" to send
|
/// Gets or sets the server reference. This can be used together with i.e. "Server Moved" to send
|
||||||
/// a different server address to the client.
|
/// a different server address to the client.
|
||||||
/// MQTTv5 only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ServerReference { get; set; }
|
public string ServerReference { get; set; }
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ namespace MQTTnet.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a key/value collection that can be used to share data within the scope of this session.
|
/// Gets or sets a key/value collection that can be used to share data within the scope of this session.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IDictionary SessionItems { get; internal set; }
|
public IDictionary SessionItems { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the topic alias maximum.
|
/// Gets or sets the topic alias maximum.
|
||||||
@@ -179,7 +179,7 @@ namespace MQTTnet.Server
|
|||||||
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add
|
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add
|
||||||
/// metadata to MQTT messages and pass information between publisher, broker, and subscriber.
|
/// metadata to MQTT messages and pass information between publisher, broker, and subscriber.
|
||||||
/// The feature is very similar to the HTTP header concept.
|
/// The feature is very similar to the HTTP header concept.
|
||||||
/// Hint: MQTT 5 feature only.
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<MqttUserProperty> UserProperties => _connectPacket.UserProperties;
|
public List<MqttUserProperty> UserProperties => _connectPacket.UserProperties;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
@@ -13,8 +14,15 @@ namespace MQTTnet.Server
|
|||||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SenderClientId { get; set; } = string.Empty;
|
public MqttApplicationMessage ApplicationMessage { get; }
|
||||||
|
|
||||||
public MqttApplicationMessage ApplicationMessage { get; set; }
|
/// <summary>
|
||||||
|
/// Gets or sets the session items which should be used for all event handlers which are involved in message
|
||||||
|
/// processing.
|
||||||
|
/// If _null_ is specified the singleton session items from the server are used instead.
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary CustomSessionItems { get; set; }
|
||||||
|
|
||||||
|
public string SenderClientId { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -669,11 +669,9 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
async Task<ValidatingConnectionEventArgs> ValidateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter)
|
async Task<ValidatingConnectionEventArgs> ValidateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter)
|
||||||
{
|
{
|
||||||
var eventArgs = new ValidatingConnectionEventArgs(connectPacket, channelAdapter)
|
// TODO: Load session items from persisted sessions in the future.
|
||||||
{
|
var sessionItems = new ConcurrentDictionary<object, object>();
|
||||||
SessionItems = new ConcurrentDictionary<object, object>()
|
var eventArgs = new ValidatingConnectionEventArgs(connectPacket, channelAdapter, sessionItems);
|
||||||
};
|
|
||||||
|
|
||||||
await _eventContainer.ValidatingConnectionEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
await _eventContainer.ValidatingConnectionEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||||
|
|
||||||
// Check the client ID and set a random one if supported.
|
// Check the client ID and set a random one if supported.
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
public sealed class MqttClientStatistics
|
public sealed class MqttClientStatistics
|
||||||
{
|
{
|
||||||
long _receivedApplicationMessagesCount;
|
Statistics _statistics = new Statistics // mutable struct, don't make readonly!
|
||||||
|
{
|
||||||
|
// Start with 1 because the CONNACK packet is not counted here.
|
||||||
|
_receivedPacketsCount = 1,
|
||||||
|
|
||||||
// Start with 1 because the CONNACK packet is not counted here.
|
// Start with 1 because the CONNECT packet is not counted here.
|
||||||
long _receivedPacketsCount = 1;
|
_sentPacketsCount = 1
|
||||||
long _sentApplicationMessagesCount;
|
};
|
||||||
|
|
||||||
// Start with 1 because the CONNECT packet is not counted here.
|
|
||||||
long _sentPacketsCount = 1;
|
|
||||||
|
|
||||||
public MqttClientStatistics()
|
public MqttClientStatistics()
|
||||||
{
|
{
|
||||||
@@ -30,7 +30,6 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DateTime ConnectedTimestamp { get; }
|
public DateTime ConnectedTimestamp { get; }
|
||||||
|
|
||||||
public DateTime LastNonKeepAlivePacketReceivedTimestamp { get; private set; }
|
public DateTime LastNonKeepAlivePacketReceivedTimestamp { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -43,13 +42,13 @@ namespace MQTTnet.Server
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime LastPacketSentTimestamp { get; private set; }
|
public DateTime LastPacketSentTimestamp { get; private set; }
|
||||||
|
|
||||||
public long ReceivedApplicationMessagesCount => Interlocked.Read(ref _receivedApplicationMessagesCount);
|
public long SentApplicationMessagesCount => Volatile.Read(ref _statistics._sentApplicationMessagesCount);
|
||||||
|
|
||||||
public long ReceivedPacketsCount => Interlocked.Read(ref _receivedPacketsCount);
|
public long ReceivedApplicationMessagesCount => Volatile.Read(ref _statistics._receivedApplicationMessagesCount);
|
||||||
|
|
||||||
public long SentApplicationMessagesCount => Interlocked.Read(ref _sentApplicationMessagesCount);
|
public long SentPacketsCount => Volatile.Read(ref _statistics._sentPacketsCount);
|
||||||
|
|
||||||
public long SentPacketsCount => Interlocked.Read(ref _sentPacketsCount);
|
public long ReceivedPacketsCount => Volatile.Read(ref _statistics._receivedPacketsCount);
|
||||||
|
|
||||||
public void HandleReceivedPacket(MqttPacket packet)
|
public void HandleReceivedPacket(MqttPacket packet)
|
||||||
{
|
{
|
||||||
@@ -61,11 +60,11 @@ namespace MQTTnet.Server
|
|||||||
// This class is tracking all values from Clients perspective!
|
// This class is tracking all values from Clients perspective!
|
||||||
LastPacketSentTimestamp = DateTime.UtcNow;
|
LastPacketSentTimestamp = DateTime.UtcNow;
|
||||||
|
|
||||||
Interlocked.Increment(ref _sentPacketsCount);
|
Interlocked.Increment(ref _statistics._sentPacketsCount);
|
||||||
|
|
||||||
if (packet is MqttPublishPacket)
|
if (packet is MqttPublishPacket)
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref _sentApplicationMessagesCount);
|
Interlocked.Increment(ref _statistics._sentApplicationMessagesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(packet is MqttPingReqPacket || packet is MqttPingRespPacket))
|
if (!(packet is MqttPingReqPacket || packet is MqttPingRespPacket))
|
||||||
@@ -73,6 +72,7 @@ namespace MQTTnet.Server
|
|||||||
LastNonKeepAlivePacketReceivedTimestamp = LastPacketReceivedTimestamp;
|
LastNonKeepAlivePacketReceivedTimestamp = LastPacketReceivedTimestamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public void ResetStatistics() => _statistics.Reset();
|
||||||
|
|
||||||
public void HandleSentPacket(MqttPacket packet)
|
public void HandleSentPacket(MqttPacket packet)
|
||||||
{
|
{
|
||||||
@@ -84,20 +84,29 @@ namespace MQTTnet.Server
|
|||||||
// This class is tracking all values from Clients perspective!
|
// This class is tracking all values from Clients perspective!
|
||||||
LastPacketReceivedTimestamp = DateTime.UtcNow;
|
LastPacketReceivedTimestamp = DateTime.UtcNow;
|
||||||
|
|
||||||
Interlocked.Increment(ref _receivedPacketsCount);
|
Interlocked.Increment(ref _statistics._receivedPacketsCount);
|
||||||
|
|
||||||
if (packet is MqttPublishPacket)
|
if (packet is MqttPublishPacket)
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref _receivedApplicationMessagesCount);
|
Interlocked.Increment(ref _statistics._receivedApplicationMessagesCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetStatistics()
|
private struct Statistics
|
||||||
{
|
{
|
||||||
Interlocked.Exchange(ref _sentApplicationMessagesCount, 0);
|
public long _receivedPacketsCount;
|
||||||
Interlocked.Exchange(ref _receivedApplicationMessagesCount, 0);
|
public long _sentPacketsCount;
|
||||||
Interlocked.Exchange(ref _sentPacketsCount, 0);
|
|
||||||
Interlocked.Exchange(ref _receivedPacketsCount, 0);
|
public long _receivedApplicationMessagesCount;
|
||||||
|
public long _sentApplicationMessagesCount;
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
Volatile.Write(ref _receivedPacketsCount, 0);
|
||||||
|
Volatile.Write(ref _sentPacketsCount, 0);
|
||||||
|
Volatile.Write(ref _receivedApplicationMessagesCount, 0);
|
||||||
|
Volatile.Write(ref _sentApplicationMessagesCount, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,8 +28,6 @@ namespace MQTTnet.Server
|
|||||||
readonly MqttRetainedMessagesManager _retainedMessagesManager;
|
readonly MqttRetainedMessagesManager _retainedMessagesManager;
|
||||||
readonly IMqttNetLogger _rootLogger;
|
readonly IMqttNetLogger _rootLogger;
|
||||||
|
|
||||||
readonly IDictionary _sessionItems = new ConcurrentDictionary<object, object>();
|
|
||||||
|
|
||||||
CancellationTokenSource _cancellationTokenSource;
|
CancellationTokenSource _cancellationTokenSource;
|
||||||
|
|
||||||
public MqttServer(MqttServerOptions options, IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger)
|
public MqttServer(MqttServerOptions options, IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger)
|
||||||
@@ -167,6 +165,12 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public bool IsStarted => _cancellationTokenSource != null;
|
public bool IsStarted => _cancellationTokenSource != null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gives access to the session items which belong to this server. This session items are passed
|
||||||
|
/// to several events instead of the client session items if the event is caused by the server instead of a client.
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary ServerSessionItems { get; } = new ConcurrentDictionary<object, object>();
|
||||||
|
|
||||||
public Task DeleteRetainedMessagesAsync()
|
public Task DeleteRetainedMessagesAsync()
|
||||||
{
|
{
|
||||||
ThrowIfNotStarted();
|
ThrowIfNotStarted();
|
||||||
@@ -228,9 +232,11 @@ namespace MQTTnet.Server
|
|||||||
throw new NotSupportedException("Injected application messages must contain a topic. Topic alias is not supported.");
|
throw new NotSupportedException("Injected application messages must contain a topic. Topic alias is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sessionItems = injectedApplicationMessage.CustomSessionItems ?? ServerSessionItems;
|
||||||
|
|
||||||
return _clientSessionsManager.DispatchApplicationMessage(
|
return _clientSessionsManager.DispatchApplicationMessage(
|
||||||
injectedApplicationMessage.SenderClientId,
|
injectedApplicationMessage.SenderClientId,
|
||||||
_sessionItems,
|
sessionItems,
|
||||||
injectedApplicationMessage.ApplicationMessage,
|
injectedApplicationMessage.ApplicationMessage,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user