using FastGithub.Configuration; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text.Json; using System.Threading; using System.Threading.Tasks; namespace FastGithub.DomainResolve { /// /// 域名的IP测速服务 /// sealed class DomainSpeedTestService { private const string DATA_FILE = "domains.json"; private readonly DnsClient dnsClient; private readonly object syncRoot = new(); private readonly Dictionary domainIPAddressHashSet = new(); /// /// 域名的IP测速服务 /// /// public DomainSpeedTestService(DnsClient dnsClient) { this.dnsClient = dnsClient; } /// /// 添加要测速的域名 /// /// /// public bool Add(string domain) { lock (this.syncRoot) { return this.domainIPAddressHashSet.TryAdd(domain, new IPAddressItemHashSet()); } } /// /// 获取测试后排序的IP /// /// /// public IPAddress[] GetIPAddresses(string domain) { lock (this.syncRoot) { if (this.domainIPAddressHashSet.TryGetValue(domain, out var hashSet) && hashSet.Count > 0) { return hashSet.ToArray().OrderBy(item => item.PingElapsed).Select(item => item.Address).ToArray(); } return Array.Empty(); } } /// /// 加载数据 /// /// /// public async Task LoadDataAsync(CancellationToken cancellationToken) { if (File.Exists(DATA_FILE) == false) { return; } using var fileStream = File.OpenRead(DATA_FILE); var domains = await JsonSerializer.DeserializeAsync(fileStream, cancellationToken: cancellationToken); if (domains == null) { return; } lock (this.syncRoot) { foreach (var domain in domains) { this.domainIPAddressHashSet.TryAdd(domain, new IPAddressItemHashSet()); } } } /// /// 保存数据 /// /// public async Task SaveDataAsync() { var domains = this.domainIPAddressHashSet.Keys .Select(item => new DomainPattern(item)) .OrderBy(item => item) .Select(item => item.ToString()) .ToArray(); try { using var fileStream = File.OpenWrite(DATA_FILE); await JsonSerializer.SerializeAsync(fileStream, domains, new JsonSerializerOptions { WriteIndented = true }); return true; } catch (Exception) { return false; } } /// /// 进行一轮IP测速 /// /// /// public async Task TestSpeedAsync(CancellationToken cancellationToken) { KeyValuePair[] keyValues; lock (this.syncRoot) { keyValues = this.domainIPAddressHashSet.ToArray(); } foreach (var keyValue in keyValues) { var domain = keyValue.Key; var hashSet = keyValue.Value; await foreach (var address in this.dnsClient.ResolveAsync(domain, cancellationToken)) { hashSet.Add(new IPAddressItem(address)); } await hashSet.PingAllAsync(); } } } }