83 lines
3.0 KiB
C#
83 lines
3.0 KiB
C#
using System;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using XWorld.Framework.Protocol;
|
|
|
|
namespace XWorld.Server.Gateway
|
|
{
|
|
public sealed class DevDiscoveryResponder
|
|
{
|
|
private readonly DevDiscoveryReply _template;
|
|
private readonly string _token;
|
|
private readonly UdpClient _udp;
|
|
private CancellationTokenSource _cts;
|
|
private Task _loop;
|
|
|
|
public int Port { get; }
|
|
|
|
public DevDiscoveryResponder(string serverName, string gatewayUrl, string resourceBaseUrl,
|
|
string token, int listenPort = DevDiscovery.Port)
|
|
{
|
|
if (string.IsNullOrEmpty(token)) throw new ArgumentException("token cannot be empty", nameof(token));
|
|
_token = token;
|
|
_template = new DevDiscoveryReply
|
|
{
|
|
ProtoVersion = DevDiscovery.ProtoVersion,
|
|
ServerName = serverName ?? "dev-server",
|
|
GatewayUrl = gatewayUrl ?? "",
|
|
ResourceBaseUrl = resourceBaseUrl ?? "",
|
|
Token = token,
|
|
};
|
|
_udp = new UdpClient(AddressFamily.InterNetwork);
|
|
_udp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
|
_udp.Client.Bind(new IPEndPoint(IPAddress.Any, listenPort));
|
|
Port = ((IPEndPoint)_udp.Client.LocalEndPoint).Port;
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
if (_loop != null) return;
|
|
_cts = new CancellationTokenSource();
|
|
_loop = ReceiveLoopAsync(_cts.Token);
|
|
}
|
|
|
|
private async Task ReceiveLoopAsync(CancellationToken ct)
|
|
{
|
|
using (ct.Register(() => { try { _udp.Close(); } catch { } }))
|
|
{
|
|
while (!ct.IsCancellationRequested)
|
|
{
|
|
UdpReceiveResult res;
|
|
try { res = await _udp.ReceiveAsync().ConfigureAwait(false); }
|
|
catch (ObjectDisposedException) { break; }
|
|
catch (SocketException) { break; }
|
|
|
|
if (!DevDiscoveryCodec.TryDecodeQuery(res.Buffer, out var q)) continue;
|
|
if (q.ProtoVersion != DevDiscovery.ProtoVersion) continue;
|
|
if (!string.Equals(q.Token, _token, StringComparison.Ordinal)) continue;
|
|
|
|
DevDiscoveryReply reply = _template;
|
|
reply.Nonce = q.Nonce;
|
|
byte[] bytes = DevDiscoveryCodec.EncodeReply(reply);
|
|
try { await _udp.SendAsync(bytes, bytes.Length, res.RemoteEndPoint).ConfigureAwait(false); }
|
|
catch (ObjectDisposedException) { break; }
|
|
catch (SocketException) { }
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task StopAsync()
|
|
{
|
|
if (_cts == null) return;
|
|
_cts.Cancel();
|
|
try { await _loop.ConfigureAwait(false); }
|
|
catch { }
|
|
_cts.Dispose();
|
|
_cts = null;
|
|
_loop = null;
|
|
}
|
|
}
|
|
}
|