啊,我误读了问题。
这不是关于如何使用随机的IWebProxy和HttpClientHandler,而是如何解决在第一次请求开始后无法重置同一HttpClientHandler的代理属性的问题。
问题在于你无法重置HttpClientHandler的代理...
```
System.InvalidOperationException: 'This instance has already started
one or more requests.
Properties can only be modified before sending the first request.'
```
但这还是相当容易的。
- HttpClientHandler的代理属性接受一个实现了IWebProxy的对象。
- IWebProxy接口有一个GetProxy方法,返回代理的Uri。
- 因此,你可以制作自己的类来实现这个接口,并控制它如何通过GetProxy返回代理的Uri。
- 你可以让它包装另一个IWebProxy,在GetProxy中它将返回内部IWebProxy的GetProxy。
- 这样,你就不必改变HttpClientHandler的代理属性,只需改变内部的IWebProxy。
实现:
public class WebProxyService
: System.Net.IWebProxy
{
protected System.Net.IWebProxy m_proxy;
public System.Net.IWebProxy Proxy
{
get { return this.m_proxy ??= System.Net.WebRequest.DefaultWebProxy; }
set { this.m_proxy = value; }
}
System.Net.ICredentials System.Net.IWebProxy.Credentials
{
get { return this.Proxy.Credentials; }
set { this.Proxy.Credentials = value; }
}
public WebProxyService()
{ }
public WebProxyService(System.Net.IWebProxy proxy)
{
this.Proxy = proxy;
}
System.Uri System.Net.IWebProxy.GetProxy(System.Uri destination)
{
return this.Proxy.GetProxy(destination);
}
bool System.Net.IWebProxy.IsBypassed(System.Uri host)
{
return this.Proxy.IsBypassed(host);
}
}
然后使用方式如下:
public class AlternatingProxy
{
public static async System.Threading.Tasks.Task Test()
{
string url = "http://aspnetmonsters.com";
System.Net.WebProxy[] proxies = new[] {
null,
new System.Net.WebProxy("104.238.172.20", 8080),
new System.Net.WebProxy("104.238.167.193", 8080),
new System.Net.WebProxy("136.244.102.38", 8080),
new System.Net.WebProxy("95.179.202.40", 8080)
};
System.Random rnd = new System.Random();
WebProxyService proxyService = new WebProxyService();
using (System.Net.Http.HttpClient hc = new System.Net.Http.HttpClient(
new System.Net.Http.HttpClientHandler { UseProxy = true, Proxy = proxyService }
))
{
hc.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148");
hc.DefaultRequestHeaders.Add("Accept-Language", "fr-FR, fr;q=0.9, en;q=0.8, it;q=0.7, *;q=0.5");
hc.DefaultRequestHeaders.Add("Referer", "https://www.baidu.com");
hc.DefaultRequestHeaders.ConnectionClose = true;
for (int i = 0; i < 10; ++i)
{
proxyService.Proxy = proxies[rnd.Next(proxies.Length)];
string response = await hc.GetStringAsync(url);
}
}
}
}
编辑:
如果您想使用单例模式,这可能是一个好主意:
public class WebProxyService
: System.Net.IWebProxy
{
protected System.Net.IWebProxy m_proxy;
public System.Net.IWebProxy Proxy
{
get { return this.m_proxy ??= System.Net.WebRequest.DefaultWebProxy; }
set { this.m_proxy = value; }
}
System.Net.ICredentials System.Net.IWebProxy.Credentials
{
get { return this.Proxy.Credentials; }
set { this.Proxy.Credentials = value; }
}
public WebProxyService()
{ }
public WebProxyService(System.Net.IWebProxy proxy)
{
this.Proxy = proxy;
}
protected System.Func<System.Net.WebProxy>[] proxies = new System.Func<System.Net.WebProxy>[] {
delegate(){ return new System.Net.WebProxy("104.238.172.20", 8080); },
delegate (){ return new System.Net.WebProxy("104.238.167.193", 8080);},
delegate(){ return new System.Net.WebProxy("136.244.102.38", 8080);},
delegate(){ return new System.Net.WebProxy("95.179.202.40", 8080);}
};
System.Uri System.Net.IWebProxy.GetProxy(System.Uri destination)
{
return proxies[RandomGen2.Next(proxies.Length)]().GetProxy(destination);
}
bool System.Net.IWebProxy.IsBypassed(System.Uri host)
{
return this.Proxy.IsBypassed(host);
}
private static class RandomGen2
{
private static System.Random _global = new System.Random();
[System.ThreadStatic]
private static System.Random _local;
public static int Next(int maxValue)
{
System.Random inst = _local;
if (inst == null)
{
int seed;
lock (_global) seed = _global.Next();
_local = inst = new System.Random(seed);
}
return inst.Next(maxValue);
}
}
}
编辑 2:
如果你更改代理,它仍然不是线程安全的。
因此,使用一个固定的不可变代理列表,并阻止设置属性。
这样,它应该是线程安全的。
public class WebProxyService
: System.Net.IWebProxy
{
protected System.Net.IWebProxy[] m_proxyList;
public System.Net.IWebProxy Proxy
{
get
{
if (this.m_proxyList != null)
return this.m_proxyList[ThreadSafeRandom.Next(this.m_proxyList.Length)];
return System.Net.WebRequest.DefaultWebProxy;
}
set
{
throw new System.InvalidOperationException("It is not thread-safe to change the proxy-list.");
}
}
System.Net.ICredentials System.Net.IWebProxy.Credentials
{
get { return this.Proxy.Credentials; }
set { this.Proxy.Credentials = value; }
}
public WebProxyService()
{
}
public WebProxyService(System.Net.IWebProxy[] proxyList)
{
this.m_proxyList = proxyList;
}
System.Uri System.Net.IWebProxy.GetProxy(System.Uri destination)
{
return this.Proxy.GetProxy(destination);
}
bool System.Net.IWebProxy.IsBypassed(System.Uri host)
{
return this.Proxy.IsBypassed(host);
}
}
Old answer:
----
Using a proxy with HttpClient in ASP.NET-Core is actually quite simple.
All you need to do is set the handler in the HttpClient-constructor.
Then set the proxy-property of the handler for each request.
Like this:
public class Program
{
public static async System.Threading.Tasks.Task Main(string[] args)
{
string url = "http://aspnetmonsters.com";
System.Net.WebProxy[] proxies = new[] {
null,
new System.Net.WebProxy("104.238.172.20", 8080),
new System.Net.WebProxy("104.238.167.193", 8080),
new System.Net.WebProxy("136.244.102.38", 8080),
new System.Net.WebProxy("95.179.202.40", 8080)
};
System.Random rnd = new System.Random();
using (System.Net.Http.HttpClientHandler handler = new System.Net.Http.HttpClientHandler()
{
Proxy = new System.Net.WebProxy("http://127.0.0.1:8888"),
UseProxy = true,
})
{
using (System.Net.Http.HttpClient hc = new System.Net.Http.HttpClient(handler))
{
System.Console.WriteLine("Starting connections");
for (int i = 0; i < 10; i++)
{
handler.Proxy = proxies[rnd.Next(proxies.Length)];
await hc.GetAsync(url);
hc.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148");
hc.DefaultRequestHeaders.Add("Accept-Language", "fr-FR, fr;q=0.9, en;q=0.8, it;q=0.7, *;q=0.5");
hc.DefaultRequestHeaders.Add("Referer", "https://www.baidu.com");
using (System.Net.Http.HttpResponseMessage response = await hc.GetAsync(url))
{
byte[] ba = await response.Content.ReadAsByteArrayAsync();
}
}
System.Console.WriteLine("Ending connections");
}
}
System.Console.WriteLine("--- Press any key to continue --- ");
System.Console.ReadKey();
}
}