Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 

282 řádky
12 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. using MQTTnet.Core.Adapter;
  6. using MQTTnet.Core.Exceptions;
  7. using MQTTnet.Core.Internal;
  8. using MQTTnet.Core.Packets;
  9. using MQTTnet.Core.Protocol;
  10. using MQTTnet.Core.Serializer;
  11. using Microsoft.Extensions.Logging;
  12. using Microsoft.Extensions.Options;
  13. namespace MQTTnet.Core.Server
  14. {
  15. public sealed class MqttClientSession
  16. {
  17. private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();
  18. private readonly IMqttClientRetainedMessageManager _clientRetainedMessageManager;
  19. private readonly MqttClientSubscriptionsManager _subscriptionsManager;
  20. private readonly MqttClientSessionsManager _sessionsManager;
  21. private readonly MqttClientPendingMessagesQueue _pendingMessagesQueue;
  22. private readonly MqttServerOptions _options;
  23. private readonly ILogger<MqttClientSession> _logger;
  24. private IMqttCommunicationAdapter _adapter;
  25. private CancellationTokenSource _cancellationTokenSource;
  26. private MqttApplicationMessage _willMessage;
  27. public MqttClientSession(
  28. string clientId,
  29. IOptions<MqttServerOptions> options,
  30. MqttClientSessionsManager sessionsManager,
  31. MqttClientSubscriptionsManager subscriptionsManager,
  32. ILogger<MqttClientSession> logger,
  33. ILogger<MqttClientPendingMessagesQueue> messageQueueLogger,
  34. IMqttClientRetainedMessageManager clientRetainedMessageManager)
  35. {
  36. _clientRetainedMessageManager = clientRetainedMessageManager ?? throw new ArgumentNullException(nameof(clientRetainedMessageManager));
  37. _sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
  38. _subscriptionsManager = subscriptionsManager ?? throw new ArgumentNullException(nameof(subscriptionsManager));
  39. _logger = logger ?? throw new ArgumentNullException(nameof(logger));
  40. ClientId = clientId;
  41. _options = options.Value;
  42. _pendingMessagesQueue = new MqttClientPendingMessagesQueue(_options, this, messageQueueLogger);
  43. }
  44. public string ClientId { get; }
  45. public MqttProtocolVersion? ProtocolVersion => _adapter?.PacketSerializer.ProtocolVersion;
  46. public bool IsConnected => _adapter != null;
  47. public async Task RunAsync(MqttApplicationMessage willMessage, IMqttCommunicationAdapter adapter)
  48. {
  49. if (adapter == null) throw new ArgumentNullException(nameof(adapter));
  50. try
  51. {
  52. var cancellationTokenSource = new CancellationTokenSource();
  53. _willMessage = willMessage;
  54. _adapter = adapter;
  55. _cancellationTokenSource = cancellationTokenSource;
  56. _pendingMessagesQueue.Start(adapter, cancellationTokenSource.Token);
  57. await ReceivePacketsAsync(adapter, cancellationTokenSource.Token).ConfigureAwait(false);
  58. }
  59. catch (OperationCanceledException)
  60. {
  61. }
  62. catch (MqttCommunicationException exception)
  63. {
  64. _logger.LogWarning(new EventId(), exception, "Client '{0}': Communication exception while processing client packets.", ClientId);
  65. }
  66. catch (Exception exception)
  67. {
  68. _logger.LogError(new EventId(), exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId);
  69. }
  70. }
  71. public void Stop()
  72. {
  73. try
  74. {
  75. _cancellationTokenSource?.Cancel(false);
  76. _cancellationTokenSource?.Dispose();
  77. _cancellationTokenSource = null;
  78. _adapter = null;
  79. _logger.LogInformation("Client '{0}': Disconnected.", ClientId);
  80. }
  81. finally
  82. {
  83. var willMessage = _willMessage;
  84. if (willMessage != null)
  85. {
  86. _willMessage = null; //clear willmessage so it is send just once
  87. _sessionsManager.DispatchApplicationMessage(this, willMessage);
  88. }
  89. }
  90. }
  91. public void EnqueuePublishPacket(MqttPublishPacket publishPacket)
  92. {
  93. if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
  94. if (!_subscriptionsManager.IsSubscribed(publishPacket))
  95. {
  96. return;
  97. }
  98. _pendingMessagesQueue.Enqueue(publishPacket);
  99. }
  100. private async Task ReceivePacketsAsync(IMqttCommunicationAdapter adapter, CancellationToken cancellationToken)
  101. {
  102. try
  103. {
  104. while (!cancellationToken.IsCancellationRequested)
  105. {
  106. var packet = await adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false);
  107. await ProcessReceivedPacketAsync(adapter, packet, cancellationToken).ConfigureAwait(false);
  108. }
  109. }
  110. catch (OperationCanceledException)
  111. {
  112. }
  113. catch (MqttCommunicationException exception)
  114. {
  115. _logger.LogWarning(new EventId(), exception, "Client '{0}': Communication exception while processing client packets.", ClientId);
  116. Stop();
  117. }
  118. catch (Exception exception)
  119. {
  120. _logger.LogError(new EventId(), exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId);
  121. Stop();
  122. }
  123. }
  124. private Task ProcessReceivedPacketAsync(IMqttCommunicationAdapter adapter, MqttBasePacket packet, CancellationToken cancellationToken)
  125. {
  126. if (packet is MqttPingReqPacket)
  127. {
  128. return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttPingRespPacket());
  129. }
  130. if (packet is MqttPublishPacket publishPacket)
  131. {
  132. return HandleIncomingPublishPacketAsync(adapter, publishPacket, cancellationToken);
  133. }
  134. if (packet is MqttPubRelPacket pubRelPacket)
  135. {
  136. return HandleIncomingPubRelPacketAsync(adapter, pubRelPacket, cancellationToken);
  137. }
  138. if (packet is MqttPubRecPacket pubRecPacket)
  139. {
  140. return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, pubRecPacket.CreateResponse<MqttPubRelPacket>());
  141. }
  142. if (packet is MqttPubAckPacket || packet is MqttPubCompPacket)
  143. {
  144. // Discard message.
  145. return Task.FromResult(0);
  146. }
  147. if (packet is MqttSubscribePacket subscribePacket)
  148. {
  149. return HandleIncomingSubscribePacketAsync(adapter, subscribePacket, cancellationToken);
  150. }
  151. if (packet is MqttUnsubscribePacket unsubscribePacket)
  152. {
  153. return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, _subscriptionsManager.Unsubscribe(unsubscribePacket));
  154. }
  155. if (packet is MqttDisconnectPacket || packet is MqttConnectPacket)
  156. {
  157. Stop();
  158. return Task.FromResult(0);
  159. }
  160. _logger.LogWarning("Client '{0}': Received not supported packet ({1}). Closing connection.", ClientId, packet);
  161. Stop();
  162. return Task.FromResult(0);
  163. }
  164. private async Task HandleIncomingSubscribePacketAsync(IMqttCommunicationAdapter adapter, MqttSubscribePacket subscribePacket, CancellationToken cancellationToken)
  165. {
  166. var subscribeResult = _subscriptionsManager.Subscribe(subscribePacket, ClientId);
  167. await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, subscribeResult.ResponsePacket).ConfigureAwait(false);
  168. await EnqueueSubscribedRetainedMessagesAsync(subscribePacket).ConfigureAwait(false);
  169. if (subscribeResult.CloseConnection)
  170. {
  171. await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttDisconnectPacket()).ConfigureAwait(false);
  172. Stop();
  173. }
  174. }
  175. private async Task EnqueueSubscribedRetainedMessagesAsync(MqttSubscribePacket subscribePacket)
  176. {
  177. var retainedMessages = await _clientRetainedMessageManager.GetSubscribedMessagesAsync(subscribePacket).ConfigureAwait(false);
  178. foreach (var publishPacket in retainedMessages)
  179. {
  180. EnqueuePublishPacket(publishPacket.ToPublishPacket());
  181. }
  182. }
  183. private async Task HandleIncomingPublishPacketAsync(IMqttCommunicationAdapter adapter, MqttPublishPacket publishPacket, CancellationToken cancellationToken)
  184. {
  185. var applicationMessage = publishPacket.ToApplicationMessage();
  186. var interceptorContext = new MqttApplicationMessageInterceptorContext
  187. {
  188. ApplicationMessage = applicationMessage
  189. };
  190. _options.ApplicationMessageInterceptor?.Invoke(interceptorContext);
  191. applicationMessage = interceptorContext.ApplicationMessage;
  192. if (applicationMessage.Retain)
  193. {
  194. await _clientRetainedMessageManager.HandleMessageAsync(ClientId, applicationMessage).ConfigureAwait(false);
  195. }
  196. switch (applicationMessage.QualityOfServiceLevel)
  197. {
  198. case MqttQualityOfServiceLevel.AtMostOnce:
  199. {
  200. _sessionsManager.DispatchApplicationMessage(this, applicationMessage);
  201. return;
  202. }
  203. case MqttQualityOfServiceLevel.AtLeastOnce:
  204. {
  205. _sessionsManager.DispatchApplicationMessage(this, applicationMessage);
  206. await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken,
  207. new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
  208. return;
  209. }
  210. case MqttQualityOfServiceLevel.ExactlyOnce:
  211. {
  212. // QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
  213. lock (_unacknowledgedPublishPackets)
  214. {
  215. _unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
  216. }
  217. _sessionsManager.DispatchApplicationMessage(this, applicationMessage);
  218. await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken,
  219. new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }).ConfigureAwait(false);
  220. return;
  221. }
  222. default:
  223. throw new MqttCommunicationException("Received a not supported QoS level.");
  224. }
  225. }
  226. private Task HandleIncomingPubRelPacketAsync(IMqttCommunicationAdapter adapter, MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
  227. {
  228. lock (_unacknowledgedPublishPackets)
  229. {
  230. _unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
  231. }
  232. return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttPubCompPacket { PacketIdentifier = pubRelPacket.PacketIdentifier });
  233. }
  234. }
  235. }