選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 

142 行
5.3 KiB

  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Runtime.CompilerServices;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using MQTTnet.Adapter;
  7. using MQTTnet.Diagnostics;
  8. using MQTTnet.Exceptions;
  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 SemaphoreSlim _queueWaitSemaphore = new SemaphoreSlim(0);
  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. _queue.Enqueue(packet);
  48. _queueWaitSemaphore.Release();
  49. _logger.Verbose<MqttClientPendingMessagesQueue>("Enqueued packet (ClientId: {0}).", _clientSession.ClientId);
  50. }
  51. private async Task SendQueuedPacketsAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken)
  52. {
  53. try
  54. {
  55. while (!cancellationToken.IsCancellationRequested)
  56. {
  57. await SendNextQueuedPacketAsync(adapter, cancellationToken).ConfigureAwait(false);
  58. }
  59. }
  60. catch (OperationCanceledException)
  61. {
  62. }
  63. catch (Exception exception)
  64. {
  65. _logger.Error<MqttClientPendingMessagesQueue>(exception, "Unhandled exception while sending enqueued packet (ClientId: {0}).", _clientSession.ClientId);
  66. }
  67. }
  68. private async Task SendNextQueuedPacketAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken)
  69. {
  70. MqttBasePacket packet = null;
  71. try
  72. {
  73. await _queueWaitSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
  74. if (!_queue.TryDequeue(out packet))
  75. {
  76. throw new InvalidOperationException(); // should not happen
  77. }
  78. if (cancellationToken.IsCancellationRequested)
  79. {
  80. return;
  81. }
  82. await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { packet }).ConfigureAwait(false);
  83. _logger.Verbose<MqttClientPendingMessagesQueue>("Enqueued packet sent (ClientId: {0}).", _clientSession.ClientId);
  84. }
  85. catch (Exception exception)
  86. {
  87. if (exception is MqttCommunicationTimedOutException)
  88. {
  89. _logger.Warning<MqttClientPendingMessagesQueue>(exception, "Sending publish packet failed due to timeout (ClientId: {0}).", _clientSession.ClientId);
  90. }
  91. else if (exception is MqttCommunicationException)
  92. {
  93. _logger.Warning<MqttClientPendingMessagesQueue>(exception, "Sending publish packet failed due to communication exception (ClientId: {0}).", _clientSession.ClientId);
  94. }
  95. else if (exception is OperationCanceledException)
  96. {
  97. }
  98. else
  99. {
  100. _logger.Error<MqttClientPendingMessagesQueue>(exception, "Sending publish packet failed (ClientId: {0}).", _clientSession.ClientId);
  101. }
  102. if (packet is MqttPublishPacket publishPacket)
  103. {
  104. if (publishPacket.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
  105. {
  106. publishPacket.Dup = true;
  107. _queue.Enqueue(packet);
  108. _queueWaitSemaphore.Release();
  109. }
  110. }
  111. if (!cancellationToken.IsCancellationRequested)
  112. {
  113. await _clientSession.StopAsync().ConfigureAwait(false);
  114. }
  115. }
  116. }
  117. public void Dispose()
  118. {
  119. _queueWaitSemaphore?.Dispose();
  120. }
  121. }
  122. }