Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 

174 rader
6.4 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using MQTTnet.Packets;
  7. using MQTTnet.Protocol;
  8. namespace MQTTnet.Server
  9. {
  10. public sealed class MqttClientSubscriptionsManager : IDisposable
  11. {
  12. private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
  13. private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
  14. private readonly IMqttServerOptions _options;
  15. private readonly string _clientId;
  16. public MqttClientSubscriptionsManager(IMqttServerOptions options, string clientId)
  17. {
  18. _options = options ?? throw new ArgumentNullException(nameof(options));
  19. _clientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
  20. }
  21. public Action<string, TopicFilter> TopicSubscribedCallback { get; set; }
  22. public Action<string, string> TopicUnsubscribedCallback { get; set; }
  23. public async Task<MqttClientSubscribeResult> SubscribeAsync(MqttSubscribePacket subscribePacket)
  24. {
  25. if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
  26. var result = new MqttClientSubscribeResult
  27. {
  28. ResponsePacket = subscribePacket.CreateResponse<MqttSubAckPacket>(),
  29. CloseConnection = false
  30. };
  31. await _semaphore.WaitAsync().ConfigureAwait(false);
  32. try
  33. {
  34. foreach (var topicFilter in subscribePacket.TopicFilters)
  35. {
  36. var interceptorContext = InterceptSubscribe(topicFilter);
  37. if (!interceptorContext.AcceptSubscription)
  38. {
  39. result.ResponsePacket.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.Failure);
  40. }
  41. else
  42. {
  43. result.ResponsePacket.SubscribeReturnCodes.Add(ConvertToMaximumQoS(topicFilter.QualityOfServiceLevel));
  44. }
  45. if (interceptorContext.CloseConnection)
  46. {
  47. result.CloseConnection = true;
  48. }
  49. if (interceptorContext.AcceptSubscription)
  50. {
  51. _subscriptions[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
  52. TopicSubscribedCallback?.Invoke(_clientId, topicFilter);
  53. }
  54. }
  55. }
  56. finally
  57. {
  58. _semaphore.Release();
  59. }
  60. return result;
  61. }
  62. public async Task<MqttUnsubAckPacket> UnsubscribeAsync(MqttUnsubscribePacket unsubscribePacket)
  63. {
  64. if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
  65. await _semaphore.WaitAsync().ConfigureAwait(false);
  66. try
  67. {
  68. foreach (var topicFilter in unsubscribePacket.TopicFilters)
  69. {
  70. _subscriptions.Remove(topicFilter);
  71. TopicUnsubscribedCallback?.Invoke(_clientId, topicFilter);
  72. }
  73. }
  74. finally
  75. {
  76. _semaphore.Release();
  77. }
  78. return unsubscribePacket.CreateResponse<MqttUnsubAckPacket>();
  79. }
  80. public async Task<CheckSubscriptionsResult> CheckSubscriptionsAsync(MqttApplicationMessage applicationMessage)
  81. {
  82. if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
  83. await _semaphore.WaitAsync().ConfigureAwait(false);
  84. try
  85. {
  86. var qosLevels = new HashSet<MqttQualityOfServiceLevel>();
  87. foreach (var subscription in _subscriptions)
  88. {
  89. if (!MqttTopicFilterComparer.IsMatch(applicationMessage.Topic, subscription.Key))
  90. {
  91. continue;
  92. }
  93. qosLevels.Add(subscription.Value);
  94. }
  95. if (qosLevels.Count == 0)
  96. {
  97. return new CheckSubscriptionsResult
  98. {
  99. IsSubscribed = false
  100. };
  101. }
  102. return CreateSubscriptionResult(applicationMessage, qosLevels);
  103. }
  104. finally
  105. {
  106. _semaphore.Release();
  107. }
  108. }
  109. public void Dispose()
  110. {
  111. _semaphore?.Dispose();
  112. }
  113. private static MqttSubscribeReturnCode ConvertToMaximumQoS(MqttQualityOfServiceLevel qualityOfServiceLevel)
  114. {
  115. switch (qualityOfServiceLevel)
  116. {
  117. case MqttQualityOfServiceLevel.AtMostOnce: return MqttSubscribeReturnCode.SuccessMaximumQoS0;
  118. case MqttQualityOfServiceLevel.AtLeastOnce: return MqttSubscribeReturnCode.SuccessMaximumQoS1;
  119. case MqttQualityOfServiceLevel.ExactlyOnce: return MqttSubscribeReturnCode.SuccessMaximumQoS2;
  120. default: return MqttSubscribeReturnCode.Failure;
  121. }
  122. }
  123. private MqttSubscriptionInterceptorContext InterceptSubscribe(TopicFilter topicFilter)
  124. {
  125. var interceptorContext = new MqttSubscriptionInterceptorContext(_clientId, topicFilter);
  126. _options.SubscriptionInterceptor?.Invoke(interceptorContext);
  127. return interceptorContext;
  128. }
  129. private static CheckSubscriptionsResult CreateSubscriptionResult(MqttApplicationMessage applicationMessage, HashSet<MqttQualityOfServiceLevel> subscribedQoSLevels)
  130. {
  131. MqttQualityOfServiceLevel effectiveQoS;
  132. if (subscribedQoSLevels.Contains(applicationMessage.QualityOfServiceLevel))
  133. {
  134. effectiveQoS = applicationMessage.QualityOfServiceLevel;
  135. }
  136. else if (subscribedQoSLevels.Count == 1)
  137. {
  138. effectiveQoS = subscribedQoSLevels.First();
  139. }
  140. else
  141. {
  142. effectiveQoS = subscribedQoSLevels.Max();
  143. }
  144. return new CheckSubscriptionsResult
  145. {
  146. IsSubscribed = true,
  147. QualityOfServiceLevel = effectiveQoS
  148. };
  149. }
  150. }
  151. }