仅当使用BeginGetResponse / EndGetResponse对时,FtpWebRequest在两次请求后锁定

时间:2016-09-08 12:13:02

标签: c# .net asynchronous webrequest ftpwebrequest

为了把事情放到上下文中,想象一个程序使用FtpWebRequest.BeginGetResponse()从FTP服务器检索文件的大小。如果由于某些连接错误而无法获得文件大小,则会在一段时间后再次尝试。它还允许用户取消整个操作。

重试特定次数后出现问题:如果FtpWebRequest.BeginGetResponse()在同一端点上连接的次数超过ServicePointManager.DefaultConnectionLimit,则后续回调不会被调用,等待句柄将无限期阻止。

使用TCPView,可能会发现在第一次ServicePointManager.DefaultConnectionLimit(失败)连接尝试后没有尝试新连接。

让我用一些人为的例子来证明这个问题。

假设,

  • .NET 4.6
  • ServicePointManager.DefaultConnectionLimit == 2
  • 192.168.1.2 运行示例的计算机的IP地址。
  • 192.168.1.2无法访问,或者在端口21上没有运行服务。

同步版

static void Main(string[] args){

    var uri = new Uri("ftp://192.168.1.2/test.txt");

    for (var z = 0; z < ServicePointManager.DefaultConnectionLimit + 1; z += 1){

        try {

            var request = (FtpWebRequest) WebRequest.Create(uri);
            request.Method = WebRequestMethods.Ftp.GetFileSize;

            using (var response = request.GetResponse())

                Console.WriteLine(response.ContentLength);

        } catch (WebException ex){

            Console.WriteLine(ex.Status);

        }

    }

}

以控制台输出终止,

ConnectFailure
ConnectFailure
ConnectFailure

而,

static void Main(string[] args){

    var uri = new Uri("ftp://192.168.1.2/test.txt");

    for (var z = 0; z < ServicePointManager.DefaultConnectionLimit + 1; z += 1){

        var request = (FtpWebRequest) WebRequest.Create(uri);
        request.Method = WebRequestMethods.Ftp.GetFileSize;

        request.BeginGetResponse(PrintFileSizeCallback, request);

    }

    Console.ReadKey(false);

}

static void PrintFileSizeCallback(IAsyncResult ar){

    try {

        using (var response = ((FtpWebRequest) ar.AsyncState).EndGetResponse(ar))

            Console.WriteLine(response.ContentLength);

    } catch (WebException ex){

        Console.WriteLine(ex.Status);
    }

}

将打印,

ConnectFailure
ConnectFailure

并且永远不会发生第三次回调。

即使等待异步方法完成也没有区别:

static void Main(string[] args){

    var uri = new Uri("ftp://192.168.1.2/test.txt");

    for (var z = 0; z < ServicePointManager.DefaultConnectionLimit + 1; z += 1){

        try {

            var request = (FtpWebRequest) WebRequest.Create(uri);
            request.Method = WebRequestMethods.Ftp.GetFileSize;

            var responseAsyncResult = request.BeginGetResponse(null, null);

            using (var response = request.EndGetResponse(responseAsyncResult))

                Console.WriteLine(response.ContentLength);

        } catch (WebException ex){

            Console.WriteLine(ex.Status);

        }

    }

}

将输出,

ConnectFailure
ConnectFailure

并挂起。

如果等待FtpWebRequest.GetResponseAsync(),则会发现相同的行为。

现在有趣的是:如果192.168.1.2 运行前面任何一个示例的机器的IP地址,或者192.168.1.2被环回地址替换,问题就会消失。

好的,让我们对前面的例子进行一些小修改:

static void Main(string[] args){

    var uri = new Uri("ftp://192.168.1.2/test.txt");

    for (var z = 0; z < ServicePointManager.DefaultConnectionLimit + 1; z += 1){

        try {

            var request = WebRequest.Create(uri);

            var responseAsyncResult = request.BeginGetResponse(null, null);

            using (var response = request.EndGetResponse(responseAsyncResult))

                Console.WriteLine(response.ContentLength);

        } catch (WebException ex){

            Console.WriteLine(ex.Status);

        }

    }

}

同样,它会像其他人一样失败,除非地址是本地/环回。

但是如果将uri方案改为http,那么一切都很好......

那么,这是一个错误还是我做错了什么?

0 个答案:

没有答案