Rebex.Net.Sftp.ConnectAsync is not completely asynchronous?

0 votes
asked Nov 14, 2016 by shyronosov (160 points)

Does the "ConnectAsync" method that is in Rebex.Net.Sftp class work completely asynchronously? I've tried to limit thread pool maximum worker threads to 10 and started 10 parallel executions of "ConnectAsync" method. In this case all the threads were blocked in Rebex.Net.ProxySocket.IR method until the timeout occurred. However if I decrease parallel executions to 9, all of them are processed successfully within few seconds.

It's critical issue for us, any help will be appreciated!

Applies to: Rebex SFTP

1 Answer

+1 vote
answered Nov 15, 2016 by Lukas Pokorny (86,990 points)
selected Nov 15, 2016 by shyronosov
 
Best answer

Unfortunately, ConnectAsync and other *Async methods are not truly asynchronous. They are in fact equivalent to their synchronous variants.

We are aware of this limitation and we plan to do something about it in the future, most likely after we deprecate .NET 2.0/3.5 platforms, which would enable us to use await/async (that would help us tremendously to bring make codebase up-to-date).

But unfortunately, this is going to be a huge amount of work that might take months to complete. We are currently busy improving other areas (such as ECDSA and ECDH support in SSH and TLS) and can't devote any resources to this yet.


However, in this particular case, the issue is most likely caused by a "none proxy" code (basically an implementation of our Rebex.Net.ISocket interface on top of System.Net.Sockets.Socket) which creates an additional thread. And this might actually be quite simple to solve.

Additionally, you can even fix this yourself by implementing a custom transport layer using ISocketFactory/ISocket interfaces and instruct the Sftp object to use it instead of the built-in layer by calling SetSocketFactory method before connecting.

Sample implementation:

public class CustomSocket : ISocket
{
    private class CustomSocketFactory : ISocketFactory
    {
        public ISocket CreateSocket()
        {
            return new CustomSocket(this);
        }
    }

    private static readonly ISocketFactory _factory = new CustomSocketFactory();

    public static ISocketFactory Factory
    {
        get { return _factory; }
    }

    private readonly Socket _socket;

    public CustomSocket(ISocketFactory factory)
    {
        _socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
    }

    public bool Connected
    {
        get { return _socket.Connected; }
    }

    public EndPoint LocalEndPoint
    {
        get { return _socket.LocalEndPoint; }
    }

    public EndPoint RemoteEndPoint
    {
        get { return _socket.RemoteEndPoint; }
    }

    public int Timeout
    {
        get
        {
            return _socket.ReceiveTimeout;
        }
        set
        {
            _socket.ReceiveTimeout = value;
            _socket.SendTimeout = value;
        }
    }

    public void Close()
    {
        _socket.Close();
    }

    public void Connect(EndPoint remoteEP)
    {
        _socket.Connect(remoteEP);
    }

    public void Connect(string serverName, int serverPort)
    {
        _socket.Connect(serverName, serverPort);
    }

    public bool Poll(int microSeconds, SocketSelectMode mode)
    {
        return _socket.Poll(microSeconds, (SelectMode)mode);
    }

    public int Receive(byte[] buffer, int offset, int count, SocketFlags socketFlags)
    {
        return _socket.Receive(buffer, offset, count, socketFlags);
    }

    public int Send(byte[] buffer, int offset, int count, SocketFlags socketFlags)
    {
        return _socket.Send(buffer, offset, count, socketFlags);
    }

    public void Shutdown(SocketShutdown how)
    {
        _socket.Shutdown(how);
    }
}

Usage:

var sftp = new Sftp();
sftp.SetSocketFactory(CustomSocket.Factory);
sftp.Connect("test.rebex.net");
sftp.Login("demo", "password");
sftp.GetList();
sftp.Disconnect();
commented Nov 15, 2016 by shyronosov (160 points)
Thanks a lot for the information and implementation of CustomSocket. With this implementation when I call sftp.ConnectAsync, there are no calls of BeginXXX and EndXXX methods in CustomSocket, instead such methods as Connect, Poll, Send, Receive are called. Thus there is the same issue with blocked threads. Is there any possibility to make calls exactly to BeginXXX and EndXXX methods?
commented Nov 15, 2016 by Lukas Pokorny (86,990 points)
Unfortunately, we are unable to address this until we refactor the internals of our SFTP/SSH implementation as outlined in the beginning of my reply above. This would be a huge undertaking and I'm afraid we won't be able to devote our resources into this in the near term. We do plan to eventually address this limitation, but it's not yet in our near-term roadmap. Sorry!
...