您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

133 行
4.4 KiB

  1. using System;
  2. using System.Net.WebSockets;
  3. using System.Security.Cryptography.X509Certificates;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using MQTTnet.Channel;
  7. using MQTTnet.Client;
  8. namespace MQTTnet.Implementations
  9. {
  10. public sealed class MqttWebSocketChannel : IMqttChannel
  11. {
  12. private readonly SemaphoreSlim _sendLock = new SemaphoreSlim(1, 1);
  13. private readonly MqttClientWebSocketOptions _options;
  14. private WebSocket _webSocket;
  15. public MqttWebSocketChannel(MqttClientWebSocketOptions options)
  16. {
  17. _options = options ?? throw new ArgumentNullException(nameof(options));
  18. }
  19. public MqttWebSocketChannel(WebSocket webSocket)
  20. {
  21. _webSocket = webSocket ?? throw new ArgumentNullException(nameof(webSocket));
  22. }
  23. public async Task ConnectAsync(CancellationToken cancellationToken)
  24. {
  25. var uri = _options.Uri;
  26. if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase) && !uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase))
  27. {
  28. if (_options.TlsOptions?.UseTls == false)
  29. {
  30. uri = "ws://" + uri;
  31. }
  32. else
  33. {
  34. uri = "wss://" + uri;
  35. }
  36. }
  37. var clientWebSocket = new ClientWebSocket();
  38. if (_options.RequestHeaders != null)
  39. {
  40. foreach (var requestHeader in _options.RequestHeaders)
  41. {
  42. clientWebSocket.Options.SetRequestHeader(requestHeader.Key, requestHeader.Value);
  43. }
  44. }
  45. if (_options.SubProtocols != null)
  46. {
  47. foreach (var subProtocol in _options.SubProtocols)
  48. {
  49. clientWebSocket.Options.AddSubProtocol(subProtocol);
  50. }
  51. }
  52. if (_options.CookieContainer != null)
  53. {
  54. clientWebSocket.Options.Cookies = _options.CookieContainer;
  55. }
  56. if (_options.TlsOptions?.UseTls == true && _options.TlsOptions?.Certificates != null)
  57. {
  58. clientWebSocket.Options.ClientCertificates = new X509CertificateCollection();
  59. foreach (var certificate in _options.TlsOptions.Certificates)
  60. {
  61. clientWebSocket.Options.ClientCertificates.Add(new X509Certificate(certificate));
  62. }
  63. }
  64. await clientWebSocket.ConnectAsync(new Uri(uri), cancellationToken).ConfigureAwait(false);
  65. _webSocket = clientWebSocket;
  66. }
  67. public async Task DisconnectAsync()
  68. {
  69. if (_webSocket == null)
  70. {
  71. return;
  72. }
  73. if (_webSocket.State == WebSocketState.Open || _webSocket.State == WebSocketState.Connecting)
  74. {
  75. await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
  76. }
  77. Dispose();
  78. }
  79. public async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  80. {
  81. var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, offset, count), cancellationToken).ConfigureAwait(false);
  82. return response.Count;
  83. }
  84. public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  85. {
  86. // This lock is required because the client will throw an exception if _SendAsync_ is
  87. // called from multiple threads at the same time. But this issue only happens with several
  88. // framework versions.
  89. await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false);
  90. try
  91. {
  92. await _webSocket.SendAsync(new ArraySegment<byte>(buffer, offset, count), WebSocketMessageType.Binary, true, cancellationToken).ConfigureAwait(false);
  93. }
  94. finally
  95. {
  96. _sendLock.Release();
  97. }
  98. }
  99. public void Dispose()
  100. {
  101. _sendLock?.Dispose();
  102. try
  103. {
  104. _webSocket?.Dispose();
  105. }
  106. catch (ObjectDisposedException)
  107. {
  108. }
  109. finally
  110. {
  111. _webSocket = null;
  112. }
  113. }
  114. }
  115. }