SCPClient hangs (downloading) when connections problem occurred on Version: 2013.4.7
Simple way of reproduction:
1. Remove scp (/var/bin/scp) from remote mashine.
2. Try to download some file from this mashine.
3. Downloading hungs...
I think, there is some problem with PipeStream interaction (PipeStream has no reaction on disconnect or other connection problems).
My Code
```
_scp = new Renci.SshNet.ScpClient(_host, _login, _password);
_scp.Connect();
_scp.Download(filename, file);
```
First hangs thread:
> Renci.SshNet.dll!Renci.SshNet.Common.PipeStream.Read(byte[] buffer, int offset, int count) Line 181 + 0xb bytes C#
mscorlib.dll!System.IO.Stream.ReadByte() + 0x28 bytes
Renci.SshNet.dll!Renci.SshNet.ScpClient.ReadByte(System.IO.Stream stream) Line 367 + 0xb bytes C#
Renci.SshNet.dll!Renci.SshNet.ScpClient.ReadString(System.IO.Stream stream) Line 384 + 0x8 bytes C#
Renci.SshNet.dll!Renci.SshNet.ScpClient.InternalDownload(Renci.SshNet.Channels.ChannelSession channel, System.IO.Stream input, System.IO.FileSystemInfo fileSystemInfo) Line 217 + 0x8 bytes C#
Renci.SshNet.dll!Renci.SshNet.ScpClient.Download(string filename, System.IO.FileInfo fileInfo) Line 132 + 0x14 bytes C#
```
Renci.SshNet.dll!Renci.SshNet.Common.PipeStream.Read
...
while (!this.ReadAvailable(count))
Monitor.Wait(this._buffer); // (will hungs infinite due to disconnection state - no one will write in PipeStream)
```
Second hangs thread:
> System.dll!System.Net.Sockets.Socket.Receive(byte[] buffer, int offset, int size, System.Net.Sockets.SocketFlags socketFlags, out System.Net.Sockets.SocketError errorCode) + 0xc6 bytes
System.dll!System.Net.Sockets.Socket.Receive(byte[] buffer, int offset, int size, System.Net.Sockets.SocketFlags socketFlags) + 0x20 bytes
Renci.SshNet.dll!Renci.SshNet.Session.SocketRead(int length, ref byte[] buffer) Line 115 + 0x34 bytes C#
Renci.SshNet.dll!Renci.SshNet.Session.Read(int length) Line 1500 + 0xf bytes C#
Renci.SshNet.dll!Renci.SshNet.Session.ReceiveMessage() Line 805 + 0xb bytes C#
Renci.SshNet.dll!Renci.SshNet.Session.MessageListener() Line 1582 + 0x8 bytes C#
Renci.SshNet.dll!Renci.SshNet.Session.Connect.AnonymousMethod__4() Line 529 + 0x8 bytes C#
Renci.SshNet.dll!Renci.SshNet.Session.ExecuteThread.AnonymousMethod__3c(object o) Line 25 + 0xf bytes C#
mscorlib.dll!System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(object state) + 0x2d bytes
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool ignoreSyncCtx) + 0xb0 bytes
mscorlib.dll!System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() + 0x5a bytes
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() + 0x147 bytes
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() + 0x2d bytes
[Native to Managed Transition]
```
partial void SocketRead(int length, ref byte[] buffer)
{
var offset = 0;
int receivedTotal = 0; // how many bytes is already received
do
{
try
{
var receivedBytes = this._socket.Receive(buffer, offset + receivedTotal, length - receivedTotal, SocketFlags.None); // hungs here
if (receivedBytes > 0)
```
------- output ----------------
SshNet.Logging Verbose: 1 : Initiating connect to '.............'.
SshNet.Logging Verbose: 1 : Server version '2.0' on 'OpenSSH_5.9p1 Debian-5ubuntu1.1'.
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'KeyExchangeInitMessage': 'SSH_MSG_KEXINIT'.
'TestDownload.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Dynamic\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Dynamic.dll'
'TestDownload.vshost.exe' (Managed (v4.0.30319)): Loaded 'Anonymously Hosted DynamicMethods Assembly'
SshNet.Logging Verbose: 1 : SendMessage to server 'KeyExchangeInitMessage': 'SSH_MSG_KEXINIT'.
SshNet.Logging Verbose: 1 : SendMessage to server 'KeyExchangeDhGroupExchangeRequest': 'SSH_MSG_KEX_DH_GEX_REQUEST'.
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'KeyExchangeDhGroupExchangeGroup': 'SSH_MSG_KEX_DH_GEX_GROUP'.
SshNet.Logging Verbose: 1 : SendMessage to server 'KeyExchangeDhGroupExchangeInit': 'SSH_MSG_KEX_DH_GEX_INIT'.
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'KeyExchangeDhGroupExchangeReply': 'SSH_MSG_KEX_DH_GEX_REPLY'.
SshNet.Logging Verbose: 1 : SendMessage to server 'NewKeysMessage': 'SSH_MSG_NEWKEYS'.
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'NewKeysMessage': 'SSH_MSG_NEWKEYS'.
SshNet.Logging Verbose: 1 : SendMessage to server 'ServiceRequestMessage': 'SSH_MSG_SERVICE_REQUEST'.
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'ServiceAcceptMessage': 'SSH_MSG_SERVICE_ACCEPT'.
SshNet.Logging Verbose: 1 : SendMessage to server 'RequestMessageNone': 'SSH_MSG_USERAUTH_REQUEST'.
The thread '<No Name>' (0x724) has exited with code 0 (0x0).
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'FailureMessage': 'SSH_MSG_USERAUTH_FAILURE'.
SshNet.Logging Verbose: 1 : SendMessage to server 'RequestMessagePassword': 'SSH_MSG_USERAUTH_REQUEST'.
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'SuccessMessage': 'SSH_MSG_USERAUTH_SUCCESS'.
SshNet.Logging Verbose: 1 : SendMessage to server 'ChannelOpenMessage': 'SSH_MSG_CHANNEL_OPEN : #0'.
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'ChannelOpenConfirmationMessage': 'SSH_MSG_CHANNEL_OPEN_CONFIRMATION : #0'.
SshNet.Logging Verbose: 1 : SendMessage to server 'ChannelRequestMessage': 'SSH_MSG_CHANNEL_REQUEST : #0'.
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'ChannelWindowAdjustMessage': 'SSH_MSG_CHANNEL_WINDOW_ADJUST : #0'.
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'ChannelSuccessMessage': 'SSH_MSG_CHANNEL_SUCCESS : #0'.
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'ChannelRequestMessage': 'SSH_MSG_CHANNEL_REQUEST : #0'.
SshNet.Logging Verbose: 1 : SendMessage to server 'ChannelDataMessage': 'SSH_MSG_CHANNEL_DATA : #0'.
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'ChannelExtendedDataMessage': 'SSH_MSG_CHANNEL_EXTENDED_DATA : #0'.
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'ChannelEofMessage': 'SSH_MSG_CHANNEL_EOF : #0'.
SshNet.Logging Verbose: 1 : ReceiveMessage from server: 'ChannelCloseMessage': 'SSH_MSG_CHANNEL_CLOSE : #0'.
SshNet.Logging Verbose: 1 : SendMessage to server 'ChannelCloseMessage': 'SSH_MSG_CHANNEL_CLOSE : #0'.
SshNet.Logging Verbose: 1 : SendMessage to server 'IgnoreMessage': 'SSH_MSG_IGNORE'.
The thread '<No Name>' (0x2254) has exited with code 0 (0x0).
The thread '<No Name>' (0x1474) has exited with code 0 (0x0).
SshNet.Logging Verbose: 1 : SendMessage to server 'IgnoreMessage': 'SSH_MSG_IGNORE'.
The thread '<No Name>' (0xeb8) has exited with code 0 (0x0).
The thread '<No Name>' (0x27d8) has exited with code 0 (0x0).
The thread '<No Name>' (0x13f8) has exited with code 0 (0x0).
SshNet.Logging Verbose: 1 : SendMessage to server 'IgnoreMessage': 'SSH_MSG_IGNORE'.
SshNet.Logging Verbose: 1 : SendMessage to server 'IgnoreMessage': 'SSH_MSG_IGNORE'.
Comments: ** Comment from web user: Euan **
Same issue here. Just download a big bunch of files and pull out the ethernet cable.
There are multiple more places where SCP gets lost, this function in ScpClient.cs is good. Usually threads are sitting at var b.. but just looking at the function you can see it's a potential infinite loop.
```
private static int ReadByte(Stream stream)
{
var b = stream.ReadByte();
while (b < 0)
{
Thread.Sleep(100);
b = stream.ReadByte();
}
return b;
}
```
PipeStream.cs,
public override int Read(byte[] buffer, int offset, int count)
```
lock (this._buffer)
{
while (!this.ReadAvailable(count))
{
Monitor.Wait(this._buffer)) // stuck here forever
}
```
ScpClient.cs
private void InternalDownload(ChannelSession channel, Stream input, Stream output, string filename, long length)
Just goes round in circles receiving no data. I ended up putting a tick counter timeout in it to break out. It eventually gets the program free, but really messy...
```
private void InternalDownload(ChannelSession channel, Stream input, Stream output, string filename, long length)
{
var buffer = new byte[Math.Min(length, this.BufferSize)];
var needToRead = length;
bool aborted = false;
long prevRead = 0;
int prevTick = Environment.TickCount;
do
{
var read = input.Read(buffer, 0, (int)Math.Min(needToRead, this.BufferSize));
output.Write(buffer, 0, read);
this.RaiseDownloadingEvent(filename, length, length - needToRead);
needToRead -= read;
if (prevRead != needToRead)
{
prevTick = Environment.TickCount;
prevRead = needToRead;
}
else
{
if ((Environment.TickCount - prevTick) > 5000)
{
// found a stuck thread...
aborted = true;
break;
}
}
}
```
I'm still looking for the cause to exceptions not being raised properly.