Skip to content

Commit

Permalink
Rely on standard async socket operations
Browse files Browse the repository at this point in the history
Don't use a while loop and Task.Delay to receive from multiple
sockets concurrently. With a small restructure we can use standard
async operations.
  • Loading branch information
alanmcgovern committed Mar 31, 2022
1 parent 1674ed2 commit 4f1a615
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Mono.Nat/Mono.Nat.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

<Target Name="SetAssemblyVersion" BeforeTargets="GetAssemblyVersion" Condition="'$(RestoreSuccess)' == 'true' And '$(Configuration)' == 'Release' " DependsOnTargets="GitVersion">
<PropertyGroup>
<MonoNatFileVersion>3.0.2</MonoNatFileVersion>
<MonoNatFileVersion>3.0.3</MonoNatFileVersion>
<MonoNatInformationalVersion>$(MonoNatFileVersion)-$(GitBranch)+$(GitCommit)</MonoNatInformationalVersion>

<AssemblyVersion Condition="'$(AssemblyVersion)' == ''">$(MonoNatABIVersion)</AssemblyVersion>
Expand Down
31 changes: 25 additions & 6 deletions Mono.Nat/Searcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ abstract class Searcher : ISearcher
protected SocketGroup Clients { get; }

protected CancellationTokenSource Cancellation { get; }
SemaphoreSlim Locker = new SemaphoreSlim (1, 1);

protected Searcher (SocketGroup clients)
{
Clients = clients;
Expand All @@ -75,18 +77,35 @@ public void Dispose ()
}

async void ListenAsync (CancellationToken token)
{
try {
var listens = new List<Task> ();
foreach (var udpClient in Clients.Clients)
listens.Add (ListenOneAsync (udpClient, token));
await Task.WhenAll (listens);
token.ThrowIfCancellationRequested ();
} catch (OperationCanceledException) {
Listening = false;
return;
} catch(Exception ex) {
Log.ExceptionFormated (ex, "Unhandled exception listening for clients in {0}", GetType().Name);
}
}

async Task ListenOneAsync (System.Net.Sockets.UdpClient udpClient, CancellationToken token)
{
while (!token.IsCancellationRequested) {
try {
(var localAddress, var data) = await Clients.ReceiveAsync (token).ConfigureAwait (false);

var data = await udpClient.ReceiveAsync ();
var localEndPoint = (IPEndPoint) udpClient.Client.LocalEndPoint;
token.ThrowIfCancellationRequested ();
await HandleMessageReceived (localAddress, data.Buffer, data.RemoteEndPoint, false, token).ConfigureAwait (false);

using (await Locker.EnterAsync (token))
await HandleMessageReceived (localEndPoint.Address, data.Buffer, data.RemoteEndPoint, false, token).ConfigureAwait (false);
} catch (OperationCanceledException) {
Listening = false;
return;
} catch(Exception ex) {
Log.ExceptionFormated (ex, "Unhandled exception listening for clients in {0}", GetType().Name);
} catch (Exception) {
// Ignore any errors
}
}
}
Expand Down
25 changes: 2 additions & 23 deletions Mono.Nat/SocketGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace Mono.Nat
{
class SocketGroup : IDisposable
{
public ICollection<UdpClient> Clients => Sockets.Keys;

Dictionary<UdpClient, List<IPAddress>> Sockets { get; }
SemaphoreSlim SocketSendLocker { get; }

Expand All @@ -27,29 +29,6 @@ public void Dispose ()
s.Key.Dispose ();
}

public async Task<(IPAddress, UdpReceiveResult)> ReceiveAsync (CancellationToken token)
{
while (true) {
foreach (var keypair in Sockets) {
token.ThrowIfCancellationRequested ();

try {
if (keypair.Key.Available > 0) {
var localAddress = ((IPEndPoint) keypair.Key.Client.LocalEndPoint).Address;
var data = await keypair.Key.ReceiveAsync ();
return (localAddress, data);
}
} catch (OperationCanceledException) {
throw;
} catch (Exception) {
// Ignore any errors
}
}

await Task.Delay (10, token);
}
}

public async Task SendAsync (byte[] buffer, IPAddress gatewayAddress, CancellationToken token)
{
using (await SocketSendLocker.EnterAsync (token)) {
Expand Down

0 comments on commit 4f1a615

Please sign in to comment.