using System; using System.Collections.Concurrent; using System.Threading.Tasks; using MQTTnet.Diagnostics; using MQTTnet.Exceptions; using MQTTnet.Internal; using MQTTnet.Packets; namespace MQTTnet.Client { public class MqttPacketDispatcher { private readonly ConcurrentDictionary, TaskCompletionSource> _awaiters = new ConcurrentDictionary, TaskCompletionSource>(); private readonly IMqttNetLogger _logger; public MqttPacketDispatcher(IMqttNetLogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task WaitForPacketAsync(Type responseType, ushort? identifier, TimeSpan timeout) { var packetAwaiter = AddPacketAwaiter(responseType, identifier); try { return await packetAwaiter.Task.TimeoutAfter(timeout).ConfigureAwait(false); } catch (MqttCommunicationTimedOutException) { _logger.Warning("Timeout while waiting for packet of type '{0}'.", responseType.Name); throw; } finally { RemovePacketAwaiter(responseType, identifier); } } public void Dispatch(MqttBasePacket packet) { if (packet == null) throw new ArgumentNullException(nameof(packet)); ushort? identifier = 0; if (packet is IMqttPacketWithIdentifier packetWithIdentifier) { identifier = packetWithIdentifier.PacketIdentifier; } var type = packet.GetType(); var key = new Tuple(identifier, type); if (_awaiters.TryRemove(key, out var tcs)) { tcs.TrySetResult(packet); return; } throw new InvalidOperationException($"Packet of type '{type.Name}' not handled or dispatched."); } public void Reset() { _awaiters.Clear(); } private TaskCompletionSource AddPacketAwaiter(Type responseType, ushort? identifier) { var tcs = new TaskCompletionSource(); if (!identifier.HasValue) { identifier = 0; } var dictionaryKey = new Tuple(identifier, responseType); if (!_awaiters.TryAdd(dictionaryKey,tcs)) { throw new InvalidOperationException($"The packet dispatcher already has an awaiter for packet of type '{responseType}' with identifier {identifier}."); } return tcs; } private void RemovePacketAwaiter(Type responseType, ushort? identifier) { if (!identifier.HasValue) { identifier = 0; } var dictionaryKey = new Tuple(identifier, responseType); _awaiters.TryRemove(dictionaryKey, out var _); } } }