Post a reply

Options
Add an Attachment

If you do not want to add an Attachment to your Post, please leave the Fields blank.

(maximum 10 MB; please compress large files; only common media, archive, text and programming file formats are allowed)

Options

Topic review

martin

Re: Thank you

Anonymous wrote:

Can you confirm that is safe using a session object in a different thread?

The Session class is thread safe (will block parallel accesses).
Guest

Thank you

Thank you for your feedback.

So i'm going to use a Session with a long life span.
Can you confirm that is safe using a session object in a different thread?
I'm looking for the best stability.
martin

Re: Best way to use Session object in critical Windows services

It looks good to me.
KillboyPowerhead

Best way to use Session object in critical Windows services

Hi,

i'd like to have some guidelines regarding Session object usage in critical Windows services.
Our service runs every 30 mins and have to upload\download some remote files in the most stable way possible. The service must not leak memory nor experiment connection floods or hangs (in particular the latest is a common issue in SFTP\FTP libraries, this one too, and i totally need to avoid it). What's the best way to use WinSCP in order to avoid issues and experience the best stability?

My idea was to create a unique Session object that lives\dies for each single service spin (so every 30 minutes) and does each upload\download in a "safe" method that each time checks connection state and eventually disconnect\recreate\reconnect the instance in case of connection issues. In this way the client connects to remote host fewer times, reducing the risk of connection problems (hang up in particular). This is an example of the Upload() method i've implemented:

private SessionOptions _sessionOptions; 

private Session _client;

private Upload(Dictionary<string, string> fileList) {
   
    foreach(var file in fileList) {
        SafeExec(() => {
            _client.PutFiles(file.Key, file.Value);
        });
    }
}


private void SafeExec(Action action)
{
   
        // Check client state
        if (_client == null || !_client.Opened)
        {

            // If client connection is not open, explicitly close it and dispose the instance
            if (this._client != null)
            {
                this._client.Close();
                this._client.Failed -= _client_Failed;
                this._client.Dispose();
                this._client = null;
            }

            // Recreate instance
            var sessionOptions = new SessionOptions
            {
                Protocol = Protocol.Sftp,
                HostName = this.host,
                UserName = this.username,
                Password = this.password,
                PortNumber = this.port,
                GiveUpSecurityAndAcceptAnySshHostKey = true
            };
            this._client = new Session();
            this._client.ExecutablePath = Path.Combine(GetExecutionFilePath(), "WinSCP.exe"); // set WinSCP.exe path
            this._client.SessionLogPath = Path.Combine(GetExecutionFilePath(), "Log\\WinScp.log"); // set session log for issue tracing
            this._client.Failed += _client_Failed; // Intercept event for internal logging

            // Open instance
            _client.Open(this._sessionOptions);
        }


        // If client is open, execute my delegate
        if (_client.Opened)
            action.Invoke();
        else
            throw new SafeExecClientDisconnected();
}


Is it a stable approach?
Or should i create a Session for each file that i have to upload\download like this pseudocode?

private Upload(Dictionary<string, string> fileList) {

    foreach(var file in fileList) {
       using(var session = new Session(...)) {
          session.Open(...);
          _client.PutFiles(file.Key, file.Value);
       }
    }
}
 


Another option to avoid freezing issues would be to surround SafeExec() in a thread with a long timeout. If the socket get stuck it won't lock the main thread and the service can continue. In order to do this, the Session object needs to be thread safe and each Session instance must not be affected by other inconsistent or stuck Session objects (so Session objects doens't have to have static shared state variables). Is it a valid way in your opinion?

private SessionOptions _sessionOptions; 

private Session _client;

private Upload(Dictionary<string, string> fileList) {
   
    foreach(var file in fileList) {
        AsyncSafeExec(() => {
            _client.PutFiles(file.Key, file.Value);
        });
    }
}
private void AsyncSafeExec(Action action) {

   var task = Task.Factory.StartNew(() => SafeExec(action));
   try {
      if(!task.Wait(3600000)) throw new Exception(); // 1 hour timeout
   } catch {
      // Ignores exceptions or timeouts
       _client = null; // Forces client creation for the next call
    }
}


Have you got some ideas to point me on the right way?

Thank you very much.