专栏首页林德熙的博客dotnet 6 为什么网络请求不跟随系统网络代理变化而动态切换代理

dotnet 6 为什么网络请求不跟随系统网络代理变化而动态切换代理

本文记录在 dotnet 6 的网络和在 .NET Framework 的行为的变更。在 dotnet 6 下,默认的网络请求在系统网络代理变更的时候,是不会动态切换代理的。例如在应用运行进行网络通讯之后,打开 Fiddler 抓包,此时将会发现 Fiddler 抓不到包,只有在应用重启之后才能抓到。或者是开着 Fiddler 抓包,然后退出 Fiddler 之后应用就断网了

如此行为是因为 Fiddler 抓包其中的一个原理就是设置系统的本机网络代理,而由于 dotnet 6 下,应用不会动态切换代理,如果在应用启动进行网络通讯之后,再打开 Fiddler 抓包,在 Fiddler 打开之后,将会修改系统的本机网络代理,但是 dotnet 6 的应用由于默认不会动态切换代理从而不走 Fiddler 的代理,因此 Fiddler 抓不到包。同理,在开着 Fiddler 抓包之后,退出了 Fiddler 将会修改本机的网络代理,但是由于 dotnet 6 的应用默认不会动态切换代理,在 Fiddler 修改了本机网络代理之后,依然 dotnet 6 的应用还在使用着被关闭的 Fiddler 的网络代理从而断网

核心原因是在 dotnet 6 下变更了网络代理动态切换的行为。其实考古找到这个行为在 .NET Core 2.0 就是默认不支持自动跟随系统代理切换而修改代理

在 .NET Framework 的 4.0 开始,通过监听注册表的 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections 的变更,在变更之后进行刷新网络请求的代理。详细请看 https://referencesource.microsoft.com/#System/net/System/Net/_AutoWebProxyScriptEngine.cs,395

在 .NET Core 下,网络代理的获取只有一次,获取到的代理没有再去监听注册表的变更,也就没有再次刷新。此问题已反馈给官方,详细请看 https://github.com/dotnet/runtime/issues/46910

在 .NET Core 将会在首次获取 HttpClient.DefaultProxy 时进行初始化,值得一提的是在 .NET Core 调用的 WebRequest.GetSystemWebProxy 方法底层也是调用 HttpClient.DefaultProxy 属性

 public static IWebProxy GetSystemWebProxy() => HttpClient.DefaultProxy;

以上的 GetSystemWebProxy 实现请看 Make WebRequest.GetSystemWebProxy() return a working proxy by stephentoub · Pull Request #41692 · dotnet/corefx

在 HttpClient.DefaultProxy 里面,将会调用到 SystemProxyInfo.cs 的 ConstructSystemProxy 方法获取对应平台的代理。这个 ConstructSystemProxy 在 OSX 和 Unix 和 Windows 有各自的实现

在 Windows 实现如下

        public static IWebProxy ConstructSystemProxy()
        {
            if (!HttpEnvironmentProxy.TryCreate(out IWebProxy? proxy))
            {
                HttpWindowsProxy.TryCreate(out proxy);
            }

            return proxy ?? new HttpNoProxy();
        }

在 HttpEnvironmentProxy 里面,将尝试通过环境变量获取代理的配置,也就是说 dotnet 6 应用是支持通过环境变量设置代理,如此更加方便调试。获取的环境变量分别是 ALL_PROXYHTTP_PROXYHTTPS_PROXY 这几个惯例变量

如上面代码,如果获取不到环境变量,那么就进入 HttpWindowsProxy 的代码。在 WinInetProxyHelper 将会读取系统的代理

如上面代码,可以看到,实际上在 HttpClient.DefaultProxy 里面只会获取一次,没有通过注册表的变更再次刷新

这就是网络请求不跟随本机网络代理变化的原因

一个解决方法就是拷贝 dotnet runtime 的读取系统的配置方法,再加上监听注册表变更进行刷新配置,从而实现动态跟随系统代理变化而变化。我拷贝了代码,写了一个版本,使用方法是

var dynamicHttpWindowsProxy = new DynamicHttpWindowsProxy();

HttpClient.DefaultProxy = dynamicHttpWindowsProxy; 

代码的实现放在githubgitee 欢迎访问

可以通过如下方式获取源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 8c64e9676c4205e55fad227a86d5d8d95a5ebe91

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 8c64e9676c4205e55fad227a86d5d8d95a5ebe91

