DomainSpeedTestService.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Text.Json;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. namespace FastGithub.DomainResolve
  10. {
  11. /// <summary>
  12. /// 域名的IP测速服务
  13. /// </summary>
  14. sealed class DomainSpeedTestService
  15. {
  16. private const string DATA_FILE = "domains.json";
  17. private readonly DnsClient dnsClient;
  18. private readonly object syncRoot = new();
  19. private readonly Dictionary<string, IPAddressItemHashSet> domainIPAddressHashSet = new();
  20. /// <summary>
  21. /// 域名的IP测速服务
  22. /// </summary>
  23. /// <param name="dnsClient"></param>
  24. public DomainSpeedTestService(DnsClient dnsClient)
  25. {
  26. this.dnsClient = dnsClient;
  27. }
  28. /// <summary>
  29. /// 添加要测速的域名
  30. /// </summary>
  31. /// <param name="domain"></param>
  32. /// <returns></returns>
  33. public bool Add(string domain)
  34. {
  35. lock (this.syncRoot)
  36. {
  37. return this.domainIPAddressHashSet.TryAdd(domain, new IPAddressItemHashSet());
  38. }
  39. }
  40. /// <summary>
  41. /// 获取测试后排序的IP
  42. /// </summary>
  43. /// <param name="domain"></param>
  44. /// <returns></returns>
  45. public IPAddress[] GetIPAddresses(string domain)
  46. {
  47. lock (this.syncRoot)
  48. {
  49. if (this.domainIPAddressHashSet.TryGetValue(domain, out var hashSet) && hashSet.Count > 0)
  50. {
  51. return hashSet.ToArray().OrderBy(item => item.PingElapsed).Select(item => item.Address).ToArray();
  52. }
  53. return Array.Empty<IPAddress>();
  54. }
  55. }
  56. /// <summary>
  57. /// 加载数据
  58. /// </summary>
  59. /// <param name="cancellationToken"></param>
  60. /// <returns></returns>
  61. public async Task LoadDataAsync(CancellationToken cancellationToken)
  62. {
  63. if (File.Exists(DATA_FILE) == false)
  64. {
  65. return;
  66. }
  67. var fileStream = File.OpenRead(DATA_FILE);
  68. var domains = await JsonSerializer.DeserializeAsync<string[]>(fileStream, cancellationToken: cancellationToken);
  69. if (domains == null)
  70. {
  71. return;
  72. }
  73. lock (this.syncRoot)
  74. {
  75. foreach (var domain in domains)
  76. {
  77. this.domainIPAddressHashSet.TryAdd(domain, new IPAddressItemHashSet());
  78. }
  79. }
  80. }
  81. /// <summary>
  82. /// 保存数据
  83. /// </summary>
  84. /// <returns></returns>
  85. public async Task SaveDataAsync()
  86. {
  87. var domains = this.domainIPAddressHashSet.Keys.ToArray();
  88. using var fileStream = File.OpenWrite(DATA_FILE);
  89. await JsonSerializer.SerializeAsync(fileStream, domains);
  90. }
  91. /// <summary>
  92. /// 进行一轮IP测速
  93. /// </summary>
  94. /// <param name="cancellationToken"></param>
  95. /// <returns></returns>
  96. public async Task TestSpeedAsync(CancellationToken cancellationToken)
  97. {
  98. KeyValuePair<string, IPAddressItemHashSet>[] keyValues;
  99. lock (this.syncRoot)
  100. {
  101. keyValues = this.domainIPAddressHashSet.ToArray();
  102. }
  103. foreach (var keyValue in keyValues)
  104. {
  105. var domain = keyValue.Key;
  106. var hashSet = keyValue.Value;
  107. await foreach (var address in this.dnsClient.ResolveAsync(domain, cancellationToken))
  108. {
  109. hashSet.Add(new IPAddressItem(address));
  110. }
  111. await hashSet.PingAllAsync();
  112. }
  113. }
  114. }
  115. }