Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 

232 рядки
8.0 KiB

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