System.Net.FtpClient.FileExists的偶发故障

时间:2016-11-07 10:48:20

标签: c# ftp net-ftp

我正在尝试使用System.Net.FtpClient,但事情没有按预期工作。

运行下面的代码,似乎即使IsConnected返回true,也会直接调用FileExists()调用Connect()(这意味着连接在调用之间完全丢失了?) 。但是,由于Connect()偶尔会失败,这也会导致失败FileExists()(失败意味着它会抛出Connection refused)。

我的代码有什么问题吗?这是预期的事情,即我是否应该准备重新尝试使用FtpClient实例做的所有事情?是否有任何标志设置为自动重试?

string myPath = ..;
string myTempPath = myPath+".tmp";

_client = GetClient(_ioc, false);
var _stream = _client.OpenWrite(myTempPath);

//write to stream

_stream.Close();
Android.Util.Log.Debug("NETFTP", "connected: " + _client.IsConnected.ToString()); //always outputs true

if (_client.FileExists(myPath) //sporadically throws, see below
    _client.DeleteFile(myPath);

其中GetClient()实现为使用我的自定义“retry-loop”来处理零星的连接失败。

private static T DoInRetryLoop<T>(Func<T> func)
{
    double timeout = 30.0;
    double timePerRequest = 1.0;
    var startTime = DateTime.Now;
    while (true)
    {
        var attemptStartTime = DateTime.Now;
        try
        {
            return func();
        }
        catch (System.Net.Sockets.SocketException e)
        {
            if ((e.ErrorCode != 10061) || (DateTime.Now > startTime.AddSeconds(timeout)))
            {
                throw;
            }
            double secondsSinceAttemptStart = (DateTime.Now - attemptStartTime).TotalSeconds;
            if (secondsSinceAttemptStart < timePerRequest)
            {
                Thread.Sleep(TimeSpan.FromSeconds(timePerRequest - secondsSinceAttemptStart));
            }
        }
    }       
}

internal FtpClient GetClient(IOConnectionInfo ioc)
{
    FtpClient client = new FtpClient();
    if ((ioc.UserName.Length > 0) || (ioc.Password.Length > 0))
        client.Credentials = new NetworkCredential(ioc.UserName, ioc.Password);
    else
        client.Credentials = new NetworkCredential("anonymous", ""); 

    Uri uri = IocPathToUri(ioc.Path);
    client.Host = uri.Host;
    if (!uri.IsDefaultPort)
        client.Port = uri.Port;
    client.EnableThreadSafeDataConnections = false;

    client.EncryptionMode = ConnectionSettings.FromIoc(ioc).EncryptionMode;

    Func<FtpClient> connect = () =>
    {
        client.Connect();
        return client;
    };
    return DoInRetryLoop(connect);

}

这是偶尔出现的例外情况:

System.Net.Sockets.SocketException : Connection refused
10-24 13:08:07.487 I/mono-stdout(24073):          at System.Net.Sockets.SocketAsyncResult.CheckIfThrowDelayedException () [0x00017] in /Users/builder/data/lanes/3540/1cf254db/source/mono/mcs/class/System/System.Net.Sockets/SocketAsyncResult.cs:127 
          at System.Net.Sockets.SocketAsyncResult.CheckIfThrowDelayedException () [0x00017] in /Users/builder/data/lanes/3540/1cf254db/source/mono/mcs/class/System/System.Net.Sockets/SocketAsyncResult.cs:127 
10-24 13:08:07.487 I/mono-stdout(24073):          at System.Net.Sockets.Socket.EndConnect (IAsyncResult result) [0x0002f] in /Users/builder/data/lanes/3540/1cf254db/source/mono/mcs/class/System/System.Net.Sockets/Socket.cs:1593 
          at System.Net.Sockets.Socket.EndConnect (IAsyncResult result) [0x0002f] in /Users/builder/data/lanes/3540/1cf254db/source/mono/mcs/class/System/System.Net.Sockets/Socket.cs:1593 
          at System.Net.FtpClient.FtpSocketStream.Connect (System.String host, Int32 port, FtpIpVersion ipVersions) [0x0011a] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpSocketStream.cs:611 
10-24 13:08:07.487 I/mono-stdout(24073):          at System.Net.FtpClient.FtpSocketStream.Connect (System.String host, Int32 port, FtpIpVersion ipVersions) [0x0011a] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpSocketStream.cs:611 
10-24 13:08:07.487 I/mono-stdout(24073):          at (wrapper remoting-invoke-with-check) System.Net.FtpClient.FtpSocketStream:Connect (string,int,System.Net.FtpClient.FtpIpVersion)
          at (wrapper remoting-invoke-with-check) System.Net.FtpClient.FtpSocketStream:Connect (string,int,System.Net.FtpClient.FtpIpVersion)
10-24 13:08:07.487 I/mono-stdout(24073):          at System.Net.FtpClient.FtpClient.Connect () [0x000ce] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpClient.cs:807 
          at System.Net.FtpClient.FtpClient.Connect () [0x000ce] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpClient.cs:807 
          at System.Net.FtpClient.FtpClient.Execute (System.String command) [0x00136] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpClient.cs:735 
10-24 13:08:07.487 I/mono-stdout(24073):          at System.Net.FtpClient.FtpClient.Execute (System.String command) [0x00136] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpClient.cs:735 
10-24 13:08:07.487 I/mono-stdout(24073):          at System.Net.FtpClient.FtpClient.Execute (System.String command, System.Object[] args) [0x00001] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpClient.cs:694 
          at System.Net.FtpClient.FtpClient.Execute (System.String command, System.Object[] args) [0x00001] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpClient.cs:694 
10-24 13:08:07.487 I/mono-stdout(24073):          at System.Net.FtpClient.FtpClient.DirectoryExists (System.String path) [0x0005d] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpClient.cs:2679 
          at System.Net.FtpClient.FtpClient.DirectoryExists (System.String path) [0x0005d] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpClient.cs:2679 
10-24 13:08:07.487 I/mono-stdout(24073):          at System.Net.FtpClient.FtpClient.FileExists (System.String path, FtpListOption options) [0x0001c] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpClient.cs:2751 
10-24 13:08:07.487 I/mono-stdout(24073):          at System.Net.FtpClient.FtpClient.FileExists (System.String path) [0x00001] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpClient.cs:2733 
          at System.Net.FtpClient.FtpClient.FileExists (System.String path, FtpListOption options) [0x0001c] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpClient.cs:2751 
          at System.Net.FtpClient.FtpClient.FileExists (System.String path) [0x00001] in [my source folder]src
etftpandroid\System.Net.FtpClient\FtpClient.cs:2733 

1 个答案:

答案 0 :(得分:1)

事实证明,FtpClient由于客户端的一些意外响应而重新连接,因为“陈旧数据”而触发了重新连接。我的解决方案是从FtpClient派生我自己的类,它使用问题中公布的DoInRetryLoop覆盖Connect()方法。

不幸的是,这只适用于EnableThreadSafeDataConnections = false或覆盖“CloneConnection”方法。后者要求我把它变成虚拟的。