You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

WS4NetClient.cs 5.4 kB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Discord.Net.WebSockets;
  8. using SuperSocket.ClientEngine;
  9. using WebSocket4Net;
  10. using WS4NetSocket = WebSocket4Net.WebSocket;
  11. namespace Discord.Net.Providers.WS4Net
  12. {
  13. internal class WS4NetClient : IWebSocketClient, IDisposable
  14. {
  15. private readonly Dictionary<string, string> _headers;
  16. private readonly SemaphoreSlim _lock;
  17. private readonly ManualResetEventSlim _waitUntilConnect;
  18. private CancellationToken _cancelToken, _parentToken;
  19. private CancellationTokenSource _cancelTokenSource;
  20. private WS4NetSocket _client;
  21. private bool _isDisposed;
  22. public WS4NetClient()
  23. {
  24. _headers = new Dictionary<string, string>();
  25. _lock = new SemaphoreSlim(1, 1);
  26. _cancelTokenSource = new CancellationTokenSource();
  27. _cancelToken = CancellationToken.None;
  28. _parentToken = CancellationToken.None;
  29. _waitUntilConnect = new ManualResetEventSlim();
  30. }
  31. public void Dispose() => Dispose(true);
  32. public event Func<byte[], int, int, Task> BinaryMessage;
  33. public event Func<string, Task> TextMessage;
  34. public event Func<Exception, Task> Closed;
  35. public async Task ConnectAsync(string host)
  36. {
  37. await _lock.WaitAsync().ConfigureAwait(false);
  38. try
  39. {
  40. await ConnectInternalAsync(host).ConfigureAwait(false);
  41. }
  42. finally
  43. {
  44. _lock.Release();
  45. }
  46. }
  47. public async Task DisconnectAsync()
  48. {
  49. await _lock.WaitAsync().ConfigureAwait(false);
  50. try
  51. {
  52. await DisconnectInternalAsync().ConfigureAwait(false);
  53. }
  54. finally
  55. {
  56. _lock.Release();
  57. }
  58. }
  59. public void SetHeader(string key, string value) => _headers[key] = value;
  60. public void SetCancelToken(CancellationToken cancelToken)
  61. {
  62. _parentToken = cancelToken;
  63. _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token)
  64. .Token;
  65. }
  66. public async Task SendAsync(byte[] data, int index, int count, bool isText)
  67. {
  68. await _lock.WaitAsync(_cancelToken).ConfigureAwait(false);
  69. try
  70. {
  71. if (isText)
  72. _client.Send(Encoding.UTF8.GetString(data, index, count));
  73. else
  74. _client.Send(data, index, count);
  75. }
  76. finally
  77. {
  78. _lock.Release();
  79. }
  80. }
  81. private void Dispose(bool disposing)
  82. {
  83. if (_isDisposed) return;
  84. if (disposing)
  85. DisconnectInternalAsync(true).GetAwaiter().GetResult();
  86. _isDisposed = true;
  87. }
  88. private async Task ConnectInternalAsync(string host)
  89. {
  90. await DisconnectInternalAsync().ConfigureAwait(false);
  91. _cancelTokenSource = new CancellationTokenSource();
  92. _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token)
  93. .Token;
  94. _client = new WS4NetSocket(host, "", customHeaderItems: _headers.ToList())
  95. {
  96. EnableAutoSendPing = false,
  97. NoDelay = true,
  98. Proxy = null
  99. };
  100. _client.MessageReceived += OnTextMessage;
  101. _client.DataReceived += OnBinaryMessage;
  102. _client.Opened += OnConnected;
  103. _client.Closed += OnClosed;
  104. _client.Open();
  105. _waitUntilConnect.Wait(_cancelToken);
  106. }
  107. private Task DisconnectInternalAsync(bool isDisposing = false)
  108. {
  109. _cancelTokenSource.Cancel();
  110. if (_client == null)
  111. return Task.Delay(0);
  112. if (_client.State == WebSocketState.Open)
  113. try
  114. {
  115. _client.Close(1000, "");
  116. }
  117. catch
  118. {
  119. }
  120. _client.MessageReceived -= OnTextMessage;
  121. _client.DataReceived -= OnBinaryMessage;
  122. _client.Opened -= OnConnected;
  123. _client.Closed -= OnClosed;
  124. try
  125. {
  126. _client.Dispose();
  127. }
  128. catch
  129. {
  130. }
  131. _client = null;
  132. _waitUntilConnect.Reset();
  133. return Task.Delay(0);
  134. }
  135. private void OnTextMessage(object sender, MessageReceivedEventArgs e) =>
  136. TextMessage(e.Message).GetAwaiter().GetResult();
  137. private void OnBinaryMessage(object sender, DataReceivedEventArgs e) =>
  138. BinaryMessage(e.Data, 0, e.Data.Length).GetAwaiter().GetResult();
  139. private void OnConnected(object sender, object e) => _waitUntilConnect.Set();
  140. private void OnClosed(object sender, object e)
  141. {
  142. var ex = (e as ErrorEventArgs)?.Exception ?? new Exception("Unexpected close");
  143. Closed(ex).GetAwaiter().GetResult();
  144. }
  145. }
  146. }