C# 绑定IP地址只是第一次起作用
我想从服务器上的一个可用IP地址发出web请求,因此我使用此类:C# 绑定IP地址只是第一次起作用,c#,binding,ip-address,multihomed,C#,Binding,Ip Address,Multihomed,我想从服务器上的一个可用IP地址发出web请求,因此我使用此类: public class UseIP { public string IP { get; private set; } public UseIP(string IP) { this.IP = IP; } public HttpWebRequest CreateWebRequest(Uri uri) { ServicePoint servicePoi
public class UseIP
{
public string IP { get; private set; }
public UseIP(string IP)
{
this.IP = IP;
}
public HttpWebRequest CreateWebRequest(Uri uri)
{
ServicePoint servicePoint = ServicePointManager.FindServicePoint(uri);
servicePoint.BindIPEndPointDelegate = new BindIPEndPoint(Bind);
return WebRequest.Create(uri) as HttpWebRequest;
}
private IPEndPoint Bind(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount)
{
IPAddress address = IPAddress.Parse(this.IP);
return new IPEndPoint(address, 0);
}
}
然后:
但是这个解决方案第一次就奏效了 我对您的示例做了一些更改,使其在我的机器上运行:
public HttpWebRequest CreateWebRequest(Uri uri)
{
HttpWebRequest wr = WebRequest.Create(uri) as HttpWebRequest;
wr.ServicePoint.BindIPEndPointDelegate = new BindIPEndPoint(Bind);
return wr;
}
我这样做是因为:
- 我认为对
的调用实际上使用“默认”ip对指定的URI执行请求,甚至不调用绑定委托。至少在我的机器中,FindServicePoint
没有以您提供的方式被调用(我知道请求是因为我没有设置代理,并且得到了代理身份验证错误)李>BindIPEndPointDelegate
- 在的文档中,它指出“如果该主机和方案存在现有ServicePoint对象,ServicePointManager对象将返回现有ServicePoint对象;否则,ServicePointManager对象将创建新的ServicePoint对象”,如果URI相同,它可能会始终返回相同的ServicePoint对象(可能解释了为什么后续调用发生在同一端点中)
- 通过这种方式,我们可以确保,即使已经请求了URI,它也将使用所需的IP,而不是使用
以前的一些“缓存”ServicePointManager
理论:
HttpWebRequest依赖于基础ServicePoint。ServicePoint表示与URL的实际连接。这与浏览器在请求之间保持与URL的连接打开并重用该连接的方式大致相同(以消除每次请求打开和关闭连接的开销),ServicePoint对HttpWebRequest执行相同的功能
我认为在每次使用HttpWebRequest时都不会调用您为ServicePoint设置的BindIPEndPointDelegate,因为ServicePoint正在重用连接。如果您可以强制关闭连接,那么对该URL的下一次调用应该会导致ServicePoint需要再次调用BindIPEndPointDelegate
不幸的是,ServicePoint接口似乎没有提供直接强制关闭连接的功能
两种解决方案(每个方案的结果略有不同)
1) 对于每个请求,设置HttpWebRequest.KeepAlive=false。在我的测试中,这导致绑定委托在每个请求中被逐个调用
2) 将ServicePoint ConnectionLeaseTimeout属性设置为零或某个小值。这将定期强制调用绑定委托(而不是每个请求一对一)
从:
可以使用此属性确保ServicePoint对象的
活动连接不会无限期保持打开状态。这个物业是
适用于应断开连接并
定期重新建立,例如负载平衡场景
默认情况下,当请求的KeepAlive为true时,MaxIdleTime
属性设置由于以下原因关闭ServicePoint连接的超时:
不活动。如果ServicePoint具有活动连接,则MaxIdleTime
无效,连接将无限期保持打开状态
当ConnectionLeaseTimeout属性设置为除
-1,并在指定的时间过后,通过将KeepAlive设置为,在为请求提供服务后关闭活动的ServicePoint连接
这个请求是错误的
设置此值会影响ServicePoint对象管理的所有连接
public class UseIP
{
public string IP { get; private set; }
public UseIP(string IP)
{
this.IP = IP;
}
public HttpWebRequest CreateWebRequest(Uri uri)
{
ServicePoint servicePoint = ServicePointManager.FindServicePoint(uri);
servicePoint.BindIPEndPointDelegate = (servicePoint, remoteEndPoint, retryCount) =>
{
IPAddress address = IPAddress.Parse(this.IP);
return new IPEndPoint(address, 0);
};
//Will cause bind to be called periodically
servicePoint.ConnectionLeaseTimeout = 0;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
//will cause bind to be called for each request (as long as the consumer of the request doesn't set it back to true!
req.KeepAlive = false;
return req;
}
}
每个请求都会调用绑定委托中的以下(基本)测试结果:
static void Main(string[] args)
{
//Note, I don't have a multihomed machine, so I'm not using the IP in my test implementation. The bind delegate increments a counter and returns IPAddress.Any.
UseIP ip = new UseIP("111.111.111.111");
for (int i = 0; i < 100; ++i)
{
HttpWebRequest req = ip.CreateWebRequest(new Uri("http://www.yahoo.com"));
using (WebResponse response = req.GetResponse())
{
}
}
Console.WriteLine(string.Format("Req: {0}", UseIP.RequestCount));
Console.WriteLine(string.Format("Bind: {0}", UseIP.BindCount));
}
static void Main(字符串[]args)
{
//请注意,我没有多宿主计算机,因此我在测试实现中没有使用IP。绑定委托递增一个计数器并返回IPAddress.Any。
UseIP ip=新的UseIP(“111.111.111.111”);
对于(int i=0;i<100;++i)
{
HttpWebRequest req=ip.CreateWebRequest(新Uri(“http://www.yahoo.com"));
使用(WebResponse-response=req.GetResponse())
{
}
}
WriteLine(string.Format(“Req:{0}”,UseIP.RequestCount));
WriteLine(string.Format(“Bind:{0}”,UseIP.BindCount));
}
问题可能是代理在每次新请求时都被重置。请尝试以下内容:
//servicePoint.BindIPEndPointDelegate = null; // Clears all delegates first, for testing
servicePoint.BindIPEndPointDelegate += delegate
{
var address = IPAddress.Parse(this.IP);
return new IPEndPoint(address, 0);
};
此外,据我所知,端点是缓存的,因此在某些情况下,即使清除委托也可能不起作用,而且它们可能会被重置。最坏情况下,您可以卸载/重新加载应用程序域。我喜欢这个新类UseIP 关于保护自己免受IPv4/IPv6差异的影响,有一点值得商榷 唯一需要更改的是Bind方法如下:
private IPEndPoint Bind(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount)
{
if ((null != IP) && (IP.AddressFamily == remoteEndPoint.AddressFamily))
return new IPEndPoint(this.IP, 0);
if (AddressFamily.InterNetworkV6 == remoteEndPoint.AddressFamily)
return new IPEndPoint(IPAddress.IPv6Any, 0);
return new IPEndPoint(IPAddress.Any, 0);
}
re:正在多次调用的绑定方法
对我来说,有效的方法是在添加代理链接之前删除它
ServicePoint servicePoint = ServicePointManager.FindServicePoint(uri);
servicePoint.BindIPEndPointDelegate -= this.Bind; // avoid duplicate calls to Bind
servicePoint.BindIPEndPointDelegate += this.Bind;
我还喜欢缓存UseIP对象的想法。因此,我将这个静态方法添加到UseIP类中
private static Dictionary<IPAddress, UseIP> _eachNIC = new Dictionary<IPAddress, UseIP>();
public static UseIP ForNIC(IPAddress nic)
{
lock (_eachNIC)
{
UseIP useIP = null;
if (!_eachNIC.TryGetValue(nic, out useIP))
{
useIP = new UseIP(nic);
_eachNIC.Add(nic, useIP);
}
return useIP;
}
}
private static Dictionary\u eachNIC=new Dictionary();
公共静态使用IP ForNIC(IP地址nic)
{
锁(_eachNIC)
{
UseIP-UseIP=null;
如果(!\u每个技术测试值(nic,out useIP))
{
useIP=新的useIP(nic);
_添加(nic、useIP);
}
返回useIP;
}
}
是,我想快速更改我的IP地址。我应该采取什么方法?你有什么异常吗?你可以尝试只在第一次绑定时将其保存在静态或实例变量中?@alexD:没有。只有第一个绑定的IP可以工作。UseIP
类的后续实例将使用相同的IP地址。奖金将授予“频繁更改web请求的IP地址”的解决方案。类似于UseIP
class的东西很有效。我以前也试过。如果您快速更改IP地址,您将发现
private static Dictionary<IPAddress, UseIP> _eachNIC = new Dictionary<IPAddress, UseIP>();
public static UseIP ForNIC(IPAddress nic)
{
lock (_eachNIC)
{
UseIP useIP = null;
if (!_eachNIC.TryGetValue(nic, out useIP))
{
useIP = new UseIP(nic);
_eachNIC.Add(nic, useIP);
}
return useIP;
}
}