HttpClient.GetAsync 连接到 VPN 时超时

HttpClient.GetAsync times out when connected to VPN

C# 4.5.2 框架 HttpClient.GetAsync() 方法在系统未使用 VPN 时在 Windows 10 上运行良好。

连接 VPN 时 HttpClient.GetAsync() 对同一地址的调用只会阻塞,直到超时。 Edge 和 Chrome 访问同一地址都没有问题。

有没有办法查看发生了什么? HttpClient 有什么不同之处?

更新: 通过调用 Dns.GetHostEntry() 获得了一些有趣的线索。没有VPN 此调用仅返回所有可以连接到的 IPv4 地址。连接 VPN 客户端后,Dns.GetHostEntry() 在列表顶部返回了额外的 IPv6 地址。与所有 IPv6 地址的连接超时,但所有 IPv4 地址仍然正常。现在有没有一种方法可以在不尝试连接的情况下找出哪些地址有效,哪些地址无效?

根据我的经验,这听起来像是 VPN/防火墙问题。切换 windows 的一项快速操作是在您的 VPN 适配器属性下,尝试取消选中 "Use default gateway on remote network" - 我知道这听起来不太可能,但过去曾遇到过这个问题......

必须自己回答这个问题,因为这个问题的原因很简单,但症状却很混乱。

根本原因:

当系统未连接到 VPN 时,DNS 仅报告主机的 IPv4 地址。所有 IPv4 地址都可用。

当 VPN 连接处于活动状态时 DNS returns 除 IPv4 之外的 IPv6 地址。 IPv4 地址仍然可以访问,但 IPv6 不行。

这种无效网络配置的原因仍然是一个谜,值得单独讨论 post。

混淆部分:

无论 VPN 连接状态如何,某些应用程序都可以正常工作。

"But web browser can connect to the same host with or without VPN." 没错。浏览器可能会使用 Happy eyeballs 方法尝试同时使用 IPv4 和 IPv6 进行连接。

"But my old app has not problems connecting." 也对。一些较旧和不太旧的应用程序默认使用 IPv4 协议。必须明确实现对 IPv6 或 IPv4+IPv6 的支持。

"But it works sometimes"。当 VPN 连接不可靠时会发生这种情况。它导致各种纯属巧合的解决方案。

到底发生了什么:

HttpClient.GetAsync() 使用默认 DNS 解析,可以使用 IPv4 和 IPv6 地址进行连接。它没有歧视,也没有直接影响协议选择的方法。如果 DNS returns 无法访问地址,则 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" header 一起使用,其中包含主机名(不是 IP 地址)。下面是添加方法。

var client = new HttpClient(); 
// Add "host" header with real host name e.g. whosebug.com 
client.DefaultRequestHeaders.Add("Host", uri.Host);