mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 00:15:15 +08:00
Fix keep alive ping send timeout disconnect handling (#2253)
Fix keep alive ping send timeout disconnect
This commit is contained in:
@@ -5,8 +5,12 @@
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using MQTTnet.Adapter;
|
||||||
|
using MQTTnet.Diagnostics.Logger;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
@@ -454,6 +458,34 @@ public sealed class MqttClient_Tests : BaseTestClass
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task KeepAlive_Ping_Send_Timeout_Raises_Disconnected()
|
||||||
|
{
|
||||||
|
var adapterFactory = new KeepAliveTimeoutAdapterFactory();
|
||||||
|
var disconnected = new TaskCompletionSource<MqttClientDisconnectedEventArgs>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
|
using var client = new MqttClientFactory().CreateMqttClient(adapterFactory);
|
||||||
|
client.DisconnectedAsync += eventArgs =>
|
||||||
|
{
|
||||||
|
disconnected.TrySetResult(eventArgs);
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var options = new MqttClientOptionsBuilder()
|
||||||
|
.WithTcpServer("localhost")
|
||||||
|
.WithKeepAlivePeriod(TimeSpan.FromSeconds(1))
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await client.ConnectAsync(options);
|
||||||
|
|
||||||
|
await adapterFactory.Adapter.PingRequestReceived.Task.WaitAsync(TimeSpan.FromSeconds(3));
|
||||||
|
var disconnectedEventArgs = await disconnected.Task.WaitAsync(TimeSpan.FromSeconds(3));
|
||||||
|
|
||||||
|
Assert.IsTrue(disconnectedEventArgs.ClientWasConnected);
|
||||||
|
Assert.IsInstanceOfType<OperationCanceledException>(disconnectedEventArgs.Exception);
|
||||||
|
Assert.IsFalse(client.IsConnected);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Publish_QoS_1_In_ApplicationMessageReceiveHandler()
|
public async Task Publish_QoS_1_In_ApplicationMessageReceiveHandler()
|
||||||
{
|
{
|
||||||
@@ -975,4 +1007,84 @@ public sealed class MqttClient_Tests : BaseTestClass
|
|||||||
Assert.IsTrue(publisher.IsConnected);
|
Assert.IsTrue(publisher.IsConnected);
|
||||||
Assert.AreEqual(Threads * MessagesPerThread, receivedCount);
|
Assert.AreEqual(Threads * MessagesPerThread, receivedCount);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
sealed class KeepAliveTimeoutAdapterFactory : IMqttClientAdapterFactory
|
||||||
|
{
|
||||||
|
public KeepAliveTimeoutAdapter Adapter { get; private set; }
|
||||||
|
|
||||||
|
public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger)
|
||||||
|
{
|
||||||
|
Adapter = new KeepAliveTimeoutAdapter(options.ProtocolVersion, options.WriterBufferSize, options.WriterBufferSizeMax);
|
||||||
|
return Adapter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class KeepAliveTimeoutAdapter : IMqttChannelAdapter
|
||||||
|
{
|
||||||
|
int _connAckReturned;
|
||||||
|
|
||||||
|
public KeepAliveTimeoutAdapter(MqttProtocolVersion protocolVersion, int writerBufferSize, int writerBufferSizeMax)
|
||||||
|
{
|
||||||
|
PacketFormatterAdapter = new MqttPacketFormatterAdapter(protocolVersion, new MqttBufferWriter(writerBufferSize, writerBufferSizeMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
public long BytesReceived { get; }
|
||||||
|
|
||||||
|
public long BytesSent { get; }
|
||||||
|
|
||||||
|
public X509Certificate2 ClientCertificate { get; }
|
||||||
|
|
||||||
|
public EndPoint RemoteEndPoint { get; } = new DnsEndPoint("localhost", 1883);
|
||||||
|
|
||||||
|
public EndPoint LocalEndPoint { get; } = new DnsEndPoint("localhost", 0);
|
||||||
|
|
||||||
|
public bool IsSecureConnection { get; }
|
||||||
|
|
||||||
|
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||||
|
|
||||||
|
public TaskCompletionSource PingRequestReceived { get; } = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
|
public Task ConnectAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DisconnectAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MqttPacket> ReceivePacketAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (Interlocked.Exchange(ref _connAckReturned, 1) == 0)
|
||||||
|
{
|
||||||
|
return new MqttConnAckPacket
|
||||||
|
{
|
||||||
|
ReturnCode = MqttConnectReturnCode.ConnectionAccepted
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetStatistics()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (packet is MqttPingReqPacket)
|
||||||
|
{
|
||||||
|
PingRequestReceived.TrySetResult();
|
||||||
|
return Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1064,7 +1064,12 @@ public sealed class MqttClient : Disposable, IMqttClient
|
|||||||
switch (exception)
|
switch (exception)
|
||||||
{
|
{
|
||||||
case OperationCanceledException:
|
case OperationCanceledException:
|
||||||
return;
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
case MqttCommunicationException:
|
case MqttCommunicationException:
|
||||||
_logger.Warning(exception, "Communication error while sending/receiving keep alive packets");
|
_logger.Warning(exception, "Communication error while sending/receiving keep alive packets");
|
||||||
break;
|
break;
|
||||||
@@ -1080,4 +1085,4 @@ public sealed class MqttClient : Disposable, IMqttClient
|
|||||||
_logger.Verbose("Stopped sending keep alive packets");
|
_logger.Verbose("Stopped sending keep alive packets");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user