Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 

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