接入指南
C# 代理设置:HttpClient 与 WebProxy
在 .NET 里,代理属于 HttpClient 下层的处理器。WebProxy 保存地址,NetworkCredential 保存登录信息,处理器会在打开 HTTPS 隧道之前自己回应代理的 407 质询。示例在 .NET 10 SDK 上运行过。
开始之前
- .NET SDK 6 或更高版本。代码依赖控制台模板从 .NET 6 起默认生成的隐式 using 和顶级语句。
- 一个终端。第 1 步会用 dotnet new console 创建项目。
- 从服务页面复制的 HOST、PORT、USERNAME 和 PASSWORD。
你的连接信息
登录控制台,打开你购买的服务。服务页面上会显示该服务的主机、端口、用户名和密码。ProxyPanda 没有一个统一的固定地址,所以每次都请从那里复制。指南中的代码使用下面这些占位符,请逐一替换成你自己的值。
- HOST
- 该服务的代理地址,按服务页面上显示的原样填写。
- PORT
- 要连接的端口。请和主机一起复制,因为不同服务的端口可能不同。
- USERNAME
- 你的代理登录名。它与你登录控制台所用的邮箱是分开的。
- PASSWORD
- 请完整复制。住宅代理在控制台中选择的选项会附加在密码末尾,凭记忆手动输入的密码会丢失这些选项。
把你用来连接的 IP 地址加入服务的白名单,就可以不用用户名和密码。这时代码里只需要 HOST 和 PORT。
示例都使用 HTTP,它可以通过加密隧道访问 https 网站。SOCKS5 同样可用:住宅代理的端口两种都接受,ISP 和数据中心代理在控制台里切换协议。
步骤
创建控制台项目
dotnet --version 用来确认 SDK。模板生成的 Program.cs 已启用隐式 using,所以下面每个示例只需再加一行 using,引入 WebProxy 和 NetworkCredential。用示例替换 Program.cs 的内容,再用 dotnet run 启动。
terminal dotnet --version dotnet new console -o ProxyCheck cd ProxyCheck给处理器配置带凭据的 WebProxy
WebProxy 以 http://HOST:PORT 的形式接收代理地址,它的 Credentials 属性保存 USERNAME 和 PASSWORD。UseProxy 默认就是 true;特意写出来,是因为 UseProxy 为 false 的处理器会悄无声息地忽略 Proxy。HttpClient.Timeout 限制每个请求的时间,建立隧道也算在内。
Program.cs using System.Net; var handler = new HttpClientHandler { Proxy = new WebProxy("http://HOST:PORT") { Credentials = new NetworkCredential("USERNAME", "PASSWORD"), }, UseProxy = true, }; using var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(30) }; var response = await client.GetAsync("https://api.ipify.org?format=json"); Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");运行并查看出口地址
dotnet run 会编译并启动项目,打印 200 和 api.ipify.org 返回的 JSON。测试代理记录了两次 CONNECT:第一次没带登录信息,被 407 拒绝,第二次带上了。处理器会自己完成这次质询。
每个代理只用一个 HttpClient
每个请求都新建并释放一个 HttpClient,连接每次都会关闭,关闭后的套接字还要在 TIME_WAIT 状态停留一段时间。负载持续时,机器可能耗尽本地端口,表现为和代理毫无关系的连接错误。客户端只创建一次,在程序运行期间一直复用。
用 SocketsHttpHandler 在静态 IP 之间轮换
第二个 Program.cs 从项目文件夹读取 proxies.txt,每个固定 IP 一行 HOST:PORT:USERNAME:PASSWORD,并为每一行建一个 SocketsHttpHandler 和对应的 HttpClient。ConnectTimeout 限制连上代理的时间,PooledConnectionLifetime 让池中的连接在五分钟后退役。第 i 个请求交给第 i 除以列表长度取余对应的客户端;出错的代理会打印错误信息,其他代理照常工作。
Program.cs using System.Net; var clients = File.ReadLines("proxies.txt") .Where(line => !string.IsNullOrWhiteSpace(line)) .Select(line => { var p = line.Trim().Split(':', 4); var handler = new SocketsHttpHandler { Proxy = new WebProxy(p[0], int.Parse(p[1])) { Credentials = new NetworkCredential(p[2], p[3]), }, UseProxy = true, ConnectTimeout = TimeSpan.FromSeconds(10), PooledConnectionLifetime = TimeSpan.FromMinutes(5), }; return new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(30) }; }) .ToList(); for (var i = 0; i < 6; i++) { var client = clients[i % clients.Count]; try { Console.WriteLine($"{i} {await client.GetStringAsync("https://api.ipify.org")}"); } catch (Exception e) when (e is HttpRequestException or TaskCanceledException) { Console.WriteLine($"{i} {e.Message}"); } }
轮换 IP 与粘性 IP
处理器会把连接放进连接池。测试中,同一个客户端的三次请求共用一条 CONNECT 隧道,共用的隧道出口地址不变。使用“粘性 IP”或 ISP、数据中心 IP 时,这正是想要的结果。
住宅代理选“随机 IP”时,把 PooledConnectionLifetime 设为 TimeSpan.Zero,或在每个 HttpRequestMessage 上把 Headers.ConnectionClose 设为 true。测试中两种做法都让每个请求建立了新隧道,而每条新隧道都可能从不同的地址发出。
一个 HttpClient 可以同时服务多个并发任务,所以工作池只需要每个代理一个客户端。
常见错误与解决办法
HttpRequestException: The proxy tunnel request to proxy '…' failed with status code '407'
代理拒绝了登录。重新复制 USERNAME 和 PASSWORD,并确认 NetworkCredential 设置在 WebProxy 上:处理器本身的 Credentials 是发给网站的,不是发给代理的。
HttpRequestException: Connection refused
从你的网络连到 HOST:PORT 时没有程序在监听。把两个值和服务页面对照,并确认服务没有到期。
TaskCanceledException: The request was canceled due to the configured HttpClient.Timeout
在 HttpClient.Timeout 内没有收到响应。住宅出口走的是家庭网络,有些较慢,给这类请求更多时间,并只重试失败的那个网址。
响应里显示的是你自己的 IP
WebProxy 设置在了另一个处理器上,而不是传给 HttpClient 的那个,或者 UseProxy 为 false。先创建处理器、设置 Proxy,再把同一个实例交给客户端。
其他接入指南
有不明白的词? 术语表用大白话解释。
卡在某一步了?
把命令和报错贴到 Discord,记得先删掉密码。那里的人大多遇到过这些错误。