Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

186 lignes
7.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.Core.Adapter;
  7. using MQTTnet.Core.Diagnostics;
  8. using MQTTnet.Core.Exceptions;
  9. using MQTTnet.Core.Internal;
  10. using MQTTnet.Core.Packets;
  11. using MQTTnet.Core.Protocol;
  12. using MQTTnet.Core.Serializer;
  13. namespace MQTTnet.Core.Server
  14. {
  15. public sealed class MqttClientSessionsManager
  16. {
  17. private readonly Dictionary<string, MqttClientSession> _clientSessions = new Dictionary<string, MqttClientSession>();
  18. private readonly MqttServerOptions _options;
  19. private readonly MqttNetTrace _trace;
  20. public MqttClientSessionsManager(MqttServerOptions options, MqttNetTrace trace)
  21. {
  22. _trace = trace ?? throw new ArgumentNullException(nameof(trace));
  23. _options = options ?? throw new ArgumentNullException(nameof(options));
  24. RetainedMessagesManager = new MqttClientRetainedMessagesManager(options, trace);
  25. }
  26. public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
  27. public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
  28. public event EventHandler<MqttClientDisconnectedEventArgs> ClientDisconnected;
  29. public MqttClientRetainedMessagesManager RetainedMessagesManager { get; }
  30. public async Task RunClientSessionAsync(IMqttCommunicationAdapter clientAdapter)
  31. {
  32. var clientId = string.Empty;
  33. try
  34. {
  35. if (!(await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, CancellationToken.None).ConfigureAwait(false) is MqttConnectPacket connectPacket))
  36. {
  37. throw new MqttProtocolViolationException("The first packet from a client must be a 'CONNECT' packet [MQTT-3.1.0-1].");
  38. }
  39. clientId = connectPacket.ClientId;
  40. // Switch to the required protocol version before sending any response.
  41. clientAdapter.PacketSerializer.ProtocolVersion = connectPacket.ProtocolVersion;
  42. var connectReturnCode = ValidateConnection(connectPacket);
  43. if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
  44. {
  45. await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, CancellationToken.None, new MqttConnAckPacket
  46. {
  47. ConnectReturnCode = connectReturnCode
  48. }).ConfigureAwait(false);
  49. return;
  50. }
  51. var clientSession = GetOrCreateClientSession(connectPacket);
  52. await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, CancellationToken.None, new MqttConnAckPacket
  53. {
  54. ConnectReturnCode = connectReturnCode,
  55. IsSessionPresent = clientSession.IsExistingSession
  56. }).ConfigureAwait(false);
  57. ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(new ConnectedMqttClient
  58. {
  59. ClientId = clientId,
  60. ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion
  61. }));
  62. await clientSession.Session.RunAsync(connectPacket.WillMessage, clientAdapter).ConfigureAwait(false);
  63. }
  64. catch (Exception exception)
  65. {
  66. _trace.Error(nameof(MqttServer), exception, exception.Message);
  67. }
  68. finally
  69. {
  70. try
  71. {
  72. await clientAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
  73. }
  74. catch (Exception)
  75. {
  76. //ignored
  77. }
  78. ClientDisconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(new ConnectedMqttClient
  79. {
  80. ClientId = clientId,
  81. ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion
  82. }));
  83. }
  84. }
  85. public void Clear()
  86. {
  87. lock (_clientSessions)
  88. {
  89. _clientSessions.Clear();
  90. }
  91. }
  92. public IList<ConnectedMqttClient> GetConnectedClients()
  93. {
  94. lock (_clientSessions)
  95. {
  96. return _clientSessions.Where(s => s.Value.IsConnected).Select(s => new ConnectedMqttClient
  97. {
  98. ClientId = s.Value.ClientId,
  99. ProtocolVersion = s.Value.ProtocolVersion ?? MqttProtocolVersion.V311
  100. }).ToList();
  101. }
  102. }
  103. public void DispatchPublishPacket(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
  104. {
  105. try
  106. {
  107. var eventArgs = new MqttApplicationMessageReceivedEventArgs(senderClientSession?.ClientId, publishPacket.ToApplicationMessage());
  108. ApplicationMessageReceived?.Invoke(this, eventArgs);
  109. }
  110. catch (Exception exception)
  111. {
  112. _trace.Error(nameof(MqttClientSessionsManager), exception, "Error while processing application message");
  113. }
  114. lock (_clientSessions)
  115. {
  116. foreach (var clientSession in _clientSessions.Values.ToList())
  117. {
  118. clientSession.EnqueuePublishPacket(publishPacket);
  119. }
  120. }
  121. }
  122. private MqttConnectReturnCode ValidateConnection(MqttConnectPacket connectPacket)
  123. {
  124. if (_options.ConnectionValidator != null)
  125. {
  126. return _options.ConnectionValidator(connectPacket);
  127. }
  128. return MqttConnectReturnCode.ConnectionAccepted;
  129. }
  130. private GetOrCreateClientSessionResult GetOrCreateClientSession(MqttConnectPacket connectPacket)
  131. {
  132. lock (_clientSessions)
  133. {
  134. var isSessionPresent = _clientSessions.TryGetValue(connectPacket.ClientId, out var clientSession);
  135. if (isSessionPresent)
  136. {
  137. if (connectPacket.CleanSession)
  138. {
  139. _clientSessions.Remove(connectPacket.ClientId);
  140. clientSession.Dispose();
  141. clientSession = null;
  142. _trace.Verbose(nameof(MqttClientSessionsManager), "Disposed existing session of client '{0}'.", connectPacket.ClientId);
  143. }
  144. else
  145. {
  146. _trace.Verbose(nameof(MqttClientSessionsManager), "Reusing existing session of client '{0}'.", connectPacket.ClientId);
  147. }
  148. }
  149. var isExistingSession = true;
  150. if (clientSession == null)
  151. {
  152. isExistingSession = false;
  153. clientSession = new MqttClientSession(connectPacket.ClientId, _options, this, _trace);
  154. _clientSessions[connectPacket.ClientId] = clientSession;
  155. _trace.Verbose(nameof(MqttClientSessionsManager), "Created a new session for client '{0}'.", connectPacket.ClientId);
  156. }
  157. return new GetOrCreateClientSessionResult { IsExistingSession = isExistingSession, Session = clientSession };
  158. }
  159. }
  160. }
  161. }