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.
 
 
 
 

136 lines
5.0 KiB

  1. using System.Threading;
  2. using System.Threading.Tasks;
  3. using MQTTnet.Channel;
  4. using MQTTnet.Exceptions;
  5. using MQTTnet.Internal;
  6. namespace MQTTnet.Serializer
  7. {
  8. public static class MqttPacketReader
  9. {
  10. public static async Task<MqttFixedHeader> ReadFixedHeaderAsync(IMqttChannel channel, byte[] fixedHeaderBuffer, byte[] singleByteBuffer, CancellationToken cancellationToken)
  11. {
  12. // The MQTT fixed header contains 1 byte of flags and at least 1 byte for the remaining data length.
  13. // So in all cases at least 2 bytes must be read for a complete MQTT packet.
  14. // async/await is used here because the next packet is received in a couple of minutes so the performance
  15. // impact is acceptable according to a useless waiting thread.
  16. var buffer = fixedHeaderBuffer;
  17. var totalBytesRead = 0;
  18. while (totalBytesRead < buffer.Length)
  19. {
  20. var bytesRead = await channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
  21. cancellationToken.ThrowIfCancellationRequested();
  22. ExceptionHelper.ThrowIfGracefulSocketClose(bytesRead);
  23. totalBytesRead += bytesRead;
  24. }
  25. var hasRemainingLength = buffer[1] != 0;
  26. if (!hasRemainingLength)
  27. {
  28. return new MqttFixedHeader(buffer[0], 0);
  29. }
  30. #if WINDOWS_UWP
  31. // UWP will have a dead lock when calling this not async.
  32. var bodyLength = await ReadBodyLengthAsync(channel, buffer[1], singleByteBuffer, cancellationToken).ConfigureAwait(false);
  33. #else
  34. // Here the async/await pattern is not used becuase the overhead of context switches
  35. // is too big for reading 1 byte in a row. We expect that the remaining data was sent
  36. // directly after the initial bytes. If the client disconnects just in this moment we
  37. // will get an exception anyway.
  38. var bodyLength = ReadBodyLength(channel, buffer[1], singleByteBuffer, cancellationToken);
  39. #endif
  40. return new MqttFixedHeader(buffer[0], bodyLength);
  41. }
  42. #if !WINDOWS_UWP
  43. private static int ReadBodyLength(IMqttChannel channel, byte initialEncodedByte, byte[] singleByteBuffer, CancellationToken cancellationToken)
  44. {
  45. var offset = 0;
  46. var multiplier = 128;
  47. var value = initialEncodedByte & 127;
  48. int encodedByte = initialEncodedByte;
  49. while ((encodedByte & 128) != 0)
  50. {
  51. offset++;
  52. if (offset > 3)
  53. {
  54. throw new MqttProtocolViolationException("Remaining length is invalid.");
  55. }
  56. cancellationToken.ThrowIfCancellationRequested();
  57. encodedByte = ReadByte(channel, singleByteBuffer, cancellationToken);
  58. value += (byte)(encodedByte & 127) * multiplier;
  59. multiplier *= 128;
  60. }
  61. return value;
  62. }
  63. private static byte ReadByte(IMqttChannel channel, byte[] singleByteBuffer, CancellationToken cancellationToken)
  64. {
  65. var readCount = channel.ReadAsync(singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
  66. cancellationToken.ThrowIfCancellationRequested();
  67. if (readCount <= 0)
  68. {
  69. ExceptionHelper.ThrowGracefulSocketClose();
  70. }
  71. return singleByteBuffer[0];
  72. }
  73. #else
  74. private static async Task<int> ReadBodyLengthAsync(IMqttChannel channel, byte initialEncodedByte, byte[] singleByteBuffer, CancellationToken cancellationToken)
  75. {
  76. var offset = 0;
  77. var multiplier = 128;
  78. var value = initialEncodedByte & 127;
  79. int encodedByte = initialEncodedByte;
  80. while ((encodedByte & 128) != 0)
  81. {
  82. offset++;
  83. if (offset > 3)
  84. {
  85. throw new MqttProtocolViolationException("Remaining length is invalid.");
  86. }
  87. cancellationToken.ThrowIfCancellationRequested();
  88. encodedByte = await ReadByteAsync(channel, singleByteBuffer, cancellationToken).ConfigureAwait(false);
  89. value += (byte)(encodedByte & 127) * multiplier;
  90. multiplier *= 128;
  91. }
  92. return value;
  93. }
  94. private static async Task<byte> ReadByteAsync(IMqttChannel channel, byte[] singleByteBuffer, CancellationToken cancellationToken)
  95. {
  96. var readCount = await channel.ReadAsync(singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
  97. cancellationToken.ThrowIfCancellationRequested();
  98. if (readCount <= 0)
  99. {
  100. ExceptionHelper.ThrowGracefulSocketClose();
  101. }
  102. return singleByteBuffer[0];
  103. }
  104. #endif
  105. }
  106. }