Ftp concurrent connections

Hi,

I have an application that is attempting to download many small files from my ftp server. The program runs very quickly then stops and waits for a few minutes and runs very quickly again. This continues and all the files are downloaded, however I have found via a trace on an intermediate box that the application opens many connections, then reaches a Concurrent Connections Quota Exceeded error, then they all close and it starts again opening many connections.

Some of my code is bellow, is this the correct method of downloading a lot of files?

ftpC.Host = _server;

ftpC.Port = _port;

ftpC.DataConnectionMode = DataConnectionMode.Auto;

if (_password != String.Empty)

ftpC.Password = _password;

if (_username != String.Empty)

ftpC.Username = _username;

ftpC.Connect(true);

ftpC.TransferProgress += new TransferProgressEventHandler(downloadProgress);

while (_q.Count > 0)

{

try

{

ftpC.Download(item.RemoteFile(), item.LocalFile(), true);

}

catch (Exception)

{

}

finally

{

ftpC.Disconnect();

}

Thanks,

Hi,

Thanks for considering Aspose.

Could you please check whether the ftp is already connected before creating the connection as follows:

if (ftpC== null || !ftpC.Connected)

ftpC.Connect(true);

I will check, however the code listed in my original post is part of a thread, so it gets called once in my tests, connects and then loops through the while loop popping items (files to download) off the queue (_q) until the queue is empty.

It should only call the connect once;

Regards,

With a little more investigation it looks like, when I attempt to download lots of little files a new connection is established for each file on a different port, but after the files has been downloaded the port stays open until (I assume) it times out, usually around a minute.

This appears to be causing problems with our firewall which has flood mitigation built into it. It allows 600 connections per IP to be open concurrently but I appear to be exceeding this limit within around 40 seconds.

I have added some code in an attempt to close the dataConnection when the file has been listed as completed, but it has had no effect. I have also tried the active mode connection which also suffers the same problem.

Thanks,

Thanks for the investigation.

We are working on it to find some solutions for you. I will keep you posted.

Cheers

Hi iret,

Do you have any updates as to your progress?

Thanks,

Hello,

We are still doing the troubleshotting.

Could you provide me more code about how the threads calling the ftpclient? The ftpclient instance is shared among the threads or each thread owns a private ftp copy?

It could be greate appreciated, if you can provide some code that we can run and reproduce your isse.

Thanks

The main section of code is:

for (int i = 0; i < _threads; i++)
{
ftpDownloadWorker d = new ftpDownloadWorker(ref workingQueue, _ftpServer, _ftpPort, _ftpUsername, _ftpPassword, _root);
d.downloading += new TransferCompletedEventHandler(downloadWorkerWorking);
Thread thread = new Thread(new ThreadStart(d.download));
thread.Priority = ThreadPriority.Normal;
thread.IsBackground = false;
thread.Start();
}

The download worker is:

private class ftpDownloadWorker
{
private string _username = "";
private string _password = "";
private string _server = "";
private int _port = 21;
private ArrayList _q;
private WorkItem item;

public ftpDownloadWorker(ref ArrayList q, string server, int port, string username, string password, string
SAMSRoot)
{
_username = username;
_password = password;
_server = server;
_port = port;
_q = q;
}

public void download()
{

FtpClient ftpC = new FtpClient();

int threadID = Thread.CurrentThread.ManagedThreadId;

ftpC.Host = _server;
ftpC.Port = _port;
ftpC.DataConnectionMode = DataConnectionMode.Auto;
ftpC.DataReceiveTimeout = 15;

if (_password != String.Empty)
ftpC.Password = _password;
if (_username != String.Empty)
ftpC.Username = _username;

ftpC.Connect(true);

while (_q.Count > 0)
{

lock (_q)
{
if (_q.Count == 0)
{
ftpC.Disconnect();
return;
}

item = (WorkItem)_q[0];

_q.RemoveAt(0);
}

try
{
ftpC.Download(item.RemoteFile, item.LocalFile, true);
}
catch (FtpException ex)
{
if (item.retryEvents >= 15)
throw new Exception("Maximum retry event has exceeded the stated maximum");

lock (_q)
{
item.retryEvents++;

_q.Insert(0,item);
}

item = null;
}

if (item != null)
{
item = null;
}
}
ftpC.Disconnect();
}

I have stripped out quite a lot of the code that handles other processes etc, if you need more I can post the entire contents of the files.

Most of the files I am downloading are images that are roughly 4KB in size, and we are hosting them from a FileZilla Server. Inbetween there is an MS ISA 2006 server which has flood control and limits each IP to 600 concurrent connections. I am usually running the program with 2 threads but I have tried 4.

Regards,

Hello,

Could you please try to use the attched dll and the Passive mode? I have made some changes on the sockets reuse.

Moreover, I think it is better to control the concurrency in the download. Because the sockets need some time to shutdown properly.

Hi iret,

I have tried the fix without changing the code and I still get the same problem.

"FWX_E_CONCURRENT_CONNECTIONS_QUOTA_EXCEEDED_DROPPED"

Could you provide an example as to the better method of concurency?

Thanks,

Thanks for the update.

I think we need to control the concurrency. If the running threads start the downloading to frequently, the socket may not have enough time to shutdown.

Coud you have a shared parameter to control the downloading? Like you can use a Semaphore to control the downloading. If it is greater than a threshold, wait for a few minutes.

iret,

How would I know that the socket has closed gracefully in order to reduce the semaphore? I am already hooking into the TransferCompletedEvent but it appears to fire when the file has been downloaded but around 60 seconds before the socket used is closed.

We are also faced with the fact that if we control the downloads we will download the first 300 files very very quickly and then have a stutter, while we wait for the sockets to close in order to fire the next 300 files.

Regards

Hello,

Could you please give it another try? I changed some settings of the socket hopefully it will shutdown quickly.

Let me know if it works.

In addition, do you think you can add some latency into your code logic? For example, each time you find the ftp download errors, stop a few minutes and then re-try?

The first time you find the ftp error, wait for 1 sec, the second time error wait for 2 times of previous wait time. When the waiting time exceeds the maximum threshold, like 30 sec, report errors and throw exception.

Any idea?

iret,

Thanks for your help! It went a little better this time, there were no errors until the main thread crashed with this error:

Unhandled Exception: Unhandled Exception: System.Net.WebException: The remote se
rver returned an error: 227 Entering Passive Mode (83,100,x,y,111,83)
. ---> System.Net.Sockets.SocketException: A connection attempt failed because t
he connected party did not properly respond after a period of time, or establish
ed connection failed because connected host has failed to respond 83.100.x.y
:28499
at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddre
ss socketAddress)
at System.Net.Sockets.Socket.Connect(EndPoint remoteEP)
at System.Net.FtpControlStream.QueueOrCreateDataConection(PipelineEntry entry
, ResponseDescription response, Boolean timeout, Stream& stream, Boolean& isSock
etReady)
at System.Net.FtpControlStream.PipelineCallback(PipelineEntry entry, Response
Description response, Boolean timeout, Stream& stream)
at System.Net.CommandStream.PostReadCommandProcessing(Stream& stream)
at System.Net.CommandStream.PostSendCommandProcessing(Stream& stream)
at System.Net.CommandStream.ContinueCommandPipeline()
at System.Net.CommandStream.SubmitRequest(WebRequest request, Boolean async,
Boolean readInitalResponseOnConnect)
at System.Net.FtpWebRequest.TimedSubmitRequestHelper(Boolean async)
at System.Net.FtpWebRequest.SubmitRequest(Boolean async)
--- End of inner exception stack trace ---
at System.Net.WebClient.DownloadFile(Uri address, String fileName)
at System.Net.WebClient.DownloadFile(String address, String fileName)
at UpdateSystem.Client.ftpDownloadWorker.download()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C
ontextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()System.Net.WebException: The re
mote server returned an error: 227 Entering Passive Mode (83,100,x,y,111,86)

. ---> System.Net.Sockets.SocketException: A connection attempt failed because t
he connected party did not properly respond after a period of time, or establish
ed connection failed because connected host has failed to respond 83.100.x.y
:28502
at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddre
ss socketAddress)
at System.Net.Sockets.Socket.Connect(EndPoint remoteEP)
at System.Net.FtpControlStream.QueueOrCreateDataConection(PipelineEntry entry
, ResponseDescription response, Boolean timeout, Stream& stream, Boolean& isSock
etReady)
at System.Net.FtpControlStream.PipelineCallback(PipelineEntry entry, Response
Description response, Boolean timeout, Stream& stream)
at System.Net.CommandStream.PostReadCommandProcessing(Stream& stream)
at System.Net.CommandStream.PostSendCommandProcessing(Stream& stream)
at System.Net.CommandStream.ContinueCommandPipeline()
at System.Net.CommandStream.SubmitRequest(WebRequest request, Boolean async,
Boolean readInitalResponseOnConnect)
at System.Net.FtpWebRequest.TimedSubmitRequestHelper(Boolean async)
at System.Net.FtpWebRequest.SubmitRequest(Boolean async)
--- End of inner exception stack trace ---
at System.Net.WebClient.DownloadFile(Uri address, String fileName)
at System.Net.WebClient.DownloadFile(String address, String fileName)
at UpdateSystem.Client.ftpDownloadWorker.download()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C
ontextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

I'm going to add some code to retry after getting this error as you suggest, but in the mean time you may be interested in our firewall log files that illustrates the problem.