必须自己回答这个问题,因为这个问题的原因很简单,但症状很混乱。
根本原因:
当系统未连接到 VPN 时,DNS 仅报告主机的 IPv4 地址。所有 IPv4 地址均可用。
当 VPN 连接处于活动状态时,除了 IPv4 之外,DNS 还会返回 IPv6 地址。 IPv4 地址仍可访问,但 IPv6 不可访问。
这种无效网络配置的原因仍然是一个谜,值得单独发布。
令人困惑的部分:
无论 VPN 连接状态如何,某些应用程序都能正常工作。
“但无论是否使用 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 连接,数字地址仅适用于包含主机名称(不是 IP 地址)的“主机”标头。这是添加它的方法。
var client = new HttpClient();
// Add "host" header with real host name e.g. stackoverflow.com
client.DefaultRequestHeaders.Add("Host", uri.Host);