Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

MqttChannelAdapter.cs 9.3 KiB

7 år sedan
7 år sedan
7 år sedan
7 år sedan
7 år sedan
7 år sedan
7 år sedan
7 år sedan
7 år sedan
7 år sedan
7 år sedan
7 år sedan
7 år sedan
7 år sedan
7 år sedan
7 år sedan
7 år sedan
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Net.Sockets;
  6. using System.Runtime.ExceptionServices;
  7. using System.Runtime.InteropServices;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. using MQTTnet.Channel;
  11. using MQTTnet.Diagnostics;
  12. using MQTTnet.Exceptions;
  13. using MQTTnet.Internal;
  14. using MQTTnet.Packets;
  15. using MQTTnet.Serializer;
  16. namespace MQTTnet.Adapter
  17. {
  18. public sealed class MqttChannelAdapter : IMqttChannelAdapter
  19. {
  20. private const uint ErrorOperationAborted = 0x800703E3;
  21. private const int ReadBufferSize = 4096; // TODO: Move buffer size to config
  22. private bool _isDisposed;
  23. private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
  24. private readonly IMqttNetLogger _logger;
  25. private readonly IMqttChannel _channel;
  26. public MqttChannelAdapter(IMqttChannel channel, IMqttPacketSerializer serializer, IMqttNetLogger logger)
  27. {
  28. _logger = logger ?? throw new ArgumentNullException(nameof(logger));
  29. _channel = channel ?? throw new ArgumentNullException(nameof(channel));
  30. PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
  31. }
  32. public IMqttPacketSerializer PacketSerializer { get; }
  33. public Task ConnectAsync(TimeSpan timeout)
  34. {
  35. ThrowIfDisposed();
  36. _logger.Verbose<MqttChannelAdapter>("Connecting [Timeout={0}]", timeout);
  37. return ExecuteAndWrapExceptionAsync(() => _channel.ConnectAsync().TimeoutAfter(timeout));
  38. }
  39. public Task DisconnectAsync(TimeSpan timeout)
  40. {
  41. ThrowIfDisposed();
  42. _logger.Verbose<MqttChannelAdapter>("Disconnecting [Timeout={0}]", timeout);
  43. return ExecuteAndWrapExceptionAsync(() => _channel.DisconnectAsync().TimeoutAfter(timeout));
  44. }
  45. public Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets)
  46. {
  47. ThrowIfDisposed();
  48. return ExecuteAndWrapExceptionAsync(async () =>
  49. {
  50. foreach (var packet in packets)
  51. {
  52. if (cancellationToken.IsCancellationRequested)
  53. {
  54. return;
  55. }
  56. if (packet == null)
  57. {
  58. continue;
  59. }
  60. _logger.Verbose<MqttChannelAdapter>("TX >>> {0} [Timeout={1}]", packet, timeout);
  61. var packetData = PacketSerializer.Serialize(packet);
  62. if (cancellationToken.IsCancellationRequested)
  63. {
  64. return;
  65. }
  66. await _channel.SendStream.WriteAsync(packetData.Array, packetData.Offset, (int)packetData.Count, cancellationToken).ConfigureAwait(false);
  67. }
  68. if (cancellationToken.IsCancellationRequested)
  69. {
  70. return;
  71. }
  72. if (timeout > TimeSpan.Zero)
  73. {
  74. await _channel.SendStream.FlushAsync(cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
  75. }
  76. else
  77. {
  78. await _channel.SendStream.FlushAsync(cancellationToken).ConfigureAwait(false);
  79. }
  80. });
  81. }
  82. public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
  83. {
  84. ThrowIfDisposed();
  85. MqttBasePacket packet = null;
  86. await ExecuteAndWrapExceptionAsync(async () =>
  87. {
  88. ReceivedMqttPacket receivedMqttPacket = null;
  89. try
  90. {
  91. if (timeout > TimeSpan.Zero)
  92. {
  93. var timeoutCts = new CancellationTokenSource(timeout);
  94. var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
  95. try
  96. {
  97. receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, linkedCts.Token).ConfigureAwait(false);
  98. }
  99. catch(OperationCanceledException ex)
  100. {
  101. //check if timed out
  102. if(linkedCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
  103. {
  104. //only timeout token was cancelled
  105. throw new MqttCommunicationTimedOutException(ex);
  106. }
  107. else
  108. {
  109. throw;
  110. }
  111. }
  112. }
  113. else
  114. {
  115. receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).ConfigureAwait(false);
  116. }
  117. if (receivedMqttPacket == null || cancellationToken.IsCancellationRequested)
  118. {
  119. throw new TaskCanceledException();
  120. }
  121. packet = PacketSerializer.Deserialize(receivedMqttPacket.Header, receivedMqttPacket.Body);
  122. if (packet == null)
  123. {
  124. throw new MqttProtocolViolationException("Received malformed packet.");
  125. }
  126. _logger.Verbose<MqttChannelAdapter>("RX <<< {0}", packet);
  127. }
  128. finally
  129. {
  130. receivedMqttPacket?.Dispose();
  131. }
  132. }).ConfigureAwait(false);
  133. return packet;
  134. }
  135. private static async Task<ReceivedMqttPacket> ReceiveAsync(Stream stream, CancellationToken cancellationToken)
  136. {
  137. var header = await MqttPacketReader.ReadHeaderAsync(stream, cancellationToken).ConfigureAwait(false);
  138. if (header == null)
  139. {
  140. return null;
  141. }
  142. if (header.BodyLength == 0)
  143. {
  144. return new ReceivedMqttPacket(header, new MemoryStream(new byte[0], false));
  145. }
  146. var body = header.BodyLength <= ReadBufferSize ? new MemoryStream(header.BodyLength) : new MemoryStream();
  147. var buffer = new byte[ReadBufferSize];
  148. while (body.Length < header.BodyLength)
  149. {
  150. var bytesLeft = header.BodyLength - (int)body.Length;
  151. if (bytesLeft > buffer.Length)
  152. {
  153. bytesLeft = buffer.Length;
  154. }
  155. var readBytesCount = await stream.ReadAsync(buffer, 0, bytesLeft, cancellationToken).ConfigureAwait(false);
  156. // Check if the client closed the connection before sending the full body.
  157. if (readBytesCount == 0)
  158. {
  159. throw new MqttCommunicationException("Connection closed while reading remaining packet body.");
  160. }
  161. // Here is no need to await because internally only an array is used and no real I/O operation is made.
  162. // Using async here will only generate overhead.
  163. body.Write(buffer, 0, readBytesCount);
  164. }
  165. body.Seek(0L, SeekOrigin.Begin);
  166. return new ReceivedMqttPacket(header, body);
  167. }
  168. private static async Task ExecuteAndWrapExceptionAsync(Func<Task> action)
  169. {
  170. try
  171. {
  172. await action().ConfigureAwait(false);
  173. }
  174. catch (TaskCanceledException)
  175. {
  176. throw;
  177. }
  178. catch (OperationCanceledException)
  179. {
  180. throw;
  181. }
  182. catch (MqttCommunicationTimedOutException)
  183. {
  184. throw;
  185. }
  186. catch (MqttCommunicationException)
  187. {
  188. throw;
  189. }
  190. catch (COMException comException)
  191. {
  192. if ((uint)comException.HResult == ErrorOperationAborted)
  193. {
  194. throw new OperationCanceledException();
  195. }
  196. throw new MqttCommunicationException(comException);
  197. }
  198. catch (IOException exception)
  199. {
  200. if (exception.InnerException is SocketException socketException)
  201. {
  202. if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
  203. {
  204. throw new OperationCanceledException();
  205. }
  206. }
  207. throw new MqttCommunicationException(exception);
  208. }
  209. catch (Exception exception)
  210. {
  211. throw new MqttCommunicationException(exception);
  212. }
  213. }
  214. public void Dispose()
  215. {
  216. _isDisposed = true;
  217. _semaphore?.Dispose();
  218. _channel?.Dispose();
  219. }
  220. private void ThrowIfDisposed()
  221. {
  222. if (_isDisposed)
  223. {
  224. throw new ObjectDisposedException(nameof(MqttChannelAdapter));
  225. }
  226. }
  227. }
  228. }