sshnet/SSH.NET

SFTP: "client not connected" after server closed connection

Closed this issue · 4 comments

As reported by multiple people here #843 (comment) , #1362 does not completely fix the general issue. A "client not connected" exception can still happen after checking for IsConnected .

I will try to reproduce this and create a PR.

This issue happened only if I didn't set the KeepAliveInternal. Figured it might be important for your tests or for someone trying to avoid the issue.

As mention by Rob-Hague in #843 (comment) , this can't really be fixed. IsConnected just can't be relied upon because you have to actually send data to notice that the connection is gone.

Even if the check that throws the exception would be removed, the sending itself would fail. The only way to avoid this, is to configure a keep alive on the client or the server.

Reopening this. We have a case where the keep alive doesn't help. Affected server is PSFTPd .

I think there is at least one issue in SSH.NET: if the "Client not connected" exception happens, I would expect IsConnected to be false after this, but it seems like it stays true so another OpenAsync fails again with Client not connected..

Idea why the keep alive doesn't help: maybe the SFTP subsession needs its own keep-alive? Or maybe the server is just not reacting to keep alives and closing the session either way....

fyi @Rob-Hague

Renci.SshNet.Common.SshConnectionException: Client not connected.
   at Renci.SshNet.Session.SendMessage(Message message)
   at Renci.SshNet.Session.Renci.SshNet.ISession.SendMessage(Message message)
   at Renci.SshNet.Channels.Channel.SendData(Byte[] data, Int32 offset, Int32 size)
   at Renci.SshNet.Channels.Channel.SendData(Byte[] data)
   at Renci.SshNet.SubsystemSession.SendData(Byte[] data)
   at Renci.SshNet.Sftp.SftpSession.SendMessage(SftpMessage sftpMessage)
   at Renci.SshNet.Sftp.SftpSession.SendRequest(SftpRequest request)
   at Renci.SshNet.Sftp.SftpSession.RequestOpenAsync(String path, Flags flags, CancellationToken cancellationToken)
   at Renci.SshNet.Sftp.SftpSession.RequestOpenAsync(String path, Flags flags, CancellationToken cancellationToken)
   at Renci.SshNet.Sftp.SftpFileStream.OpenAsync(ISftpSession session, String path, FileMode mode, FileAccess access, Int32 bufferSize, CancellationToken cancellationToken)

There also seems be a related issue where the client can get into a broken state. Even after re-connect with ConnectAsync, any following OpenAsync keeps throwing the exception from the old session. It seems like Session.Reset() is not called in some cases (which would reset the exception handle).

We currently work around both issues by explicitly disconnecting before doing a reconnect. This seems to work (for now).

Renci.SshNet.Common.SshConnectionException: An established connection was aborted by the server.
   at Renci.SshNet.Session.WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout)
   at Renci.SshNet.Session.Renci.SshNet.ISession.WaitOnHandle(WaitHandle waitHandle)
   at Renci.SshNet.Channels.Channel.GetDataLengthThatCanBeSentInMessage(Int32 messageLength)
   at Renci.SshNet.Sftp.SftpSession.SendRequest(SftpRequest request)
   at Renci.SshNet.Sftp.SftpSession.RequestOpenAsync(String path, Flags flags, CancellationToken cancellationToken)
   at Renci.SshNet.Sftp.SftpSession.RequestOpenAsync(String path, Flags flags, CancellationToken cancellationToken)
   at Renci.SshNet.Sftp.SftpFileStream.OpenAsync(ISftpSession session, String path, FileMode mode, FileAccess access, Int32 bufferSize, CancellationToken cancellationToken)

Also worth mentioning that when the server disconnects the session, we do get an ErrorOccurred event with this exception (as expected).