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.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
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]
|
||||
public async Task Publish_QoS_1_In_ApplicationMessageReceiveHandler()
|
||||
{
|
||||
@@ -975,4 +1007,84 @@ public sealed class MqttClient_Tests : BaseTestClass
|
||||
Assert.IsTrue(publisher.IsConnected);
|
||||
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)
|
||||
{
|
||||
case OperationCanceledException:
|
||||
return;
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case MqttCommunicationException:
|
||||
_logger.Warning(exception, "Communication error while sending/receiving keep alive packets");
|
||||
break;
|
||||
@@ -1080,4 +1085,4 @@ public sealed class MqttClient : Disposable, IMqttClient
|
||||
_logger.Verbose("Stopped sending keep alive packets");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user