Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user