Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 

246 righe
9.5 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.Adapter;
  7. using MQTTnet.Diagnostics;
  8. using MQTTnet.Exceptions;
  9. using MQTTnet.Packets;
  10. using MQTTnet.Protocol;
  11. using MQTTnet.Serializer;
  12. namespace MQTTnet.Server
  13. {
  14. public sealed class MqttClientSessionsManager
  15. {
  16. private readonly Dictionary<string, MqttClientSession> _sessions = new Dictionary<string, MqttClientSession>();
  17. private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
  18. private readonly IMqttServerOptions _options;
  19. private readonly MqttServer _server;
  20. private readonly MqttRetainedMessagesManager _retainedMessagesManager;
  21. private readonly IMqttNetLogger _logger;
  22. public MqttClientSessionsManager(IMqttServerOptions options, MqttRetainedMessagesManager retainedMessagesManager, MqttServer server, IMqttNetLogger logger)
  23. {
  24. _server = server ?? throw new ArgumentNullException(nameof(server));
  25. _logger = logger ?? throw new ArgumentNullException(nameof(logger));
  26. _options = options ?? throw new ArgumentNullException(nameof(options));
  27. _retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager));
  28. }
  29. public async Task RunClientSessionAsync(IMqttChannelAdapter clientAdapter, CancellationToken cancellationToken)
  30. {
  31. var clientId = string.Empty;
  32. try
  33. {
  34. if (!(await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false) is MqttConnectPacket connectPacket))
  35. {
  36. throw new MqttProtocolViolationException("The first packet from a client must be a 'CONNECT' packet [MQTT-3.1.0-1].");
  37. }
  38. clientId = connectPacket.ClientId;
  39. // Switch to the required protocol version before sending any response.
  40. clientAdapter.PacketSerializer.ProtocolVersion = connectPacket.ProtocolVersion;
  41. var connectReturnCode = ValidateConnection(connectPacket);
  42. if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
  43. {
  44. await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttConnAckPacket
  45. {
  46. ConnectReturnCode = connectReturnCode
  47. }).ConfigureAwait(false);
  48. return;
  49. }
  50. var clientSession = await GetOrCreateClientSessionAsync(connectPacket).ConfigureAwait(false);
  51. await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttConnAckPacket
  52. {
  53. ConnectReturnCode = connectReturnCode,
  54. IsSessionPresent = clientSession.IsExistingSession
  55. }).ConfigureAwait(false);
  56. _server.OnClientConnected(new ConnectedMqttClient
  57. {
  58. ClientId = clientId,
  59. ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion
  60. });
  61. await clientSession.Session.RunAsync(connectPacket, clientAdapter).ConfigureAwait(false);
  62. }
  63. catch (Exception exception)
  64. {
  65. _logger.Error<MqttClientSessionsManager>(exception, exception.Message);
  66. }
  67. finally
  68. {
  69. try
  70. {
  71. await clientAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
  72. }
  73. catch (Exception)
  74. {
  75. // ignored
  76. }
  77. _server.OnClientDisconnected(new ConnectedMqttClient
  78. {
  79. ClientId = clientId,
  80. ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion
  81. });
  82. }
  83. }
  84. public async Task StopAsync()
  85. {
  86. await _semaphore.WaitAsync().ConfigureAwait(false);
  87. try
  88. {
  89. foreach (var session in _sessions)
  90. {
  91. await session.Value.StopAsync().ConfigureAwait(false);
  92. }
  93. _sessions.Clear();
  94. }
  95. finally
  96. {
  97. _semaphore.Release();
  98. }
  99. }
  100. public async Task<IList<ConnectedMqttClient>> GetConnectedClientsAsync()
  101. {
  102. await _semaphore.WaitAsync().ConfigureAwait(false);
  103. try
  104. {
  105. return _sessions.Where(s => s.Value.IsConnected).Select(s => new ConnectedMqttClient
  106. {
  107. ClientId = s.Value.ClientId,
  108. ProtocolVersion = s.Value.ProtocolVersion ?? MqttProtocolVersion.V311,
  109. LastPacketReceived = s.Value.LastPacketReceived,
  110. LastNonKeepAlivePacketReceived = s.Value.LastNonKeepAlivePacketReceived
  111. }).ToList();
  112. }
  113. finally
  114. {
  115. _semaphore.Release();
  116. }
  117. }
  118. public async Task DispatchApplicationMessageAsync(MqttClientSession senderClientSession, MqttApplicationMessage applicationMessage)
  119. {
  120. try
  121. {
  122. applicationMessage = InterceptApplicationMessage(senderClientSession, applicationMessage);
  123. if (applicationMessage == null)
  124. {
  125. return;
  126. }
  127. if (applicationMessage.Retain)
  128. {
  129. await _retainedMessagesManager.HandleMessageAsync(senderClientSession?.ClientId, applicationMessage).ConfigureAwait(false);
  130. }
  131. _server.OnApplicationMessageReceived(senderClientSession?.ClientId, applicationMessage);
  132. }
  133. catch (Exception exception)
  134. {
  135. _logger.Error<MqttClientSessionsManager>(exception, "Error while processing application message");
  136. }
  137. await _semaphore.WaitAsync().ConfigureAwait(false);
  138. try
  139. {
  140. foreach (var clientSession in _sessions.Values)
  141. {
  142. await clientSession.EnqueueApplicationMessageAsync(applicationMessage);
  143. }
  144. }
  145. finally
  146. {
  147. _semaphore.Release();
  148. }
  149. }
  150. private MqttApplicationMessage InterceptApplicationMessage(MqttClientSession senderClientSession, MqttApplicationMessage applicationMessage)
  151. {
  152. if (_options.ApplicationMessageInterceptor == null)
  153. {
  154. return applicationMessage;
  155. }
  156. var interceptorContext = new MqttApplicationMessageInterceptorContext(
  157. senderClientSession.ClientId,
  158. applicationMessage);
  159. _options.ApplicationMessageInterceptor(interceptorContext);
  160. return interceptorContext.ApplicationMessage;
  161. }
  162. private MqttConnectReturnCode ValidateConnection(MqttConnectPacket connectPacket)
  163. {
  164. if (_options.ConnectionValidator == null)
  165. {
  166. return MqttConnectReturnCode.ConnectionAccepted;
  167. }
  168. var context = new MqttConnectionValidatorContext(
  169. connectPacket.ClientId,
  170. connectPacket.Username,
  171. connectPacket.Password,
  172. connectPacket.WillMessage);
  173. _options.ConnectionValidator(context);
  174. return context.ReturnCode;
  175. }
  176. private async Task<GetOrCreateClientSessionResult> GetOrCreateClientSessionAsync(MqttConnectPacket connectPacket)
  177. {
  178. await _semaphore.WaitAsync().ConfigureAwait(false);
  179. try
  180. {
  181. var isSessionPresent = _sessions.TryGetValue(connectPacket.ClientId, out var clientSession);
  182. if (isSessionPresent)
  183. {
  184. if (connectPacket.CleanSession)
  185. {
  186. _sessions.Remove(connectPacket.ClientId);
  187. await clientSession.StopAsync().ConfigureAwait(false);
  188. clientSession.Dispose();
  189. clientSession = null;
  190. _logger.Trace<MqttClientSessionsManager>("Stopped existing session of client '{0}'.", connectPacket.ClientId);
  191. }
  192. else
  193. {
  194. _logger.Trace<MqttClientSessionsManager>("Reusing existing session of client '{0}'.", connectPacket.ClientId);
  195. }
  196. }
  197. var isExistingSession = true;
  198. if (clientSession == null)
  199. {
  200. isExistingSession = false;
  201. clientSession = new MqttClientSession(connectPacket.ClientId, _options, _retainedMessagesManager, this, _logger);
  202. _sessions[connectPacket.ClientId] = clientSession;
  203. _logger.Trace<MqttClientSessionsManager>("Created a new session for client '{0}'.", connectPacket.ClientId);
  204. }
  205. return new GetOrCreateClientSessionResult { IsExistingSession = isExistingSession, Session = clientSession };
  206. }
  207. finally
  208. {
  209. _semaphore.Release();
  210. }
  211. }
  212. }
  213. }