获取代码之后,进入 NilerlanaihikaWhurreeberhalur 文件夹,具体实现放在 Proxy 文件里面,在 Program.cs 包含了测试逻辑,可以不断尝试访问百度。可以测试在使用 HttpClient.DefaultProxy = dynamicHttpWindowsProxy; 时,切换 Fiddler 代理配置,和不使用 DynamicHttpWindowsProxy 切换配置的行为

以上代码基本都是从 dotnet runtime 里面抄的,可以放心用在正式的项目。监听注册表变更是从 https://www.codeproject.com/Articles/4502/RegistryMonitor-a-NET-wrapper-class-for-RegNotifyC 抄的,这是一段比较古老稳定的代码,只不过需要多开启一个线程用来监听注册表。这就是为什么在例子代码里面,会延迟去启动监听注册表

参考文档:

[How to auto reset Fiddler when ‘system proxy was changed’ in Fiddler

Telerik Forums](https://www.telerik.com/forums/how-to-auto-reset-fiddler-when-%27system-proxy-was-changed%27 )

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:https://blog.lindexi.com/复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

推荐阅读

  • appscan扫出API成批分配问题解决方案

    APPScan 10.0.7新增的扫描功能 HCL AppScan® Enterprise 中的新增功能 (hcltechsw.com),增加了批量分配API的规则,导致以前系统没扫出来的问题,现在也被扫了出来。

    西门呀在吹雪
    JSON网络安全安全
  • Pinia.js - Vue新一代状态管理器

    安装成功会把依赖加在 package.json 的 dependencies 中。

    青年码农
    Vue.jsJavaScriptTypeScripthttpsGitHub
  • 大数据必学Java基础(五十五):泛型深入了解

    集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,

    Lanson
    Java
  • Impala fe query plan

    Be->Coordinator(Fe->be)->Be Executor->Coordinator

    jasong
    数据库云数据仓库
  • Kubernetes小技巧关于节点pod ip node数量规划

    最近就想体验各种多集群互联(基于wireguard),然后就深感网络划分的重要性,开始网络设计的杂七乱八的。想互联了都各种问题了,网络重叠了怎么办?集群扩容IP资源不够了杂整?还有就是默认的每个node节点的subset都默认是24?我一台机器上面也跑不了那么多Pod阿......

    对你无可奈何
    Node.jsTCP/IPKubernetes
  • 【CLS 数据淘金第一期】负载均衡 CLB 日志可视化分析大洞察

    产品使用问题、技术咨询欢迎加入 CLS 粉丝群,直接跟日志专家 1v1 对话,更有超多粉丝福利,快来加入我们吧。

    日志服务CLS小助手
    负载均衡日志服务运维
  • 【云安全最佳实践】基础防护建设

    做好基线核查和加固是安全管理工作中的基础工作,但却与安全事件密切相关。例如系统账号登录策略未做好合规要求,黑客就可以通过弱口令、默认口令等方式登录系统。但如果做好基线核查和系统加固,既可以很好应对监管部门安全检查,也可以增加黑客入侵的困难,在面对突发安全事件或0Day漏洞时候有足够的响应处理时间,因此安全基线管理工作不可缺少。

    Khan安全团队
    黑客网络安全安全移动应用安全漏洞扫描
  • JS算法之回溯法

    今天,我们继续探索JS算法相关的知识点。我们来谈谈关于「回溯法」的相关知识点和具体的算法。

    前端柒八九
    编程算法
  • 【玩转 Cloud Studio】初识 Cloud Studio 神器

    Cloud Studio 是一种基于浏览器的集成式开发环境(IDE),向开发者提供了一个永不间断的云端工作站。用户在使用 Cloud Studio 时无需安装应用程序和插件,只要打开浏览器就能使用,非常的方便和用好。此外,Cloud Studio 还包含代码高亮、自动补全、Git 集成、终端等 IDE 的基础功能,同时支持实时调试、插件扩展等,可以帮助开发者快速完成各种应用的开发、编译与部署工作。

    liuzhen007
    腾讯云IDE云服务器网站建设打包Go
  • 大数据必学Java基础(五十四):List接口深入了解

    在JDK1.7中:在调用构造器的时候给底层数组elementData初始化,数组初始化长度为10

    Lanson
    Java

扫码关注腾讯云开发者

领取腾讯云代金券