Спорадический сбой System.Net.FtpClient.FileExists

Я пытаюсь перейти на использование 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() реализован как использование моего пользовательского "цикла повторения" для устранения спорадических сбоев подключения.

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 ответ

Оказалось, что FtpClient переподключался из-за неожиданного ответа от клиента, который вызвал переподключение из-за "устаревших данных". Моим решением было извлечь мой собственный класс из FtpClient, который переопределяет метод Connect() с использованием DoInRetryLoop, как указано в вопросе.

К сожалению, это работает только с EnableThreadSafeDataConnections = false или с переопределением метода "CloneConnection". Последнее потребовало, чтобы я сделал это виртуальным.

Другие вопросы по тегам