From ca6eb6aff40d22b15b80ad53b54d25e8b955fc05 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 23 Dec 2016 15:07:59 -0400 Subject: [PATCH] Added UDPClient Provider --- .../Discord.Net.Providers.UdpClient.csproj | 27 ++++ .../UDPClient.cs | 128 ++++++++++++++++++ .../UDPClientProvider.cs | 9 ++ .../project.json | 37 +++++ 4 files changed, 201 insertions(+) create mode 100644 src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj create mode 100644 src/Discord.Net.Providers.UDPClient/UDPClient.cs create mode 100644 src/Discord.Net.Providers.UDPClient/UDPClientProvider.cs create mode 100644 src/Discord.Net.Providers.UDPClient/project.json diff --git a/src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj b/src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj new file mode 100644 index 000000000..64225a50d --- /dev/null +++ b/src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj @@ -0,0 +1,27 @@ + + + An optional UDP client provider for Discord.Net using System.Net.UdpClient + 1.0.0-beta2 + net45 + true + Discord.Net.Providers.UDPClient + discord;discordapp + https://github.com/RogueException/Discord.Net + http://opensource.org/licenses/MIT + git + git://github.com/RogueException/Discord.Net + Discord.Providers.UDPClient + + + + + + + + + + $(NoWarn);CS1573;CS1591 + true + true + + \ No newline at end of file diff --git a/src/Discord.Net.Providers.UDPClient/UDPClient.cs b/src/Discord.Net.Providers.UDPClient/UDPClient.cs new file mode 100644 index 000000000..459feb335 --- /dev/null +++ b/src/Discord.Net.Providers.UDPClient/UDPClient.cs @@ -0,0 +1,128 @@ +using Discord.Net.Udp; +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using UdpSocket = System.Net.Sockets.UdpClient; + +namespace Discord.Net.Providers.UDPClient +{ + internal class UDPClient : IUdpSocket, IDisposable + { + public event Func ReceivedDatagram; + + private readonly SemaphoreSlim _lock; + private UdpSocket _udp; + private IPEndPoint _destination; + private CancellationTokenSource _cancelTokenSource; + private CancellationToken _cancelToken, _parentToken; + private Task _task; + private bool _isDisposed; + + public UDPClient() + { + _lock = new SemaphoreSlim(1, 1); + } + private void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + StopInternalAsync(true).GetAwaiter().GetResult(); + _isDisposed = true; + } + } + public void Dispose() + { + Dispose(true); + } + + + public async Task StartAsync() + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await StartInternalAsync(_cancelToken).ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + public async Task StartInternalAsync(CancellationToken cancelToken) + { + await StopInternalAsync().ConfigureAwait(false); + + _cancelTokenSource = new CancellationTokenSource(); + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + + _udp = new UdpSocket(); + + _task = RunAsync(_cancelToken); + } + public async Task StopAsync() + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await StopInternalAsync().ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + public async Task StopInternalAsync(bool isDisposing = false) + { + try { _cancelTokenSource.Cancel(false); } catch { } + + if (!isDisposing) + await (_task ?? Task.Delay(0)).ConfigureAwait(false); + + if (_udp != null) + { + try { _udp.Close(); } + catch { } + _udp = null; + } + } + + public void SetDestination(string host, int port) + { + var entry = Dns.GetHostEntryAsync(host).GetAwaiter().GetResult(); + _destination = new IPEndPoint(entry.AddressList[0], port); + } + public void SetCancelToken(CancellationToken cancelToken) + { + _parentToken = cancelToken; + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + } + + public async Task SendAsync(byte[] data, int index, int count) + { + if (index != 0) //Should never happen? + { + var newData = new byte[count]; + Buffer.BlockCopy(data, index, newData, 0, count); + data = newData; + } + await _udp.SendAsync(data, count, _destination).ConfigureAwait(false); + } + + private async Task RunAsync(CancellationToken cancelToken) + { + var closeTask = Task.Delay(-1, cancelToken); + while (!cancelToken.IsCancellationRequested) + { + var receiveTask = _udp.ReceiveAsync(); + var task = await Task.WhenAny(closeTask, receiveTask).ConfigureAwait(false); + if (task == closeTask) + break; + + var result = receiveTask.Result; + await ReceivedDatagram(result.Buffer, 0, result.Buffer.Length).ConfigureAwait(false); + } + } + } +} \ No newline at end of file diff --git a/src/Discord.Net.Providers.UDPClient/UDPClientProvider.cs b/src/Discord.Net.Providers.UDPClient/UDPClientProvider.cs new file mode 100644 index 000000000..6bdf9eb63 --- /dev/null +++ b/src/Discord.Net.Providers.UDPClient/UDPClientProvider.cs @@ -0,0 +1,9 @@ +using Discord.Net.Udp; + +namespace Discord.Net.Providers.UDPClient +{ + public static class UDPClientProvider + { + public static readonly UdpSocketProvider Instance = () => new UDPClient(); + } +} diff --git a/src/Discord.Net.Providers.UDPClient/project.json b/src/Discord.Net.Providers.UDPClient/project.json new file mode 100644 index 000000000..59d8509ad --- /dev/null +++ b/src/Discord.Net.Providers.UDPClient/project.json @@ -0,0 +1,37 @@ +{ + "version": "1.0.0-*", + "description": "An optional UDP client provider for Discord.Net using System.Net.UdpClient.", + "authors": [ "RogueException" ], + + "packOptions": { + "tags": [ "discord", "discordapp" ], + "licenseUrl": "http://opensource.org/licenses/MIT", + "projectUrl": "https://github.com/RogueException/Discord.Net", + "repository": { + "type": "git", + "url": "git://github.com/RogueException/Discord.Net" + } + }, + + "configurations": { + "Release": { + "buildOptions": { + "define": [ "RELEASE" ], + "nowarn": [ "CS1573", "CS1591" ], + "optimize": true, + "warningsAsErrors": true, + "xmlDoc": true + } + } + }, + + "dependencies": { + "Discord.Net.Core": { + "target": "project" + } + }, + + "frameworks": { + "net45": {} + } +} \ No newline at end of file