我正在尝试使用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
答案 0 :(得分:1)
事实证明,FtpClient由于客户端的一些意外响应而重新连接,因为“陈旧数据”而触发了重新连接。我的解决方案是从FtpClient派生我自己的类,它使用问题中公布的DoInRetryLoop覆盖Connect()方法。
不幸的是,这只适用于EnableThreadSafeDataConnections = false或覆盖“CloneConnection”方法。后者要求我把它变成虚拟的。