DiscoveryService.cs 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. using System.Net;
  2. using System.Net.Sockets;
  3. using System.Text;
  4. using System.Text.Json;
  5. using QuickIP.Client.Models;
  6. namespace QuickIP.Client.Services;
  7. public sealed class DiscoveryService
  8. {
  9. private const int DiscoveryPort = 50000;
  10. public async Task<DiscoveredDevice?> DiscoverAsync(string localIPv4, CancellationToken cancellationToken = default)
  11. {
  12. if (string.IsNullOrWhiteSpace(localIPv4))
  13. {
  14. return null;
  15. }
  16. using var client = new UdpClient(AddressFamily.InterNetwork)
  17. {
  18. EnableBroadcast = true,
  19. };
  20. client.Client.Bind(new IPEndPoint(IPAddress.Parse(localIPv4), 0));
  21. var request = new
  22. {
  23. protocol_version = 1,
  24. message_type = "discover",
  25. request_id = Guid.NewGuid().ToString(),
  26. client_name = Environment.MachineName,
  27. };
  28. var payload = JsonSerializer.SerializeToUtf8Bytes(request);
  29. await client.SendAsync(payload, payload.Length, new IPEndPoint(IPAddress.Broadcast, DiscoveryPort));
  30. using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
  31. timeoutCts.CancelAfter(TimeSpan.FromSeconds(3));
  32. try
  33. {
  34. var result = await client.ReceiveAsync(timeoutCts.Token);
  35. var response = JsonSerializer.Deserialize<DiscoveryResponse>(result.Buffer);
  36. if (response is null || response.MessageType != "discover_response")
  37. {
  38. return null;
  39. }
  40. return new DiscoveredDevice
  41. {
  42. DeviceId = response.DeviceId ?? string.Empty,
  43. Hostname = response.Hostname ?? string.Empty,
  44. AgentVersion = response.AgentVersion ?? string.Empty,
  45. Lan2Ip = response.Lan2Ip ?? string.Empty,
  46. AuthRequired = response.AuthRequired,
  47. };
  48. }
  49. catch (OperationCanceledException)
  50. {
  51. return null;
  52. }
  53. }
  54. private sealed class DiscoveryResponse
  55. {
  56. public string? MessageType { get; set; }
  57. public string? DeviceId { get; set; }
  58. public string? Hostname { get; set; }
  59. public string? AgentVersion { get; set; }
  60. public string? Lan2Ip { get; set; }
  61. public bool AuthRequired { get; set; }
  62. }
  63. }