Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

160 строки
5.6 KiB

  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. using MQTTnet.Adapter;
  6. using MQTTnet.Diagnostics;
  7. using MQTTnet.Exceptions;
  8. using MQTTnet.Internal;
  9. using MQTTnet.Packets;
  10. using MQTTnet.Protocol;
  11. namespace MQTTnet.Server
  12. {
  13. public sealed class MqttClientPendingMessagesQueue : IDisposable
  14. {
  15. private readonly ConcurrentQueue<MqttBasePacket> _queue = new ConcurrentQueue<MqttBasePacket>();
  16. private readonly AsyncAutoResetEvent _queueAutoResetEvent = new AsyncAutoResetEvent();
  17. private readonly IMqttServerOptions _options;
  18. private readonly MqttClientSession _clientSession;
  19. private readonly IMqttNetLogger _logger;
  20. private Task _workerTask;
  21. public MqttClientPendingMessagesQueue(IMqttServerOptions options, MqttClientSession clientSession, IMqttNetLogger logger)
  22. {
  23. _options = options ?? throw new ArgumentNullException(nameof(options));
  24. _clientSession = clientSession ?? throw new ArgumentNullException(nameof(clientSession));
  25. _logger = logger ?? throw new ArgumentNullException(nameof(logger));
  26. }
  27. public int Count => _queue.Count;
  28. public void Start(IMqttChannelAdapter adapter, CancellationToken cancellationToken)
  29. {
  30. if (adapter == null) throw new ArgumentNullException(nameof(adapter));
  31. if (cancellationToken.IsCancellationRequested)
  32. {
  33. return;
  34. }
  35. _workerTask = Task.Run(() => SendQueuedPacketsAsync(adapter, cancellationToken), cancellationToken);
  36. }
  37. public void WaitForCompletion()
  38. {
  39. if (_workerTask != null)
  40. {
  41. Task.WaitAll(_workerTask);
  42. }
  43. }
  44. public void Enqueue(MqttBasePacket packet)
  45. {
  46. if (packet == null) throw new ArgumentNullException(nameof(packet));
  47. if (_queue.Count >= _options.MaxPendingMessagesPerClient)
  48. {
  49. if (_options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
  50. {
  51. return;
  52. }
  53. if (_options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage)
  54. {
  55. _queue.TryDequeue(out _);
  56. }
  57. }
  58. _queue.Enqueue(packet);
  59. _queueAutoResetEvent.Set();
  60. _logger.Verbose(this, "Enqueued packet (ClientId: {0}).", _clientSession.ClientId);
  61. }
  62. private async Task SendQueuedPacketsAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken)
  63. {
  64. try
  65. {
  66. while (!cancellationToken.IsCancellationRequested)
  67. {
  68. await TrySendNextQueuedPacketAsync(adapter, cancellationToken).ConfigureAwait(false);
  69. }
  70. }
  71. catch (OperationCanceledException)
  72. {
  73. }
  74. catch (Exception exception)
  75. {
  76. _logger.Error(this, exception, "Unhandled exception while sending enqueued packet (ClientId: {0}).", _clientSession.ClientId);
  77. }
  78. }
  79. private async Task TrySendNextQueuedPacketAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken)
  80. {
  81. MqttBasePacket packet = null;
  82. try
  83. {
  84. if (_queue.IsEmpty)
  85. {
  86. await _queueAutoResetEvent.WaitOneAsync(cancellationToken).ConfigureAwait(false);
  87. }
  88. if (!_queue.TryDequeue(out packet))
  89. {
  90. return;
  91. }
  92. if (cancellationToken.IsCancellationRequested)
  93. {
  94. return;
  95. }
  96. await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, new[] { packet }, cancellationToken).ConfigureAwait(false);
  97. _logger.Verbose<MqttClientPendingMessagesQueue>("Enqueued packet sent (ClientId: {0}).",
  98. _clientSession.ClientId);
  99. }
  100. catch (Exception exception)
  101. {
  102. if (exception is MqttCommunicationTimedOutException)
  103. {
  104. _logger.Warning(this, exception, "Sending publish packet failed: Timeout (ClientId: {0}).", _clientSession.ClientId);
  105. }
  106. else if (exception is MqttCommunicationException)
  107. {
  108. _logger.Warning(this, exception, "Sending publish packet failed: Communication exception (ClientId: {0}).", _clientSession.ClientId);
  109. }
  110. else if (exception is OperationCanceledException)
  111. {
  112. }
  113. else
  114. {
  115. _logger.Error(this, exception, "Sending publish packet failed (ClientId: {0}).", _clientSession.ClientId);
  116. }
  117. if (packet is MqttPublishPacket publishPacket)
  118. {
  119. if (publishPacket.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
  120. {
  121. publishPacket.Dup = true;
  122. Enqueue(publishPacket);
  123. }
  124. }
  125. if (!cancellationToken.IsCancellationRequested)
  126. {
  127. _clientSession.Stop(MqttClientDisconnectType.NotClean);
  128. }
  129. }
  130. }
  131. public void Dispose()
  132. {
  133. _queueAutoResetEvent?.Dispose();
  134. }
  135. }
  136. }