Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

WebSocket4NetMqttChannel.cs 8.0 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Security.Authentication;
  7. using System.Security.Cryptography.X509Certificates;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. using MQTTnet.Channel;
  11. using MQTTnet.Client.Options;
  12. using MQTTnet.Exceptions;
  13. using MQTTnet.Internal;
  14. using SuperSocket.ClientEngine;
  15. using WebSocket4Net;
  16. namespace MQTTnet.Extensions.WebSocket4Net
  17. {
  18. public class WebSocket4NetMqttChannel : IMqttChannel
  19. {
  20. private readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
  21. private readonly IMqttClientOptions _clientOptions;
  22. private readonly MqttClientWebSocketOptions _webSocketOptions;
  23. private WebSocket _webSocket;
  24. public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions, MqttClientWebSocketOptions webSocketOptions)
  25. {
  26. _clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions));
  27. _webSocketOptions = webSocketOptions ?? throw new ArgumentNullException(nameof(webSocketOptions));
  28. }
  29. public string Endpoint => _webSocketOptions.Uri;
  30. public bool IsSecureConnection { get; private set; }
  31. public X509Certificate2 ClientCertificate { get; }
  32. public async Task ConnectAsync(CancellationToken cancellationToken)
  33. {
  34. var uri = _webSocketOptions.Uri;
  35. if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase) && !uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase))
  36. {
  37. if (_webSocketOptions.TlsOptions?.UseTls == false)
  38. {
  39. uri = "ws://" + uri;
  40. }
  41. else
  42. {
  43. uri = "wss://" + uri;
  44. }
  45. }
  46. var sslProtocols = _webSocketOptions?.TlsOptions?.SslProtocol ?? SslProtocols.None;
  47. var subProtocol = _webSocketOptions.SubProtocols.FirstOrDefault() ?? string.Empty;
  48. var cookies = new List<KeyValuePair<string, string>>();
  49. if (_webSocketOptions.CookieContainer != null)
  50. {
  51. throw new NotSupportedException("Cookies are not supported.");
  52. }
  53. List<KeyValuePair<string, string>> customHeaders = null;
  54. if (_webSocketOptions.RequestHeaders != null)
  55. {
  56. customHeaders = _webSocketOptions.RequestHeaders.Select(i => new KeyValuePair<string, string>(i.Key, i.Value)).ToList();
  57. }
  58. EndPoint proxy = null;
  59. if (_webSocketOptions.ProxyOptions != null)
  60. {
  61. throw new NotSupportedException("Proxies are not supported.");
  62. }
  63. // The user agent can be empty always because it is just added to the custom headers as "User-Agent".
  64. var userAgent = string.Empty;
  65. var origin = string.Empty;
  66. var webSocketVersion = WebSocketVersion.None;
  67. var receiveBufferSize = 0;
  68. var certificates = new X509CertificateCollection();
  69. if (_webSocketOptions?.TlsOptions?.Certificates != null)
  70. {
  71. foreach (var certificate in _webSocketOptions.TlsOptions.Certificates)
  72. {
  73. certificates.Add(new X509Certificate(certificate));
  74. }
  75. }
  76. _webSocket = new WebSocket(uri, subProtocol, cookies, customHeaders, userAgent, origin, webSocketVersion, proxy, sslProtocols, receiveBufferSize)
  77. {
  78. NoDelay = true,
  79. Security =
  80. {
  81. AllowUnstrustedCertificate = _webSocketOptions?.TlsOptions?.AllowUntrustedCertificates == true,
  82. AllowCertificateChainErrors = _webSocketOptions?.TlsOptions?.IgnoreCertificateChainErrors == true,
  83. Certificates = certificates
  84. }
  85. };
  86. await ConnectInternalAsync(cancellationToken).ConfigureAwait(false);
  87. IsSecureConnection = uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase);
  88. }
  89. public Task DisconnectAsync(CancellationToken cancellationToken)
  90. {
  91. if (_webSocket != null && _webSocket.State == WebSocketState.Open)
  92. {
  93. _webSocket.Close();
  94. }
  95. return Task.FromResult(0);
  96. }
  97. public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  98. {
  99. var readBytes = 0;
  100. while (count > 0 && !cancellationToken.IsCancellationRequested)
  101. {
  102. if (!_receiveBuffer.TryTake(out var @byte))
  103. {
  104. if (readBytes == 0)
  105. {
  106. // Block until at least one byte was received.
  107. @byte = _receiveBuffer.Take(cancellationToken);
  108. }
  109. else
  110. {
  111. return Task.FromResult(readBytes);
  112. }
  113. }
  114. buffer[offset] = @byte;
  115. offset++;
  116. count--;
  117. readBytes++;
  118. }
  119. return Task.FromResult(readBytes);
  120. }
  121. public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  122. {
  123. _webSocket.Send(buffer, offset, count);
  124. return Task.FromResult(0);
  125. }
  126. public void Dispose()
  127. {
  128. if (_webSocket == null)
  129. {
  130. return;
  131. }
  132. _webSocket.DataReceived -= OnDataReceived;
  133. _webSocket.Error -= OnError;
  134. _webSocket.Dispose();
  135. _webSocket = null;
  136. }
  137. private void OnError(object sender, ErrorEventArgs e)
  138. {
  139. System.Diagnostics.Debug.Write(e.Exception.ToString());
  140. }
  141. private void OnDataReceived(object sender, DataReceivedEventArgs e)
  142. {
  143. foreach (var @byte in e.Data)
  144. {
  145. _receiveBuffer.Add(@byte);
  146. }
  147. }
  148. private async Task ConnectInternalAsync(CancellationToken cancellationToken)
  149. {
  150. _webSocket.Error += OnError;
  151. _webSocket.DataReceived += OnDataReceived;
  152. var taskCompletionSource = new TaskCompletionSource<Exception>();
  153. void ErrorHandler(object sender, ErrorEventArgs e)
  154. {
  155. taskCompletionSource.TrySetResult(e.Exception);
  156. }
  157. void SuccessHandler(object sender, EventArgs e)
  158. {
  159. taskCompletionSource.TrySetResult(null);
  160. }
  161. try
  162. {
  163. _webSocket.Opened += SuccessHandler;
  164. _webSocket.Error += ErrorHandler;
  165. _webSocket.Open();
  166. var exception = await MqttTaskTimeout.WaitAsync(c =>
  167. {
  168. c.Register(() => taskCompletionSource.TrySetCanceled());
  169. return taskCompletionSource.Task;
  170. }, _clientOptions.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
  171. if (exception != null)
  172. {
  173. if (exception is AuthenticationException authenticationException)
  174. {
  175. throw new MqttCommunicationException(authenticationException.InnerException);
  176. }
  177. if (exception is OperationCanceledException)
  178. {
  179. throw new MqttCommunicationTimedOutException();
  180. }
  181. throw new MqttCommunicationException(exception);
  182. }
  183. }
  184. catch (OperationCanceledException)
  185. {
  186. throw new MqttCommunicationTimedOutException();
  187. }
  188. finally
  189. {
  190. _webSocket.Opened -= SuccessHandler;
  191. _webSocket.Error -= ErrorHandler;
  192. }
  193. }
  194. }
  195. }