绑定IP地址只是第一次使用

Xaq*_*ron 10 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 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后:

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
Run Code Online (Sandbox Code Playgroud)

但解决方案刚刚第一次运行!

Joe*_*ger 15

一个理论:

HttpWebRequest依赖于底层的ServicePoint.ServicePoint表示与URL的实际连接.与您的浏览器保持连接到请求之间的URL并重用该连接(以消除打开和关闭每个请求的连接的开销)的方式大致相同,ServicePoint为HttpWebRequest执行相同的功能.

我认为,每次使用HttpWebRequest时都不会调用为ServicePoint设置的BindIPEndPointDelegate,因为ServicePoint正在重用连接.如果您可以强制关闭连接,则对该URL的下一次调用应该导致ServicePoint需要再次调用BindIPEndPointDelegate.

不幸的是,ServicePoint接口似乎没有让您能够直接强制关闭连接.

两种解决方案(每种解决方案略有不同)

1)对于每个请求,设置HttpWebRequest.KeepAlive = false.在我的测试中,这导致Bind委托与每个请求一对一调用.

2)将ServicePoint ConnectionLeaseTimeout属性设置为零或一些小值.这将产生定期强制调用绑定委托的效果(不是每个请求一对一).

文档:

您可以使用此属性来确保ServicePoint对象的活动连接不会无限期保持打开状态.此属性适用于应定期删除和重新建立连接的情况,例如负载平衡方案.

默认情况下,保持活动是要求真,MaxIdleTime属性设置超时关闭的ServicePoint连接由于不活动.如果ServicePoint具有活动连接,则MaxIdleTime无效,连接将无限期保持打开状态.

当ConnectionLeaseTimeout属性设置为-1以外的值时,在指定的时间过去之后,通过在该请求中将KeepAlive设置为false,在服务请求后关闭活动的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

以下(基本)测试结果为每个请求调用绑定委托:

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));
    }
Run Code Online (Sandbox Code Playgroud)