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.

UnstableUdpClient.cs 4.4 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. using Discord.Net.Udp;
  2. using System;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. namespace Discord.Net.Providers.UnstableUdpSocket
  8. {
  9. internal class UnstableUdpSocket : IUdpSocket, IDisposable
  10. {
  11. private const double FailureRate = 0.10; //10%
  12. public event Func<byte[], int, int, Task> ReceivedDatagram;
  13. private readonly SemaphoreSlim _lock;
  14. private readonly Random _rand;
  15. private UdpClient _udp;
  16. private IPEndPoint _destination;
  17. private CancellationTokenSource _cancelTokenSource;
  18. private CancellationToken _cancelToken, _parentToken;
  19. private Task _task;
  20. private bool _isDisposed;
  21. public UnstableUdpSocket()
  22. {
  23. _lock = new SemaphoreSlim(1, 1);
  24. _rand = new Random();
  25. _cancelTokenSource = new CancellationTokenSource();
  26. }
  27. private void Dispose(bool disposing)
  28. {
  29. if (!_isDisposed)
  30. {
  31. if (disposing)
  32. StopInternalAsync(true).GetAwaiter().GetResult();
  33. _isDisposed = true;
  34. }
  35. }
  36. public void Dispose()
  37. {
  38. Dispose(true);
  39. }
  40. public async Task StartAsync()
  41. {
  42. await _lock.WaitAsync().ConfigureAwait(false);
  43. try
  44. {
  45. await StartInternalAsync(_cancelToken).ConfigureAwait(false);
  46. }
  47. finally
  48. {
  49. _lock.Release();
  50. }
  51. }
  52. public async Task StartInternalAsync(CancellationToken cancelToken)
  53. {
  54. await StopInternalAsync().ConfigureAwait(false);
  55. _cancelTokenSource = new CancellationTokenSource();
  56. _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token;
  57. _udp = new UdpClient(0);
  58. _task = RunAsync(_cancelToken);
  59. }
  60. public async Task StopAsync()
  61. {
  62. await _lock.WaitAsync().ConfigureAwait(false);
  63. try
  64. {
  65. await StopInternalAsync().ConfigureAwait(false);
  66. }
  67. finally
  68. {
  69. _lock.Release();
  70. }
  71. }
  72. public async Task StopInternalAsync(bool isDisposing = false)
  73. {
  74. try { _cancelTokenSource.Cancel(false); } catch { }
  75. if (!isDisposing)
  76. await (_task ?? Task.Delay(0)).ConfigureAwait(false);
  77. if (_udp != null)
  78. {
  79. try { _udp.Dispose(); }
  80. catch { }
  81. _udp = null;
  82. }
  83. }
  84. public void SetDestination(string host, int port)
  85. {
  86. var entry = Dns.GetHostEntryAsync(host).GetAwaiter().GetResult();
  87. _destination = new IPEndPoint(entry.AddressList[0], port);
  88. }
  89. public void SetCancelToken(CancellationToken cancelToken)
  90. {
  91. _parentToken = cancelToken;
  92. _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token;
  93. }
  94. public async Task SendAsync(byte[] data, int index, int count)
  95. {
  96. if (!UnstableCheck())
  97. return;
  98. if (index != 0) //Should never happen?
  99. {
  100. var newData = new byte[count];
  101. Buffer.BlockCopy(data, index, newData, 0, count);
  102. data = newData;
  103. }
  104. await _udp.SendAsync(data, count, _destination).ConfigureAwait(false);
  105. }
  106. private async Task RunAsync(CancellationToken cancelToken)
  107. {
  108. var closeTask = Task.Delay(-1, cancelToken);
  109. while (!cancelToken.IsCancellationRequested)
  110. {
  111. var receiveTask = _udp.ReceiveAsync();
  112. var task = await Task.WhenAny(closeTask, receiveTask).ConfigureAwait(false);
  113. if (task == closeTask)
  114. break;
  115. var result = receiveTask.Result;
  116. await ReceivedDatagram(result.Buffer, 0, result.Buffer.Length).ConfigureAwait(false);
  117. }
  118. }
  119. private bool UnstableCheck()
  120. {
  121. return _rand.NextDouble() > FailureRate;
  122. }
  123. }
  124. }