No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 

324 líneas
14 KiB

  1. using System;
  2. using System.Collections.Generic;
  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. using MQTTnet.Serializer;
  12. namespace MQTTnet.Server
  13. {
  14. public sealed class MqttClientSession : IDisposable
  15. {
  16. private readonly IMqttServerOptions _options;
  17. private readonly IMqttNetLogger _logger;
  18. private readonly MqttRetainedMessagesManager _retainedMessagesManager;
  19. private IMqttChannelAdapter _adapter;
  20. private CancellationTokenSource _cancellationTokenSource;
  21. private MqttApplicationMessage _willMessage;
  22. public MqttClientSession(
  23. string clientId,
  24. IMqttServerOptions options,
  25. MqttRetainedMessagesManager retainedMessagesManager,
  26. IMqttNetLogger logger)
  27. {
  28. _options = options ?? throw new ArgumentNullException(nameof(options));
  29. _retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager));
  30. _logger = logger ?? throw new ArgumentNullException(nameof(logger));
  31. ClientId = clientId;
  32. KeepAliveMonitor = new MqttClientKeepAliveMonitor(clientId, StopDueToKeepAliveTimeoutAsync, _logger);
  33. SubscriptionsManager = new MqttClientSubscriptionsManager(_options, clientId);
  34. PendingMessagesQueue = new MqttClientPendingMessagesQueue(_options, this, _logger);
  35. }
  36. public Func<MqttClientSession, MqttApplicationMessage, Task> ApplicationMessageReceivedCallback { get; set; }
  37. public MqttClientSubscriptionsManager SubscriptionsManager { get; }
  38. public MqttClientPendingMessagesQueue PendingMessagesQueue { get; }
  39. public MqttClientKeepAliveMonitor KeepAliveMonitor { get; }
  40. public string ClientId { get; }
  41. public MqttProtocolVersion? ProtocolVersion => _adapter?.PacketSerializer.ProtocolVersion;
  42. public bool IsConnected => _adapter != null;
  43. public async Task RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter)
  44. {
  45. if (connectPacket == null) throw new ArgumentNullException(nameof(connectPacket));
  46. if (adapter == null) throw new ArgumentNullException(nameof(adapter));
  47. try
  48. {
  49. var cancellationTokenSource = new CancellationTokenSource();
  50. _willMessage = connectPacket.WillMessage;
  51. _adapter = adapter;
  52. _cancellationTokenSource = cancellationTokenSource;
  53. PendingMessagesQueue.Start(adapter, cancellationTokenSource.Token);
  54. KeepAliveMonitor.Start(connectPacket.KeepAlivePeriod, cancellationTokenSource.Token);
  55. await ReceivePacketsAsync(adapter, cancellationTokenSource.Token).ConfigureAwait(false);
  56. }
  57. catch (OperationCanceledException)
  58. {
  59. }
  60. catch (MqttCommunicationException exception)
  61. {
  62. _logger.Warning<MqttClientSession>(exception, "Client '{0}': Communication exception while processing client packets.", ClientId);
  63. }
  64. catch (Exception exception)
  65. {
  66. _logger.Error<MqttClientSession>(exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId);
  67. }
  68. }
  69. public async Task StopAsync()
  70. {
  71. try
  72. {
  73. if (_cancellationTokenSource == null)
  74. {
  75. return;
  76. }
  77. _cancellationTokenSource?.Cancel(false);
  78. _cancellationTokenSource?.Dispose();
  79. _cancellationTokenSource = null;
  80. if (_adapter != null)
  81. {
  82. await _adapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
  83. _adapter = null;
  84. }
  85. _logger.Info<MqttClientSession>("Client '{0}': Session stopped.", ClientId);
  86. }
  87. finally
  88. {
  89. var willMessage = _willMessage;
  90. if (willMessage != null)
  91. {
  92. _willMessage = null; // clear willmessage so it is send just once
  93. await ApplicationMessageReceivedCallback(this, willMessage).ConfigureAwait(false);
  94. }
  95. }
  96. }
  97. public async Task EnqueueApplicationMessageAsync(MqttApplicationMessage applicationMessage)
  98. {
  99. if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
  100. var result = await SubscriptionsManager.CheckSubscriptionsAsync(applicationMessage);
  101. if (!result.IsSubscribed)
  102. {
  103. return;
  104. }
  105. var publishPacket = applicationMessage.ToPublishPacket();
  106. publishPacket.QualityOfServiceLevel = result.QualityOfServiceLevel;
  107. PendingMessagesQueue.Enqueue(publishPacket);
  108. }
  109. public async Task SubscribeAsync(IList<TopicFilter> topicFilters)
  110. {
  111. if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
  112. await SubscriptionsManager.SubscribeAsync(new MqttSubscribePacket
  113. {
  114. TopicFilters = topicFilters
  115. }).ConfigureAwait(false);
  116. await EnqueueSubscribedRetainedMessagesAsync(topicFilters).ConfigureAwait(false);
  117. }
  118. public Task UnsubscribeAsync(IList<string> topicFilters)
  119. {
  120. if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
  121. return SubscriptionsManager.UnsubscribeAsync(new MqttUnsubscribePacket
  122. {
  123. TopicFilters = topicFilters
  124. });
  125. }
  126. public void Dispose()
  127. {
  128. ApplicationMessageReceivedCallback = null;
  129. SubscriptionsManager?.Dispose();
  130. PendingMessagesQueue?.Dispose();
  131. _cancellationTokenSource?.Dispose();
  132. }
  133. private Task StopDueToKeepAliveTimeoutAsync()
  134. {
  135. _logger.Info<MqttClientSession>("Client '{0}': Timeout while waiting for KeepAlive packet.", ClientId);
  136. return StopAsync();
  137. }
  138. private async Task ReceivePacketsAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken)
  139. {
  140. try
  141. {
  142. while (!cancellationToken.IsCancellationRequested)
  143. {
  144. var packet = await adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false);
  145. KeepAliveMonitor.PacketReceived(packet);
  146. await ProcessReceivedPacketAsync(adapter, packet, cancellationToken).ConfigureAwait(false);
  147. }
  148. }
  149. catch (OperationCanceledException)
  150. {
  151. }
  152. catch (MqttCommunicationException exception)
  153. {
  154. _logger.Warning<MqttClientSession>(exception, "Client '{0}': Communication exception while processing client packets.", ClientId);
  155. await StopAsync().ConfigureAwait(false);
  156. }
  157. catch (Exception exception)
  158. {
  159. _logger.Error<MqttClientSession>(exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId);
  160. await StopAsync().ConfigureAwait(false);
  161. }
  162. }
  163. private Task ProcessReceivedPacketAsync(IMqttChannelAdapter adapter, MqttBasePacket packet, CancellationToken cancellationToken)
  164. {
  165. if (packet is MqttPublishPacket publishPacket)
  166. {
  167. return HandleIncomingPublishPacketAsync(adapter, publishPacket, cancellationToken);
  168. }
  169. if (packet is MqttPingReqPacket)
  170. {
  171. return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttPingRespPacket());
  172. }
  173. if (packet is MqttPubRelPacket pubRelPacket)
  174. {
  175. return HandleIncomingPubRelPacketAsync(adapter, pubRelPacket, cancellationToken);
  176. }
  177. if (packet is MqttPubRecPacket pubRecPacket)
  178. {
  179. return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, pubRecPacket.CreateResponse<MqttPubRelPacket>());
  180. }
  181. if (packet is MqttPubAckPacket || packet is MqttPubCompPacket)
  182. {
  183. // Discard message.
  184. return Task.FromResult(0);
  185. }
  186. if (packet is MqttSubscribePacket subscribePacket)
  187. {
  188. return HandleIncomingSubscribePacketAsync(adapter, subscribePacket, cancellationToken);
  189. }
  190. if (packet is MqttUnsubscribePacket unsubscribePacket)
  191. {
  192. return HandleIncomingUnsubscribePacketAsync(adapter, unsubscribePacket, cancellationToken);
  193. }
  194. if (packet is MqttDisconnectPacket || packet is MqttConnectPacket)
  195. {
  196. return StopAsync();
  197. }
  198. _logger.Warning<MqttClientSession>("Client '{0}': Received not supported packet ({1}). Closing connection.", ClientId, packet);
  199. return StopAsync();
  200. }
  201. private async Task HandleIncomingSubscribePacketAsync(IMqttChannelAdapter adapter, MqttSubscribePacket subscribePacket, CancellationToken cancellationToken)
  202. {
  203. var subscribeResult = await SubscriptionsManager.SubscribeAsync(subscribePacket).ConfigureAwait(false);
  204. await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, subscribeResult.ResponsePacket).ConfigureAwait(false);
  205. if (subscribeResult.CloseConnection)
  206. {
  207. await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttDisconnectPacket()).ConfigureAwait(false);
  208. await StopAsync().ConfigureAwait(false);
  209. }
  210. await EnqueueSubscribedRetainedMessagesAsync(subscribePacket.TopicFilters).ConfigureAwait(false);
  211. }
  212. private async Task HandleIncomingUnsubscribePacketAsync(IMqttChannelAdapter adapter, MqttUnsubscribePacket unsubscribePacket, CancellationToken cancellationToken)
  213. {
  214. var unsubscribeResult = await SubscriptionsManager.UnsubscribeAsync(unsubscribePacket).ConfigureAwait(false);
  215. await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, unsubscribeResult);
  216. }
  217. private async Task EnqueueSubscribedRetainedMessagesAsync(ICollection<TopicFilter> topicFilters)
  218. {
  219. var retainedMessages = await _retainedMessagesManager.GetSubscribedMessagesAsync(topicFilters);
  220. foreach (var applicationMessage in retainedMessages)
  221. {
  222. await EnqueueApplicationMessageAsync(applicationMessage).ConfigureAwait(false);
  223. }
  224. }
  225. private Task HandleIncomingPublishPacketAsync(IMqttChannelAdapter adapter, MqttPublishPacket publishPacket, CancellationToken cancellationToken)
  226. {
  227. var applicationMessage = publishPacket.ToApplicationMessage();
  228. switch (applicationMessage.QualityOfServiceLevel)
  229. {
  230. case MqttQualityOfServiceLevel.AtMostOnce:
  231. {
  232. return ApplicationMessageReceivedCallback?.Invoke(this, applicationMessage);
  233. }
  234. case MqttQualityOfServiceLevel.AtLeastOnce:
  235. {
  236. return HandleIncomingPublishPacketWithQoS1(adapter, applicationMessage, publishPacket, cancellationToken);
  237. }
  238. case MqttQualityOfServiceLevel.ExactlyOnce:
  239. {
  240. return HandleIncomingPublishPacketWithQoS2(adapter, applicationMessage, publishPacket, cancellationToken);
  241. }
  242. default:
  243. {
  244. throw new MqttCommunicationException("Received a not supported QoS level.");
  245. }
  246. }
  247. }
  248. private async Task HandleIncomingPublishPacketWithQoS1(IMqttChannelAdapter adapter, MqttApplicationMessage applicationMessage, MqttPublishPacket publishPacket, CancellationToken cancellationToken)
  249. {
  250. await ApplicationMessageReceivedCallback(this, applicationMessage).ConfigureAwait(false);
  251. var response = new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier };
  252. await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, response).ConfigureAwait(false);
  253. }
  254. private async Task HandleIncomingPublishPacketWithQoS2(IMqttChannelAdapter adapter, MqttApplicationMessage applicationMessage, MqttPublishPacket publishPacket, CancellationToken cancellationToken)
  255. {
  256. // QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
  257. await ApplicationMessageReceivedCallback(this, applicationMessage).ConfigureAwait(false);
  258. var response = new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier };
  259. await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, response).ConfigureAwait(false);
  260. }
  261. private Task HandleIncomingPubRelPacketAsync(IMqttChannelAdapter adapter, MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
  262. {
  263. var response = new MqttPubCompPacket { PacketIdentifier = pubRelPacket.PacketIdentifier };
  264. return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, response);
  265. }
  266. }
  267. }