You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

96 lines
3.1 KiB

  1. using System;
  2. using System.Diagnostics;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. using MQTTnet.Diagnostics;
  6. using MQTTnet.Packets;
  7. namespace MQTTnet.Server
  8. {
  9. public sealed class MqttClientKeepAliveMonitor
  10. {
  11. private readonly Stopwatch _lastPacketReceivedTracker = new Stopwatch();
  12. private readonly Stopwatch _lastNonKeepAlivePacketReceivedTracker = new Stopwatch();
  13. private readonly string _clientId;
  14. private readonly Action _timeoutCallback;
  15. private readonly IMqttNetLogger _logger;
  16. private Task _workerTask;
  17. public MqttClientKeepAliveMonitor(string clientId, Action timeoutCallback, IMqttNetLogger logger)
  18. {
  19. _clientId = clientId;
  20. _timeoutCallback = timeoutCallback;
  21. _logger = logger;
  22. }
  23. public TimeSpan LastPacketReceived => _lastPacketReceivedTracker.Elapsed;
  24. public TimeSpan LastNonKeepAlivePacketReceived => _lastNonKeepAlivePacketReceivedTracker.Elapsed;
  25. public void Start(int keepAlivePeriod, CancellationToken cancellationToken)
  26. {
  27. if (keepAlivePeriod == 0)
  28. {
  29. return;
  30. }
  31. _workerTask = Task.Run(() => RunAsync(keepAlivePeriod, cancellationToken).ConfigureAwait(false), cancellationToken);
  32. }
  33. public void WaitForCompletion()
  34. {
  35. if (_workerTask != null)
  36. {
  37. Task.WaitAll(_workerTask);
  38. }
  39. }
  40. private async Task RunAsync(int keepAlivePeriod, CancellationToken cancellationToken)
  41. {
  42. try
  43. {
  44. _lastPacketReceivedTracker.Restart();
  45. _lastNonKeepAlivePacketReceivedTracker.Restart();
  46. while (!cancellationToken.IsCancellationRequested)
  47. {
  48. // Values described here: [MQTT-3.1.2-24].
  49. if (_lastPacketReceivedTracker.Elapsed.TotalSeconds > keepAlivePeriod * 1.5D)
  50. {
  51. _logger.Warning<MqttClientSession>("Client '{0}': Did not receive any packet or keep alive signal.", _clientId);
  52. _timeoutCallback?.Invoke();
  53. return;
  54. }
  55. await Task.Delay(keepAlivePeriod, cancellationToken).ConfigureAwait(false);
  56. }
  57. }
  58. catch (OperationCanceledException)
  59. {
  60. }
  61. catch (Exception exception)
  62. {
  63. _logger.Error<MqttClientSession>(exception, "Client '{0}': Unhandled exception while checking keep alive timeouts.", _clientId);
  64. }
  65. finally
  66. {
  67. _logger.Verbose<MqttClientSession>("Client {0}: Stopped checking keep alive timeout.", _clientId);
  68. }
  69. }
  70. public void PacketReceived(MqttBasePacket packet)
  71. {
  72. _lastPacketReceivedTracker.Restart();
  73. if (!(packet is MqttPingReqPacket))
  74. {
  75. _lastNonKeepAlivePacketReceivedTracker.Restart();
  76. }
  77. }
  78. }
  79. }