mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-28 01:02:37 +08:00
Merge pull request #1075 from tkurucsai/feature/pending-appmsg-acknowledge
Feature/pending appmsg acknowledge
This commit is contained in:
@@ -585,9 +585,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,35 +641,13 @@ namespace MQTTnet.Client
|
|||||||
}
|
}
|
||||||
|
|
||||||
var publishPacket = publishPacketDequeueResult.Item;
|
var publishPacket = publishPacketDequeueResult.Item;
|
||||||
|
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket, cancellationToken);
|
||||||
|
|
||||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
if (eventArgs.AutoAcknowledge)
|
||||||
{
|
{
|
||||||
await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
|
await eventArgs.Acknowledge();
|
||||||
}
|
}
|
||||||
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
|
||||||
{
|
|
||||||
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!eventArgs.ProcessingFailed)
|
|
||||||
{
|
|
||||||
var pubAckPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubAckPacket(publishPacket, eventArgs.ReasonCode);
|
|
||||||
await SendAsync(pubAckPacket, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
|
||||||
{
|
|
||||||
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!eventArgs.ProcessingFailed)
|
|
||||||
{
|
|
||||||
var pubRecPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRecPacket(publishPacket, eventArgs.ReasonCode);
|
|
||||||
await SendAsync(pubRecPacket, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new MqttProtocolViolationException("Received a not supported QoS level.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@@ -681,6 +659,36 @@ namespace MQTTnet.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal Task AcknowledgeReceivedPublishPacket(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||||
|
{
|
||||||
|
// no response required
|
||||||
|
}
|
||||||
|
else if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
||||||
|
{
|
||||||
|
if (!eventArgs.ProcessingFailed)
|
||||||
|
{
|
||||||
|
var pubAckPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubAckPacket(eventArgs.PublishPacket, eventArgs.ReasonCode);
|
||||||
|
return SendAsync(pubAckPacket, eventArgs.CancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
||||||
|
{
|
||||||
|
if (!eventArgs.ProcessingFailed)
|
||||||
|
{
|
||||||
|
var pubRecPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRecPacket(eventArgs.PublishPacket, eventArgs.ReasonCode);
|
||||||
|
return SendAsync(pubRecPacket, eventArgs.CancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new MqttProtocolViolationException("Received a not supported QoS level.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
Task ProcessReceivedPubRecPacket(MqttPubRecPacket pubRecPacket, CancellationToken cancellationToken)
|
Task ProcessReceivedPubRecPacket(MqttPubRecPacket pubRecPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!_packetDispatcher.TryDispatch(pubRecPacket))
|
if (!_packetDispatcher.TryDispatch(pubRecPacket))
|
||||||
@@ -753,10 +761,10 @@ namespace MQTTnet.Client
|
|||||||
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(pubRecPacket, pubCompPacket);
|
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(pubRecPacket, pubCompPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
|
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
|
var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
|
||||||
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage);
|
var eventArgs = new MqttApplicationMessageReceivedEventArgs(this, publishPacket, cancellationToken, Options.ClientId, applicationMessage);
|
||||||
|
|
||||||
var handler = ApplicationMessageReceivedHandler;
|
var handler = ApplicationMessageReceivedHandler;
|
||||||
if (handler != null)
|
if (handler != null)
|
||||||
|
|||||||
@@ -1,15 +1,41 @@
|
|||||||
using System;
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Packets;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MQTTnet
|
namespace MQTTnet
|
||||||
{
|
{
|
||||||
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
|
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public MqttApplicationMessageReceivedEventArgs(string clientId, MqttApplicationMessage applicationMessage)
|
public MqttApplicationMessageReceivedEventArgs(MqttClient client, MqttPublishPacket publishPacket, CancellationToken cancellationToken, string clientId, MqttApplicationMessage applicationMessage)
|
||||||
{
|
{
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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; }
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -29,5 +55,19 @@ namespace MQTTnet
|
|||||||
public bool IsHandled { get; set; }
|
public bool IsHandled { get; set; }
|
||||||
|
|
||||||
public object Tag { get; set; }
|
public object Tag { get; set; }
|
||||||
|
|
||||||
|
public bool AutoAcknowledge { get; set; }
|
||||||
|
|
||||||
|
public Task Acknowledge()
|
||||||
|
{
|
||||||
|
if (Interlocked.CompareExchange(ref acknowledged, 1, 0) == 0)
|
||||||
|
{
|
||||||
|
return Client.AcknowledgeReceivedPublishPacket(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -445,6 +445,55 @@ namespace MQTTnet.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Preserve_Message_Order_With_Delayed_Acknowledgement()
|
||||||
|
{
|
||||||
|
// The messages are sent in reverse or to ensure that the delay in the handler
|
||||||
|
// needs longer for the first messages and later messages may be processed earlier (if there
|
||||||
|
// is an issue).
|
||||||
|
const int MessagesCount = 50;
|
||||||
|
|
||||||
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServerAsync();
|
||||||
|
|
||||||
|
var client1 = await testEnvironment.ConnectClientAsync();
|
||||||
|
await client1.SubscribeAsync("x", MqttQualityOfServiceLevel.ExactlyOnce);
|
||||||
|
|
||||||
|
var receivedValues = new List<int>();
|
||||||
|
|
||||||
|
Task Handler1(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
var value = int.Parse(eventArgs.ApplicationMessage.ConvertPayloadToString());
|
||||||
|
eventArgs.AutoAcknowledge = false;
|
||||||
|
Task.Delay(value).ContinueWith(x => eventArgs.Acknowledge());
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine($"received {value}");
|
||||||
|
lock (receivedValues)
|
||||||
|
{
|
||||||
|
receivedValues.Add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
client1.UseApplicationMessageReceivedHandler(Handler1);
|
||||||
|
|
||||||
|
var client2 = await testEnvironment.ConnectClientAsync();
|
||||||
|
for (var i = MessagesCount; i > 0; i--)
|
||||||
|
{
|
||||||
|
await client2.PublishAsync("x", i.ToString(), MqttQualityOfServiceLevel.ExactlyOnce);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(5000);
|
||||||
|
|
||||||
|
for (var i = MessagesCount; i > 0; i--)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(i, receivedValues[MessagesCount - i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Send_Reply_For_Any_Received_Message()
|
public async Task Send_Reply_For_Any_Received_Message()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user