Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 74 additions & 44 deletions DHCPServer/Library/DHCPServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class DHCPServer : IDHCPServer
{
private const int ClientInformationWriteRetries = 10;

private readonly SemaphoreSlim _taskSemaphoreSlim = new(1, 1);
private IPEndPoint _endPoint = new(IPAddress.Loopback, 67);
private IUDPSocket _socket = default!;
private IPAddress _subnetMask = IPAddress.Any;
Expand All @@ -34,7 +35,8 @@ public class DHCPServer : IDHCPServer
private readonly AutoPumpQueue<int> _updateClientInfoQueue;
private readonly Random _random = new();
private CancellationTokenSource _cancellationTokenSource = new();
private Task? _mainTask;
private Task? _receiveTask;
private Task? _timerTask;

#region IDHCPServer Members

Expand Down Expand Up @@ -235,7 +237,7 @@ public DHCPServer(ILogger logger, string? clientInfoPath, IUDPSocketFactory udpS
_logger = logger;
_clientInfoPath = clientInfoPath;
_udpSocketFactory = udpSocketFactory;
_hostName = System.Environment.MachineName;
_hostName = Environment.MachineName;
}

public void Start()
Expand All @@ -262,7 +264,8 @@ public void Start()
Trace($"Starting DHCP server '{_endPoint}'");
_active = true;
_socket = _udpSocketFactory.Create(_endPoint, 2048, true, 10);
_mainTask = Task.Run(async () => { await MainTask(_cancellationTokenSource.Token); });
_receiveTask = Task.Run(async () => await ReceiveTask(_cancellationTokenSource.Token));
_timerTask = Task.Run(async () => await TimerTask(_cancellationTokenSource.Token));
Trace("DHCP Server start succeeded");
}
catch(Exception e)
Expand Down Expand Up @@ -339,21 +342,29 @@ private void Stop(Exception? reason)
notify = true;
_cancellationTokenSource.Cancel();

if(_mainTask!= null)
AwaitTask(_receiveTask);
AwaitTask(_timerTask);

_receiveTask = null;
_timerTask = null;

_socket.Dispose();
Trace("Stopped");

void AwaitTask(Task task)
{
try
if(task != null)
{
_mainTask.GetAwaiter().GetResult();
}
catch(Exception ex)
{
_logger?.LogError(ex, $"Exception during {nameof(Stop)}");
try
{
task.GetAwaiter().GetResult();
}
catch(Exception ex)
{
_logger?.LogError(ex, $"Exception during {nameof(Stop)}");
}
}
_mainTask = null;
}

_socket.Dispose();
Trace("Stopped");
}

if(notify)
Expand Down Expand Up @@ -825,57 +836,76 @@ private async Task OfferClient(DHCPMessage dhcpMessage, DHCPClient client)
await SendOFFER(dhcpMessage, client.IPAddress, _leaseTime);
}

private async Task MainTask(CancellationToken cancellationToken)
private async Task ReceiveTask(CancellationToken cancellationToken)
{
var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
Task<(IPEndPoint,ReadOnlyMemory<byte>)>? receiveTask = default;
Task? timerTask = default;

while(!cancellationToken.IsCancellationRequested)
{
receiveTask ??= _socket.Receive(cancellationToken);
timerTask ??= timer.WaitForNextTickAsync(cancellationToken).AsTask();

var completedTask = await Task.WhenAny(receiveTask, timerTask);
bool enteredSemaphore = false;

if(completedTask == receiveTask)
try
{
try
{
(var ipEndPoint, var data) = await receiveTask;
await OnReceive(ipEndPoint, data);
}
catch(UDPSocketException ex)
{
if(ex.IsFatal)
{
_logger?.LogError(ex, $"fatal exception in {nameof(MainTask)}");
throw;
}
}
finally
(IPEndPoint ipEndPoint, ReadOnlyMemory<byte> data) = await _socket.Receive(cancellationToken);
await _taskSemaphoreSlim.WaitAsync(cancellationToken);
enteredSemaphore = true;
await OnReceive(ipEndPoint, data);
}
catch(UDPSocketException ex)
{
if(ex.IsFatal)
{
receiveTask = null;
_logger?.LogError(ex, $"fatal exception in {nameof(ReceiveTask)}");
return;
}
}
else if(completedTask == timerTask)
catch
{
// Ignored.
}
finally
{
if(enteredSemaphore)
_taskSemaphoreSlim.Release();
}
}
}

private async Task TimerTask(CancellationToken cancellationToken)
{
PeriodicTimer timer = new(TimeSpan.FromSeconds(1));

try
{
while(!cancellationToken.IsCancellationRequested)
{
bool enteredSemaphore = false;

try
{
await timerTask;
await timer.WaitForNextTickAsync(cancellationToken);
await _taskSemaphoreSlim.WaitAsync(cancellationToken);
enteredSemaphore = true;
CheckLeaseExpiration();
}
finally
{
timerTask = null;
if(enteredSemaphore)
_taskSemaphoreSlim.Release();
}
}
}
catch
{
// Ignored.
}
finally
{
timer.Dispose();
}
}

private DHCPClient? GetKnownClient(DHCPClient client)
{
return _clients.TryGetValue(client, out var value) ? value : null;
return _clients.GetValueOrDefault(client);
}

private async Task OnReceive(IPEndPoint endPoint, ReadOnlyMemory<byte> data)
Expand Down Expand Up @@ -1050,7 +1080,7 @@ private async Task OnReceive(IPEndPoint endPoint, ReadOnlyMemory<byte> data)
client.State = DHCPClient.TState.Assigned;
client.LeaseStartTime = DateTime.Now;
client.LeaseDuration = _leaseTime;
_=_clients.TryAdd(client, client);
_ = _clients.TryAdd(client, client);
await SendACK(dhcpMessage, dhcpMessage.ClientIPAddress, client.LeaseDuration);
}
else
Expand Down