You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

189 lines
6.2 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using MQTTnet.Core.Channel;
  7. using MQTTnet.Core.Client;
  8. using MQTTnet.Core.Diagnostics;
  9. using MQTTnet.Core.Exceptions;
  10. using MQTTnet.Core.Internal;
  11. using MQTTnet.Core.Packets;
  12. using MQTTnet.Core.Serializer;
  13. namespace MQTTnet.Core.Adapter
  14. {
  15. public class MqttChannelCommunicationAdapter : IMqttCommunicationAdapter
  16. {
  17. private readonly IMqttCommunicationChannel _channel;
  18. private Task _sendTask = Task.FromResult(0); // this task is used to prevent overlapping write
  19. public MqttChannelCommunicationAdapter(IMqttCommunicationChannel channel, IMqttPacketSerializer serializer)
  20. {
  21. _channel = channel ?? throw new ArgumentNullException(nameof(channel));
  22. PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
  23. }
  24. public IMqttPacketSerializer PacketSerializer { get; }
  25. public async Task ConnectAsync(TimeSpan timeout, MqttClientOptions options)
  26. {
  27. try
  28. {
  29. await _channel.ConnectAsync(options).TimeoutAfter(timeout).ConfigureAwait(false);
  30. }
  31. catch (TaskCanceledException)
  32. {
  33. throw;
  34. }
  35. catch (MqttCommunicationTimedOutException)
  36. {
  37. throw;
  38. }
  39. catch (MqttCommunicationException)
  40. {
  41. throw;
  42. }
  43. catch (Exception exception)
  44. {
  45. throw new MqttCommunicationException(exception);
  46. }
  47. }
  48. public async Task DisconnectAsync(TimeSpan timeout)
  49. {
  50. try
  51. {
  52. await _channel.DisconnectAsync().TimeoutAfter(timeout).ConfigureAwait(false);
  53. }
  54. catch (TaskCanceledException)
  55. {
  56. throw;
  57. }
  58. catch (MqttCommunicationTimedOutException)
  59. {
  60. throw;
  61. }
  62. catch (MqttCommunicationException)
  63. {
  64. throw;
  65. }
  66. catch (Exception exception)
  67. {
  68. throw new MqttCommunicationException(exception);
  69. }
  70. }
  71. public async Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets)
  72. {
  73. try
  74. {
  75. lock (_channel)
  76. {
  77. foreach (var packet in packets)
  78. {
  79. MqttTrace.Information(nameof(MqttChannelCommunicationAdapter), "TX >>> {0} [Timeout={1}]", packet, timeout);
  80. var writeBuffer = PacketSerializer.Serialize(packet);
  81. _sendTask = _sendTask.ContinueWith(p => _channel.SendStream.WriteAsync(writeBuffer, 0, writeBuffer.Length, cancellationToken).ConfigureAwait(false), cancellationToken);
  82. }
  83. }
  84. await _sendTask; // configure await false generates stackoverflow
  85. if (timeout > TimeSpan.Zero)
  86. {
  87. await _channel.SendStream.FlushAsync(cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
  88. }
  89. else
  90. {
  91. await _channel.SendStream.FlushAsync(cancellationToken).ConfigureAwait(false);
  92. }
  93. }
  94. catch (TaskCanceledException)
  95. {
  96. throw;
  97. }
  98. catch (MqttCommunicationTimedOutException)
  99. {
  100. throw;
  101. }
  102. catch (MqttCommunicationException)
  103. {
  104. throw;
  105. }
  106. catch (Exception exception)
  107. {
  108. throw new MqttCommunicationException(exception);
  109. }
  110. }
  111. public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
  112. {
  113. try
  114. {
  115. ReceivedMqttPacket receivedMqttPacket;
  116. if (timeout > TimeSpan.Zero)
  117. {
  118. receivedMqttPacket = await ReceiveAsync(_channel.RawReceiveStream, cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
  119. }
  120. else
  121. {
  122. receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).ConfigureAwait(false);
  123. }
  124. if (cancellationToken.IsCancellationRequested)
  125. {
  126. throw new TaskCanceledException();
  127. }
  128. var packet = PacketSerializer.Deserialize(receivedMqttPacket);
  129. if (packet == null)
  130. {
  131. throw new MqttProtocolViolationException("Received malformed packet.");
  132. }
  133. MqttTrace.Information(nameof(MqttChannelCommunicationAdapter), "RX <<< {0}", packet);
  134. return packet;
  135. }
  136. catch (TaskCanceledException)
  137. {
  138. throw;
  139. }
  140. catch (MqttCommunicationTimedOutException)
  141. {
  142. throw;
  143. }
  144. catch (MqttCommunicationException)
  145. {
  146. throw;
  147. }
  148. catch (Exception exception)
  149. {
  150. throw new MqttCommunicationException(exception);
  151. }
  152. }
  153. private static async Task<ReceivedMqttPacket> ReceiveAsync(Stream stream, CancellationToken cancellationToken)
  154. {
  155. var header = MqttPacketReader.ReadHeaderFromSource(stream, cancellationToken);
  156. if (header.BodyLength == 0)
  157. {
  158. return new ReceivedMqttPacket(header, new MemoryStream(0));
  159. }
  160. var body = new byte[header.BodyLength];
  161. var offset = 0;
  162. do
  163. {
  164. var readBytesCount = await stream.ReadAsync(body, offset, body.Length - offset, cancellationToken).ConfigureAwait(false);
  165. offset += readBytesCount;
  166. } while (offset < header.BodyLength);
  167. return new ReceivedMqttPacket(header, new MemoryStream(body, 0, body.Length));
  168. }
  169. }
  170. }