diff --git a/FastGithub.DomainResolve/DomainResolver.cs b/FastGithub.DomainResolve/DomainResolver.cs index b376939..931c44b 100644 --- a/FastGithub.DomainResolve/DomainResolver.cs +++ b/FastGithub.DomainResolve/DomainResolver.cs @@ -2,10 +2,8 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Net; -using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -18,7 +16,7 @@ namespace FastGithub.DomainResolve sealed class DomainResolver : IDomainResolver { private readonly DnsClient dnsClient; - private readonly ConcurrentDictionary dnsEndPointAddressTestResult = new(); + private readonly ConcurrentDictionary dnsEndPointAddressElapseds = new(); /// /// 域名解析器 @@ -35,72 +33,9 @@ namespace FastGithub.DomainResolve /// 域名 public void Prefetch(string domain) { - var endPoint = new DnsEndPoint(domain, 443); - this.dnsEndPointAddressTestResult.TryAdd(endPoint, IPAddressTestResult.Empty); - } - - /// - /// 对所有节点进行测速 - /// - /// - /// - public async Task TestAllEndPointsAsync(CancellationToken cancellationToken) - { - foreach (var keyValue in this.dnsEndPointAddressTestResult) - { - if (keyValue.Value.IsEmpty || keyValue.Value.IsExpired) - { - var dnsEndPoint = keyValue.Key; - var addresses = new List(); - await foreach (var adddress in this.dnsClient.ResolveAsync(dnsEndPoint.Host, cancellationToken)) - { - addresses.Add(adddress); - } - - var addressTestResult = IPAddressTestResult.Empty; - if (addresses.Count == 1) - { - var addressElapseds = new[] { new IPAddressElapsed(addresses[0], TimeSpan.Zero) }; - addressTestResult = new IPAddressTestResult(addressElapseds); - } - else if (addresses.Count > 1) - { - var tasks = addresses.Select(item => GetIPAddressElapsedAsync(item, dnsEndPoint.Port, cancellationToken)); - var addressElapseds = await Task.WhenAll(tasks); - addressTestResult = new IPAddressTestResult(addressElapseds); - } - this.dnsEndPointAddressTestResult[dnsEndPoint] = addressTestResult; - } - } - } - - /// - /// 获取连接耗时 - /// - /// - /// - /// - /// - private static async Task GetIPAddressElapsedAsync(IPAddress address, int port, CancellationToken cancellationToken) - { - var stopWatch = Stopwatch.StartNew(); - try - { - using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10d)); - using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token); - using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); - await socket.ConnectAsync(address, port, linkedTokenSource.Token); - return new IPAddressElapsed(address, stopWatch.Elapsed); - } - catch (Exception) - { - cancellationToken.ThrowIfCancellationRequested(); - return new IPAddressElapsed(address, TimeSpan.MaxValue); - } - finally - { - stopWatch.Stop(); - } + const int HTTPS_PORT = 443; + var dnsEndPoint = new DnsEndPoint(domain, HTTPS_PORT); + this.dnsEndPointAddressElapseds.TryAdd(dnsEndPoint, IPAddressElapsedCollection.Empty); } /// @@ -126,21 +61,55 @@ namespace FastGithub.DomainResolve /// public async IAsyncEnumerable ResolveAllAsync(DnsEndPoint endPoint, [EnumeratorCancellation] CancellationToken cancellationToken) { - if (this.dnsEndPointAddressTestResult.TryGetValue(endPoint, out var speedTestResult) && speedTestResult.IsEmpty == false) + if (this.dnsEndPointAddressElapseds.TryGetValue(endPoint, out var addressElapseds) && addressElapseds.IsEmpty == false) { - foreach (var addressElapsed in speedTestResult.AddressElapseds) + foreach (var addressElapsed in addressElapseds) { yield return addressElapsed.Adddress; } } else { - this.dnsEndPointAddressTestResult.TryAdd(endPoint, IPAddressTestResult.Empty); + this.dnsEndPointAddressElapseds.TryAdd(endPoint, IPAddressElapsedCollection.Empty); await foreach (var adddress in this.dnsClient.ResolveAsync(endPoint.Host, cancellationToken)) { yield return adddress; } } } + + /// + /// 对所有节点进行测速 + /// + /// + /// + public async Task TestAllEndPointsAsync(CancellationToken cancellationToken) + { + foreach (var keyValue in this.dnsEndPointAddressElapseds) + { + if (keyValue.Value.IsEmpty || keyValue.Value.IsExpired) + { + var dnsEndPoint = keyValue.Key; + var addresses = new List(); + await foreach (var adddress in this.dnsClient.ResolveAsync(dnsEndPoint.Host, cancellationToken)) + { + addresses.Add(adddress); + } + + var addressElapseds = IPAddressElapsedCollection.Empty; + if (addresses.Count == 1) + { + var addressElapsed = new IPAddressElapsed(addresses[0], TimeSpan.Zero); + addressElapseds = new IPAddressElapsedCollection(addressElapsed); + } + else if (addresses.Count > 1) + { + var tasks = addresses.Select(address => IPAddressElapsed.ParseAsync(address, dnsEndPoint.Port, cancellationToken)); + addressElapseds = new IPAddressElapsedCollection(await Task.WhenAll(tasks)); + } + this.dnsEndPointAddressElapseds[dnsEndPoint] = addressElapseds; + } + } + } } } diff --git a/FastGithub.DomainResolve/IDomainResolver.cs b/FastGithub.DomainResolve/IDomainResolver.cs index 45037db..45877db 100644 --- a/FastGithub.DomainResolve/IDomainResolver.cs +++ b/FastGithub.DomainResolve/IDomainResolver.cs @@ -16,13 +16,6 @@ namespace FastGithub.DomainResolve /// 域名 void Prefetch(string domain); - /// - /// 对所有节点进行测速 - /// - /// - /// - Task TestAllEndPointsAsync(CancellationToken cancellationToken); - /// /// 解析ip /// @@ -38,5 +31,12 @@ namespace FastGithub.DomainResolve /// /// IAsyncEnumerable ResolveAllAsync(DnsEndPoint endPoint, CancellationToken cancellationToken = default); + + /// + /// 对所有节点进行测速 + /// + /// + /// + Task TestAllEndPointsAsync(CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/FastGithub.DomainResolve/IPAddressElapsed.cs b/FastGithub.DomainResolve/IPAddressElapsed.cs index e5d21ba..f5e6f38 100644 --- a/FastGithub.DomainResolve/IPAddressElapsed.cs +++ b/FastGithub.DomainResolve/IPAddressElapsed.cs @@ -1,27 +1,32 @@ using System; using System.Diagnostics; using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; namespace FastGithub.DomainResolve { /// - /// IP连接耗时 + /// IP延时 /// [DebuggerDisplay("Adddress={Adddress} Elapsed={Elapsed}")] struct IPAddressElapsed { + private static readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(10d); + /// /// 获取IP地址 /// public IPAddress Adddress { get; } /// - /// 获取连接耗时 + /// 获取延时 /// public TimeSpan Elapsed { get; } /// - /// IP连接耗时 + /// IP延时 /// /// /// @@ -30,5 +35,34 @@ namespace FastGithub.DomainResolve this.Adddress = adddress; this.Elapsed = elapsed; } + + /// + /// 获取连接耗时 + /// + /// + /// + /// + /// + public static async Task ParseAsync(IPAddress address, int port, CancellationToken cancellationToken) + { + var stopWatch = Stopwatch.StartNew(); + try + { + using var timeoutTokenSource = new CancellationTokenSource(connectTimeout); + using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutTokenSource.Token); + using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + await socket.ConnectAsync(address, port, linkedTokenSource.Token); + return new IPAddressElapsed(address, stopWatch.Elapsed); + } + catch (Exception) + { + cancellationToken.ThrowIfCancellationRequested(); + return new IPAddressElapsed(address, TimeSpan.MaxValue); + } + finally + { + stopWatch.Stop(); + } + } } } diff --git a/FastGithub.DomainResolve/IPAddressElapsedCollection.cs b/FastGithub.DomainResolve/IPAddressElapsedCollection.cs new file mode 100644 index 0000000..bef3dfc --- /dev/null +++ b/FastGithub.DomainResolve/IPAddressElapsedCollection.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace FastGithub.DomainResolve +{ + /// + /// IP延时集合 + /// + [DebuggerDisplay("Count={Count} IsExpired={IsExpired}")] + sealed class IPAddressElapsedCollection : IEnumerable + { + private readonly List addressElapseds; + private readonly int creationTickCount = Environment.TickCount; + private static readonly int maxLifeTime = 60 * 1000; + + /// + /// 获取空的 + /// + public static IPAddressElapsedCollection Empty = new(); + + /// + /// 获取数量 + /// + public int Count => this.addressElapseds.Count; + + /// + /// 获取是否为空 + /// + public bool IsEmpty => this.addressElapseds.Count == 0; + + /// + /// 获取是否已过期 + /// + public bool IsExpired => Environment.TickCount - this.creationTickCount > maxLifeTime; + + /// + /// IP延时集合 + /// + private IPAddressElapsedCollection() + { + this.addressElapseds = new List(); + this.creationTickCount = 0; + } + + /// + /// IP延时集合 + /// + /// + public IPAddressElapsedCollection(IPAddressElapsed addressElapsed) + { + this.addressElapseds = new List { addressElapsed }; + } + + /// + /// IP延时集合 + /// + /// + public IPAddressElapsedCollection(IEnumerable addressElapseds) + { + this.addressElapseds = addressElapseds.OrderBy(item => item.Elapsed).ToList(); + } + + /// + /// 获取迭代器 + /// + /// + public IEnumerator GetEnumerator() + { + return this.addressElapseds.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.addressElapseds.GetEnumerator(); + } + } +} diff --git a/FastGithub.DomainResolve/IPAddressTestResult.cs b/FastGithub.DomainResolve/IPAddressTestResult.cs deleted file mode 100644 index 38f0c86..0000000 --- a/FastGithub.DomainResolve/IPAddressTestResult.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace FastGithub.DomainResolve -{ - /// - /// IP测速结果 - /// - sealed class IPAddressTestResult - { - private static readonly TimeSpan lifeTime = TimeSpan.FromMinutes(2d); - private readonly int creationTickCount = Environment.TickCount; - - /// - /// 获取空的 - /// - public static IPAddressTestResult Empty = new(Array.Empty()); - - /// - /// 获取是否为空 - /// - public bool IsEmpty => this.AddressElapseds.Length == 0; - - /// - /// 获取是否已过期 - /// - public bool IsExpired => lifeTime < TimeSpan.FromMilliseconds(Environment.TickCount - this.creationTickCount); - - /// - /// 获取测速结果 - /// - public IPAddressElapsed[] AddressElapseds { get; } - - /// - /// 测速结果 - /// - /// - public IPAddressTestResult(IEnumerable addressElapseds) - { - this.AddressElapseds = addressElapseds.OrderBy(item => item.Elapsed).ToArray(); - } - } -} diff --git a/FastGithub.PacketIntercept/DnsInterceptHostedService.cs b/FastGithub.PacketIntercept/DnsInterceptHostedService.cs index 1ac3b12..f4c5c83 100644 --- a/FastGithub.PacketIntercept/DnsInterceptHostedService.cs +++ b/FastGithub.PacketIntercept/DnsInterceptHostedService.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Hosting; +using FastGithub.WinDiverts; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; @@ -45,6 +46,10 @@ namespace FastGithub.PacketIntercept /// public override async Task StartAsync(CancellationToken cancellationToken) { + // 首次加载驱动往往有异常,所以要提前加载 + var handle = WinDivert.WinDivertOpen("true", WinDivertLayer.Network, 0, WinDivertOpenFlags.None); + WinDivert.WinDivertClose(handle); + foreach (var solver in this.conflictSolvers) { await solver.SolveAsync(cancellationToken);