mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-18 18:43:10 +08:00
Refactor deferred ACK of publish packets.
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
* [Client] Changed exception types so that proper exceptions are thrown when connecting (#1082).
|
* [Client] Changed exception types so that proper exceptions are thrown when connecting (#1082).
|
||||||
* [Client] Exposed Dup flag in application messages.
|
* [Client] Exposed Dup flag in application messages.
|
||||||
* [Client] Improved keep alive message sending.
|
* [Client] Improved keep alive message sending.
|
||||||
|
* [Client] Added support for deferred message approval (#1075, thanks to @tkurucsai).
|
||||||
* [RpcClient] Fixed an issue when using a custom application message reveived handler (#1093).
|
* [RpcClient] Fixed an issue when using a custom application message reveived handler (#1093).
|
||||||
* [RpcClient] Fixed a race condition when using timeout and cancellation token at the same time (BREAKING CHANGE!).
|
* [RpcClient] Fixed a race condition when using timeout and cancellation token at the same time (BREAKING CHANGE!).
|
||||||
* [Server] Improved keep alive message sending.
|
* [Server] Improved keep alive message sending.
|
||||||
|
|||||||
@@ -230,21 +230,21 @@ namespace MQTTnet.Client
|
|||||||
switch (applicationMessage.QualityOfServiceLevel)
|
switch (applicationMessage.QualityOfServiceLevel)
|
||||||
{
|
{
|
||||||
case MqttQualityOfServiceLevel.AtMostOnce:
|
case MqttQualityOfServiceLevel.AtMostOnce:
|
||||||
{
|
{
|
||||||
return PublishAtMostOnce(publishPacket, cancellationToken);
|
return PublishAtMostOnce(publishPacket, cancellationToken);
|
||||||
}
|
}
|
||||||
case MqttQualityOfServiceLevel.AtLeastOnce:
|
case MqttQualityOfServiceLevel.AtLeastOnce:
|
||||||
{
|
{
|
||||||
return PublishAtLeastOnceAsync(publishPacket, cancellationToken);
|
return PublishAtLeastOnceAsync(publishPacket, cancellationToken);
|
||||||
}
|
}
|
||||||
case MqttQualityOfServiceLevel.ExactlyOnce:
|
case MqttQualityOfServiceLevel.ExactlyOnce:
|
||||||
{
|
{
|
||||||
return PublishExactlyOnceAsync(publishPacket, cancellationToken);
|
return PublishExactlyOnceAsync(publishPacket, cancellationToken);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,7 +365,8 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
// This handler must be executed in a new thread because otherwise a dead lock may happen
|
// This handler must be executed in a new thread because otherwise a dead lock may happen
|
||||||
// when trying to reconnect in that handler etc.
|
// when trying to reconnect in that handler etc.
|
||||||
Task.Run(() => disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception, authenticateResult, _disconnectReason))).RunInBackground(_logger);
|
Task.Run(() => disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception, authenticateResult, _disconnectReason)))
|
||||||
|
.RunInBackground(_logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -585,9 +586,9 @@ namespace MQTTnet.Client
|
|||||||
if (!_packetDispatcher.TryDispatch(packet))
|
if (!_packetDispatcher.TryDispatch(packet))
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException($"Received packet '{packet}' at an unexpected time.");
|
throw new MqttProtocolViolationException($"Received packet '{packet}' at an unexpected time.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
if (_cleanDisconnectInitiated)
|
if (_cleanDisconnectInitiated)
|
||||||
@@ -641,13 +642,12 @@ namespace MQTTnet.Client
|
|||||||
}
|
}
|
||||||
|
|
||||||
var publishPacket = publishPacketDequeueResult.Item;
|
var publishPacket = publishPacketDequeueResult.Item;
|
||||||
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket, cancellationToken);
|
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
|
||||||
|
|
||||||
if (eventArgs.AutoAcknowledge)
|
if (eventArgs.AutoAcknowledge)
|
||||||
{
|
{
|
||||||
await eventArgs.Acknowledge();
|
await eventArgs.AcknowledgeAsync(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@@ -659,7 +659,7 @@ namespace MQTTnet.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Task AcknowledgeReceivedPublishPacket(MqttApplicationMessageReceivedEventArgs eventArgs)
|
internal Task AcknowledgeReceivedPublishPacket(MqttApplicationMessageReceivedEventArgs eventArgs, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||||
{
|
{
|
||||||
@@ -670,7 +670,7 @@ namespace MQTTnet.Client
|
|||||||
if (!eventArgs.ProcessingFailed)
|
if (!eventArgs.ProcessingFailed)
|
||||||
{
|
{
|
||||||
var pubAckPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubAckPacket(eventArgs.PublishPacket, eventArgs.ReasonCode);
|
var pubAckPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubAckPacket(eventArgs.PublishPacket, eventArgs.ReasonCode);
|
||||||
return SendAsync(pubAckPacket, eventArgs.CancellationToken);
|
return SendAsync(pubAckPacket, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
else if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
||||||
@@ -678,7 +678,7 @@ namespace MQTTnet.Client
|
|||||||
if (!eventArgs.ProcessingFailed)
|
if (!eventArgs.ProcessingFailed)
|
||||||
{
|
{
|
||||||
var pubRecPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRecPacket(eventArgs.PublishPacket, eventArgs.ReasonCode);
|
var pubRecPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRecPacket(eventArgs.PublishPacket, eventArgs.ReasonCode);
|
||||||
return SendAsync(pubRecPacket, eventArgs.CancellationToken);
|
return SendAsync(pubRecPacket, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -710,7 +710,7 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
Task ProcessReceivedDisconnectPacket(MqttDisconnectPacket disconnectPacket)
|
Task ProcessReceivedDisconnectPacket(MqttDisconnectPacket disconnectPacket)
|
||||||
{
|
{
|
||||||
_disconnectReason = (MqttClientDisconnectReason)(disconnectPacket.ReasonCode ?? MqttDisconnectReasonCode.NormalDisconnection);
|
_disconnectReason = (MqttClientDisconnectReason) (disconnectPacket.ReasonCode ?? MqttDisconnectReasonCode.NormalDisconnection);
|
||||||
|
|
||||||
// Also dispatch disconnect to waiting threads to generate a proper exception.
|
// Also dispatch disconnect to waiting threads to generate a proper exception.
|
||||||
_packetDispatcher.FailAll(new MqttUnexpectedDisconnectReceivedException(disconnectPacket));
|
_packetDispatcher.FailAll(new MqttUnexpectedDisconnectReceivedException(disconnectPacket));
|
||||||
@@ -755,16 +755,16 @@ namespace MQTTnet.Client
|
|||||||
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var pubRelPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
var pubRelPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
||||||
|
|
||||||
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
|
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(pubRecPacket, pubCompPacket);
|
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(pubRecPacket, pubCompPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
|
||||||
{
|
{
|
||||||
var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
|
var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
|
||||||
var eventArgs = new MqttApplicationMessageReceivedEventArgs(this, publishPacket, cancellationToken, Options.ClientId, applicationMessage);
|
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage, publishPacket, AcknowledgeReceivedPublishPacket);
|
||||||
|
|
||||||
var handler = ApplicationMessageReceivedHandler;
|
var handler = ApplicationMessageReceivedHandler;
|
||||||
if (handler != null)
|
if (handler != null)
|
||||||
@@ -813,4 +813,4 @@ namespace MQTTnet.Client
|
|||||||
return Interlocked.CompareExchange(ref _isDisconnectPending, 1, 0) != 0;
|
return Interlocked.CompareExchange(ref _isDisconnectPending, 1, 0) != 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,41 +1,30 @@
|
|||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Implementations;
|
|
||||||
using MQTTnet.Packets;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Packets;
|
||||||
|
|
||||||
namespace MQTTnet
|
namespace MQTTnet
|
||||||
{
|
{
|
||||||
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
|
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public MqttApplicationMessageReceivedEventArgs(MqttClient client, MqttPublishPacket publishPacket, CancellationToken cancellationToken, string clientId, MqttApplicationMessage applicationMessage)
|
readonly Func<MqttApplicationMessageReceivedEventArgs, CancellationToken, Task> _acknowledgeHandler;
|
||||||
|
|
||||||
|
int _isAcknowledged;
|
||||||
|
|
||||||
|
public MqttApplicationMessageReceivedEventArgs(
|
||||||
|
string clientId,
|
||||||
|
MqttApplicationMessage applicationMessage,
|
||||||
|
MqttPublishPacket publishPacket,
|
||||||
|
Func<MqttApplicationMessageReceivedEventArgs, CancellationToken, Task> acknowledgeHandler)
|
||||||
{
|
{
|
||||||
Client = client ?? throw new ArgumentNullException(nameof(client));
|
|
||||||
PublishPacket = publishPacket ?? throw new ArgumentNullException(nameof(publishPacket));
|
|
||||||
CancellationToken = cancellationToken;
|
|
||||||
acknowledged = 0;
|
|
||||||
ClientId = clientId;
|
ClientId = clientId;
|
||||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
AutoAcknowledge = true;
|
PublishPacket = publishPacket;
|
||||||
|
_acknowledgeHandler = acknowledgeHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttApplicationMessageReceivedEventArgs(string clientId, MqttApplicationMessage applicationMessage)
|
|
||||||
{
|
|
||||||
acknowledged = 1;
|
|
||||||
ClientId = clientId;
|
|
||||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
|
||||||
AutoAcknowledge = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal MqttClient Client { get; }
|
|
||||||
|
|
||||||
internal CancellationToken CancellationToken { get; }
|
|
||||||
|
|
||||||
internal MqttPublishPacket PublishPacket { get; }
|
internal MqttPublishPacket PublishPacket { get; }
|
||||||
|
|
||||||
int acknowledged;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the client identifier.
|
/// Gets the client identifier.
|
||||||
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
||||||
@@ -53,21 +42,27 @@ namespace MQTTnet
|
|||||||
/// This value can be used in user code for custom control flow.
|
/// This value can be used in user code for custom control flow.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsHandled { get; set; }
|
public bool IsHandled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets ir sets whether the library should send MQTT ACK packets automatically if required.
|
||||||
|
/// </summary>
|
||||||
|
public bool AutoAcknowledge { get; set; } = true;
|
||||||
|
|
||||||
public object Tag { get; set; }
|
public object Tag { get; set; }
|
||||||
|
|
||||||
public bool AutoAcknowledge { get; set; }
|
public Task AcknowledgeAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
public Task Acknowledge()
|
if (_acknowledgeHandler == null)
|
||||||
{
|
|
||||||
if (Interlocked.CompareExchange(ref acknowledged, 1, 0) == 0)
|
|
||||||
{
|
{
|
||||||
return Client.AcknowledgeReceivedPublishPacket(this);
|
throw new NotSupportedException("Deferred acknowledgement of application message is not yet supported in MQTTnet server.");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (Interlocked.CompareExchange(ref _isAcknowledged, 1, 0) == 0)
|
||||||
{
|
{
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return _acknowledgeHandler(this, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException("The application message is already acknowledged.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ namespace MQTTnet.Server
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await handler.HandleApplicationMessageReceivedAsync(new MqttApplicationMessageReceivedEventArgs(senderClientId, applicationMessage)).ConfigureAwait(false);
|
await handler.HandleApplicationMessageReceivedAsync(new MqttApplicationMessageReceivedEventArgs(senderClientId, applicationMessage, null, null)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -466,7 +466,7 @@ namespace MQTTnet.Tests
|
|||||||
{
|
{
|
||||||
var value = int.Parse(eventArgs.ApplicationMessage.ConvertPayloadToString());
|
var value = int.Parse(eventArgs.ApplicationMessage.ConvertPayloadToString());
|
||||||
eventArgs.AutoAcknowledge = false;
|
eventArgs.AutoAcknowledge = false;
|
||||||
Task.Delay(value).ContinueWith(x => eventArgs.Acknowledge());
|
Task.Delay(value).ContinueWith(x => eventArgs.AcknowledgeAsync(CancellationToken.None));
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"received {value}");
|
System.Diagnostics.Debug.WriteLine($"received {value}");
|
||||||
lock (receivedValues)
|
lock (receivedValues)
|
||||||
|
|||||||
Reference in New Issue
Block a user