Спорадический сбой 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". Последнее потребовало, чтобы я сделал это виртуальным.