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.3 kB

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