Description
I'm using one SftpClient object, simultaneously on two different threads. While the primary thread is doing a large upload (UploadFile()
), I have a background task making sure we're still connected by doing ListDirectory("/")
every few seconds. When I do that, the SftpClient disconnects.
I tested this using two different SSH servers: My primary target is a small embedded device, running Dropbear SSH server. On that server, the disconnect usually happens on the very first call to ListDirectory. For testing, I also tried a server running OpenSSH on Rocky Linux 8. Using that, the call to ListDirectory generally succeeds several times, but it eventually fails as well.
I am using SSH.NET version 2024.2.0 on .Net 9.
Here is a self-contained demonstration program.
using Renci.SshNet;
namespace SshMultiUseTest;
internal class Program
{
#if false
// This is my small embedded device. Low power. Running PetaLinux on a Xilinx SoC.
// SSH Server is Dropbear v2020.80. (No, I can't change that.)
// (Why 127.0.0.1? I'm on a work computer, with a strict VPN. I have to use an SSH tunnel
// to make connections to machines on the local network.)
const string host = "127.0.0.1";
const int port = 2122;
const string user = "root";
const string privKey = "DeviceKey.id_rsa";
#else
// This is my Linux server. It's a normal desktop PC, maybe 10 years old. Intel i7 of some sort.
// Running Rocky Linux 8. SSH Server is OpenSSH 8.0p1.
const string host = "127.0.0.1";
const int port = 2200;
const string user = "dyaw";
const string privKey = "id_rsa";
#endif
const string largeFile = "C:\\Data\\deviceUpgrade.mfuf";
static void Main(string[] args)
{
SftpClient sftpClient = new(
host, port, user,
new PrivateKeyFile(typeof(Program).Assembly.GetManifestResourceStream(typeof(Program), privKey)!));
sftpClient.Connect();
// Verify connectivity
foreach (var item in sftpClient.ListDirectory("/")) { }
using FileStream fs = new(largeFile, FileMode.Open, FileAccess.Read);
int count = 0;
void callback(ulong uploaded)
{
int newCount = Interlocked.Increment(ref count);
if (newCount < 20 || newCount % 100 == 0)
Console.WriteLine("Uploaded: {0:N0}", uploaded);
}
Task largeUploadTask = Task.Run(() => sftpClient.UploadFile(fs, "/home/" + user + "/uploadTest", callback));
int exceptionCount = 0;
while (!largeUploadTask.IsCompleted)
{
Thread.Sleep(1000);
try
{
foreach (var item in sftpClient.ListDirectory("/")) { }
Console.WriteLine("Small Task: Directory Listing complete");
exceptionCount = 0;
}
catch (Exception e)
{
if (++exceptionCount < 5)
{
// These get repetitive after the first couple. Be quiet.
Console.WriteLine("From small task: {0}: {1}", e.GetType().FullName, e.Message);
}
}
}
try
{
largeUploadTask.Wait();
}
catch (Exception e)
{
if (e is AggregateException && e.InnerException != null)
e = e.InnerException;
Console.WriteLine("From large task: {0}: {1}", e.GetType().FullName, e.Message);
}
}
}
Result, connecting to the embedded device:
Uploaded: 65,460
Uploaded: 32,730
Uploaded: 98,190
Uploaded: 130,920
Uploaded: 163,650
Uploaded: 196,380
Uploaded: 229,110
Uploaded: 261,840
Uploaded: 294,570
Uploaded: 327,300
Uploaded: 360,030
Uploaded: 392,760
Uploaded: 425,490
Uploaded: 458,220
Uploaded: 490,950
Uploaded: 523,680
Uploaded: 556,410
Uploaded: 589,140
Uploaded: 621,870
Uploaded: 3,273,000
Uploaded: 6,546,000
From small task: Renci.SshNet.Common.SshException: Channel was closed.
From small task: System.InvalidOperationException: The session is not open.
From small task: System.InvalidOperationException: The session is not open.
From small task: System.InvalidOperationException: The session is not open.
(long pause here)
From large task: Renci.SshNet.Common.SshOperationTimeoutException: Session operation has timed out
Result, connecting to my regular server:
Uploaded: 65,478
Uploaded: 32,739
Uploaded: 98,217
Uploaded: 130,956
Uploaded: 163,695
Uploaded: 196,434
Uploaded: 229,173
Uploaded: 261,912
Uploaded: 294,651
Uploaded: 327,390
Uploaded: 360,129
Uploaded: 392,868
Uploaded: 425,607
Uploaded: 458,346
Uploaded: 523,824
Uploaded: 556,563
Uploaded: 491,085
Uploaded: 589,302
Uploaded: 622,041
Uploaded: 3,273,900
Uploaded: 6,547,800
Uploaded: 9,821,700
Uploaded: 13,095,600
Uploaded: 16,369,500
Uploaded: 19,643,400
Uploaded: 22,917,300
Uploaded: 26,191,200
Uploaded: 29,465,100
Uploaded: 32,739,000
Uploaded: 36,012,900
Uploaded: 39,286,800
Uploaded: 42,560,700
Uploaded: 45,834,600
Uploaded: 49,108,500
Uploaded: 52,382,400
Uploaded: 55,656,300
Small Task: Directory Listing complete
Uploaded: 58,930,200
Uploaded: 62,204,100
Uploaded: 65,478,000
Uploaded: 68,751,900
Uploaded: 72,025,800
Uploaded: 75,299,700
Uploaded: 78,573,600
Uploaded: 81,847,500
Uploaded: 85,121,400
Uploaded: 88,395,300
Uploaded: 91,669,200
Uploaded: 94,943,100
Uploaded: 98,217,000
Uploaded: 101,490,900
Uploaded: 104,764,800
Uploaded: 108,038,700
Uploaded: 111,312,600
Uploaded: 114,586,500
Uploaded: 117,860,400
Uploaded: 121,134,300
Uploaded: 124,408,200
Uploaded: 127,682,100
Uploaded: 130,956,000
Uploaded: 134,229,900
Uploaded: 137,503,800
Uploaded: 140,777,700
Uploaded: 144,051,600
Uploaded: 147,325,500
Uploaded: 150,599,400
Uploaded: 153,873,300
Uploaded: 157,147,200
Uploaded: 160,421,100
Uploaded: 163,695,000
Uploaded: 166,968,900
Uploaded: 170,242,800
Uploaded: 173,516,700
Uploaded: 176,790,600
Uploaded: 180,064,500
Small Task: Directory Listing complete
Uploaded: 183,338,400
Uploaded: 186,612,300
Uploaded: 189,886,200
Uploaded: 193,160,100
Uploaded: 196,434,000
Uploaded: 199,707,900
Uploaded: 202,981,800
Uploaded: 206,255,700
Uploaded: 209,529,600
Uploaded: 212,803,500
Uploaded: 216,077,400
Uploaded: 219,351,300
Uploaded: 222,625,200
Uploaded: 225,899,100
Uploaded: 229,173,000
Uploaded: 232,446,900
Uploaded: 235,720,800
Uploaded: 238,994,700
Uploaded: 242,268,600
Uploaded: 245,542,500
Uploaded: 248,816,400
Uploaded: 252,090,300
Uploaded: 255,364,200
Uploaded: 258,638,100
Uploaded: 261,912,000
Uploaded: 265,185,900
Uploaded: 268,459,800
Uploaded: 271,733,700
Small Task: Directory Listing complete
Uploaded: 275,007,600
Uploaded: 278,281,500
Uploaded: 281,555,400
Uploaded: 284,829,300
Uploaded: 288,103,200
Uploaded: 291,377,100
Uploaded: 294,651,000
Uploaded: 297,924,900
Uploaded: 301,198,800
Uploaded: 304,472,700
Uploaded: 307,746,600
Uploaded: 311,020,500
Uploaded: 314,294,400
Uploaded: 317,568,300
Uploaded: 320,842,200
Uploaded: 324,116,100
Uploaded: 327,390,000
Uploaded: 330,663,900
Uploaded: 333,937,800
Uploaded: 337,211,700
Uploaded: 340,485,600
From small task: Renci.SshNet.Common.SshException: Channel was closed.
From small task: System.InvalidOperationException: The session is not open.
From small task: System.InvalidOperationException: The session is not open.
From small task: System.InvalidOperationException: The session is not open.
(long pause here)
From large task: Renci.SshNet.Common.SshOperationTimeoutException: Session operation has timed out