作为这个问题的根本原因,当系统未连接到VPN时,DNS仅报告主机的IPv4地址。所有IPv4地址都可以使用。
当VPN连接处于活动状态时,DNS除了IPv4地址外还返回IPv6地址。IPv4地址仍然可访问,但IPv6则不行。
这种无效的网络配置的原因仍然是一个谜,值得单独发布一篇文章来探讨。
有些应用程序无论VPN连接状态如何都可以正常工作。
“但是Web浏览器可以在有或没有VPN的情况下连接到同一台主机。”是真的。浏览器可能使用Happy eyeballs方法,同时尝试使用IPv4和IPv6进行连接。
“但我的旧应用程序没有连接问题。”也是真的。一些较旧的和不那么旧的应用程序默认使用IPv4协议。必须显式实现对IPv6或IPv4 + IPv6的支持。
“但有时它能够正常工作”。这种情况发生在VPN连接不可靠的情况下。它会导致各种解决方案,这只是巧合。
具体发生了什么:
HttpClient.GetAsync()使用默认的DNS解析,并可以使用IPv4和IPv6地址进行连接。它不区分地址类型,也没有直接影响协议选择的方法。如果DNS返回无法访问的地址,则HttpClient可能会使用该无效地址进行连接,导致超时。
可能的解决方法:
最佳:请求IT修复IPv6 DNS问题。DNS不应报告无法访问的地址。
好:实现Happy eyeballs方法。使用数字IP连接到IPv6和IPv4主机地址,而不是使用主机名进行自动解析。
可行:始终使用数字IP连接到IPv4。
以下代码片段显示了如何连接到特定IP地址:
// Get DNS entries for the host.
var hostEntry = Dns.GetHostEntry(uri.Host)
// Get IPv4 address
var ip4 = hostEntry.AddressList.First(addr => addr.AddressFamily == AddressFamily.InterNetwork)
// Build URI with numeric IPv4
var uriBuilderIP4 = new UriBuilder(uri)
uriBuilderIP4.Host = ip4.ToString())
var uri4 = uriBuilder4.Uri
// Get IPv6 address
var ip6 = hostEntry.AddressList.First(addr => addr.AddressFamily == AddressFamily.InterNetworkV6)
// Build URI with numeric IPv6
var uriBuilderIP6 = new UriBuilder(uri)
uriBuilderIP6.Host = $"[{ip6}]";
var uri6 = uriBuilder6.Uri;
对于HTTPS连接,只有在“host”头中带有主机名(而非IP地址)时,数字地址才起作用。以下是添加它的方法。
var client = new HttpClient();
// Add "host" header with real host name e.g. stackoverflow.com
client.DefaultRequestHeaders.Add("Host", uri.Host);