using System.Net;
using System.Net.Sockets;
using Microsoft.Extensions.Options;
namespace FinalFactory.Rendezvous.Server.Transport;
///
/// Owns the cancellable UDP socket used by the future NAT mediator.
///
public sealed partial class UdpMediatorService : BackgroundService
{
private readonly ILogger _logger;
private readonly UdpMediatorOptions _options;
private UdpClient? _udpClient;
///
/// Initializes a new UDP mediator service.
///
public UdpMediatorService(
IOptions options,
ILogger logger)
{
_options = options.Value;
_logger = logger;
}
///
/// Gets the bound endpoint after startup completes.
///
public IPEndPoint? LocalEndpoint { get; private set; }
///
public override Task StartAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (_udpClient is not null)
{
throw new InvalidOperationException("The UDP mediator is already running.");
}
IPAddress listenAddress = IPAddress.Parse(_options.ListenAddress);
UdpClient udpClient = new(new IPEndPoint(listenAddress, _options.Port));
_udpClient = udpClient;
IPEndPoint localEndpoint =
(IPEndPoint?)udpClient.Client.LocalEndPoint
?? throw new InvalidOperationException("The UDP socket did not expose its bound endpoint.");
LocalEndpoint = localEndpoint;
LogMediatorListening(_logger, localEndpoint.Address, localEndpoint.Port);
return base.StartAsync(cancellationToken);
}
///
public override async Task StopAsync(CancellationToken cancellationToken)
{
await base.StopAsync(cancellationToken).ConfigureAwait(false);
_udpClient?.Dispose();
_udpClient = null;
LocalEndpoint = null;
LogMediatorStopped(_logger);
}
///
public override void Dispose()
{
_udpClient?.Dispose();
_udpClient = null;
LocalEndpoint = null;
base.Dispose();
}
///
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
UdpClient udpClient = _udpClient
?? throw new InvalidOperationException("The UDP mediator socket was not initialized.");
try
{
while (!stoppingToken.IsCancellationRequested)
{
_ = await udpClient.ReceiveAsync(stoppingToken).ConfigureAwait(false);
// Bootstrap deliberately emits no UDP response. Protocol handling lands in #11.
}
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
// Expected during normal shutdown.
}
catch (ObjectDisposedException) when (stoppingToken.IsCancellationRequested)
{
// Disposing the socket is the fallback that releases a blocked receive.
}
finally
{
LocalEndpoint = null;
}
}
[LoggerMessage(
EventId = 1,
Level = LogLevel.Information,
Message = "UDP mediator listening on {ListenAddress}:{ListenPort}")]
private static partial void LogMediatorListening(
ILogger logger,
IPAddress listenAddress,
int listenPort);
[LoggerMessage(
EventId = 2,
Level = LogLevel.Information,
Message = "UDP mediator stopped")]
private static partial void LogMediatorStopped(ILogger logger);
}