Теория:
HttpWebRequest опирается на базовую ServicePoint.ServicePoint представляет фактическое соединение с URL.Во многом аналогично тому, как ваш браузер поддерживает соединение с URL-адресом открытым между запросами и повторно использует это соединение (чтобы исключить издержки на открытие и закрытие соединения с каждым запросом), ServicePoint выполняет ту же функцию для HttpWebRequest.
Я думаю, что BindIPEndPointDelegate, который вы устанавливаете для ServicePoint, не вызывается при каждом использовании HttpWebRequest, потому что ServicePoint повторно использует соединение.Если вы могли бы принудительно закрыть соединение, то следующий вызов этого URL-адреса должен заставить ServicePoint снова вызывать BindIPEndPointDelegate.
К сожалению, не похоже, что интерфейс ServicePoint дает вам возможность напрямуюпринудительно закрыть соединение.
Два решения (каждое с немного отличающимися результатами)
1) Для каждого запроса установите HttpWebRequest.KeepAlive = false.В моем тесте это приводило к тому, что делегат Bind вызывался один-на-один с каждым запросом.
2) Установите для свойства ServicePoint ConnectionLeaseTimeout значение ноль или небольшое значение.Это будет периодически приводить к вызову делегата Bind (не один к одному с каждым запросом).
Из документации 1016 *:
Вы можете использовать это свойство, чтобы гарантировать, что активные соединения объекта ServicePoint не остаются открытыми бесконечно.Это свойство предназначено для сценариев, в которых соединения должны периодически сбрасываться и восстанавливаться, например, для сценариев балансировки нагрузки.
По умолчанию, когда KeepAlive имеет значение true для запроса, свойство MaxIdleTime устанавливает время ожидания для закрытия соединений ServicePoint.из-за бездействия.Если у ServicePoint есть активные соединения, MaxIdleTime не имеет никакого эффекта, и соединения остаются открытыми в течение неопределенного времени.
Когда для свойства ConnectionLeaseTimeout установлено значение, отличное от -1, и по истечении указанного времени активное соединение ServicePoint будетзакрывается после обслуживания запроса путем установки для KeepAlive значения false в этом запросе.
Установка этого значения влияет на все соединения, управляемые объектом 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;
}
}
Следующий (базовый) тестприводит к тому, что делегат Bind вызывается для каждого запроса:
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));
}