diff --git a/src/Renci.SshNet/BaseClient.cs b/src/Renci.SshNet/BaseClient.cs
index db11a6178..6c721bebc 100644
--- a/src/Renci.SshNet/BaseClient.cs
+++ b/src/Renci.SshNet/BaseClient.cs
@@ -72,7 +72,7 @@ private set
/// if this client is connected; otherwise, .
///
/// The method was called after the client was disposed.
- public bool IsConnected
+ public virtual bool IsConnected
{
get
{
@@ -228,14 +228,19 @@ public void Connect()
// forwarded port with a client instead of with a session
//
// To be discussed with Oleg (or whoever is interested)
- if (IsSessionConnected())
+ if (IsConnected)
{
throw new InvalidOperationException("The client is already connected.");
}
OnConnecting();
- Session = CreateAndConnectSession();
+ // The session may already/still be connected here because e.g. in SftpClient, IsConnected also checks the internal SFTP session
+ var session = Session;
+ if (session is null || !session.IsConnected)
+ {
+ Session = CreateAndConnectSession();
+ }
try
{
@@ -287,14 +292,19 @@ public async Task ConnectAsync(CancellationToken cancellationToken)
// forwarded port with a client instead of with a session
//
// To be discussed with Oleg (or whoever is interested)
- if (IsSessionConnected())
+ if (IsConnected)
{
throw new InvalidOperationException("The client is already connected.");
}
OnConnecting();
- Session = await CreateAndConnectSessionAsync(cancellationToken).ConfigureAwait(false);
+ // The session may already/still be connected here because e.g. in SftpClient, IsConnected also checks the internal SFTP session
+ var session = Session;
+ if (session is null || !session.IsConnected)
+ {
+ Session = await CreateAndConnectSessionAsync(cancellationToken).ConfigureAwait(false);
+ }
try
{
diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs
index e9a73c484..16f15ada6 100644
--- a/src/Renci.SshNet/SftpClient.cs
+++ b/src/Renci.SshNet/SftpClient.cs
@@ -108,6 +108,22 @@ public uint BufferSize
}
}
+ ///
+ /// Gets a value indicating whether this client is connected to the server and
+ /// the SFTP session is open.
+ ///
+ ///
+ /// if this client is connected and the SFTP session is open; otherwise, .
+ ///
+ /// The method was called after the client was disposed.
+ public override bool IsConnected
+ {
+ get
+ {
+ return base.IsConnected && _sftpSession.IsOpen;
+ }
+ }
+
///
/// Gets remote working directory.
///
@@ -2473,7 +2489,15 @@ protected override void OnConnected()
{
base.OnConnected();
- _sftpSession = CreateAndConnectToSftpSession();
+ var sftpSession = _sftpSession;
+ if (sftpSession is null)
+ {
+ _sftpSession = CreateAndConnectToSftpSession();
+ }
+ else if (!sftpSession.IsOpen)
+ {
+ sftpSession.Connect();
+ }
}
///
diff --git a/test/Renci.SshNet.IntegrationTests/ConnectivityTests.cs b/test/Renci.SshNet.IntegrationTests/ConnectivityTests.cs
index 61ba9e424..a6ff3465c 100644
--- a/test/Renci.SshNet.IntegrationTests/ConnectivityTests.cs
+++ b/test/Renci.SshNet.IntegrationTests/ConnectivityTests.cs
@@ -287,6 +287,44 @@ public void Common_DetectLossOfNetworkConnectivityThroughSftpInvocation()
}
}
+ [TestMethod]
+ public void SftpClient_HandleSftpSessionClose()
+ {
+ using (var client = new SftpClient(_connectionInfoFactory.Create()))
+ {
+ client.Connect();
+ Assert.IsTrue(client.IsConnected);
+
+ client.SftpSession.Disconnect();
+ Assert.IsFalse(client.IsConnected);
+
+ client.Connect();
+ Assert.IsTrue(client.IsConnected);
+
+ client.Disconnect();
+ Assert.IsFalse(client.IsConnected);
+ }
+ }
+
+ [TestMethod]
+ public async Task SftpClient_HandleSftpSessionCloseAsync()
+ {
+ using (var client = new SftpClient(_connectionInfoFactory.Create()))
+ {
+ await client.ConnectAsync(CancellationToken.None);
+ Assert.IsTrue(client.IsConnected);
+
+ client.SftpSession.Disconnect();
+ Assert.IsFalse(client.IsConnected);
+
+ await client.ConnectAsync(CancellationToken.None);
+ Assert.IsTrue(client.IsConnected);
+
+ client.Disconnect();
+ Assert.IsFalse(client.IsConnected);
+ }
+ }
+
[TestMethod]
public void Common_DetectSessionKilledOnServer()
{