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); } }
然后:
UseIP useIP = new UseIP("Valid IP address here..."); Uri uri = new Uri("http://ip.nefsc.noaa.gov"); HttpWebRequest request = useIP.CreateWebRequest(uri); // Then make the request with the specified IP address
但解决方案第一次工作!
解决方法
HttpWebRequest依赖于底层ServicePoint. ServicePoint表示与URL的实际连接.与浏览器保持与请求之间打开的URL的连接并重用该连接(为了消除打开和关闭每个请求的连接的开销),ServicePoint执行与HttpWebRequest相同的功能.
我认为您正在为ServicePoint设置的BindIPEndPointDelegate在每次使用HttpWebRequest时都不会被调用,因为ServicePoint正在重用该连接.如果您可以强制连接关闭,则下一次调用该URL应该导致ServicePoint需要再次调用BindIPEndPointDelegate.
不幸的是,ServicePoint界面并不能让您直接强制连接关闭.
两个解决方案(每个结果略有不同)
1)对于每个请求,设置HttpWebRequest.KeepAlive = false.在我的测试中,这导致绑定委托每个请求一一对应.
2)将ServicePoint ConnectionLeaseTimeout属性设置为零或一些小值.这将有效地定期强制绑定委托被调用(不是每个请求一对一).
You can use this property to ensure that a ServicePoint object’s
active connections do not remain open indefinitely. This property is
intended for scenarios where connections should be dropped and
reestablished periodically,such as load balancing scenarios.By default,when KeepAlive is true for a request,the MaxIdleTime
property sets the time-out for closing ServicePoint connections due to
inactivity. If the ServicePoint has active connections,MaxIdleTime
has no effect and the connections remain open indefinitely.When the ConnectionLeaseTimeout property is set to a value other than
-1,and after the specified time elapses,an active ServicePoint connection is closed after servicing a request by setting KeepAlive to
false in that request.
设置此值会影响由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)); }