25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MqttClientSessionsManager.cs 7.9 KiB

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