问题
I have 2 problems with using SSH.NET library.
I would like to create SSH connection and then disconnect. But if I start asynchronous read, disconnecting causes problems. My program simply freezes when I try to end a connection.
public void button1_Click(object sender, EventArgs e)
{
var ssh = new SshClient(IP, UserName, Password);
ssh.Connect();
var stream = ssh.CreateShellStream("anything", 80, 24, 800, 600, 4096);
byte[] buffer = new byte[1000];
stream.BeginRead(buffer, 0, buffer.Length, null, null);
stream.DataReceived += new EventHandler<Renci.SshNet.Common.ShellDataEventArgs>(
(s, ex) =>
{
Invoke((MethodInvoker)delegate
{
textBox1.AppendText(stream.Read());
});
}
);
stream.WriteLine("ls");
stream.Dispose();
ssh.Disconnect(); //problem here
}
I also do not know how to create a connection that will be availbe from anywhere in the code. I would like to be able to define IP, UserName and Password (e.g. write them in some textBoxes), establish a connection and then interact with it by executing some commands and getting input. Passing the credentials, as I understand, requires some event hanlder (e.g. button1_Click), but then if I would like to interact with the created stream = ssh.CreateShellStream(...)
by passing commands with e.g. button2_Click, I cannot do that because "The name "stream" does not exist in the current context".
Earlier I have been doing it by plik (from PuTTY) and Process functionality of C#:
private void ConnectButton_Click(object sender, EventArgs e)
{
...
p = new Process();
...
sw = p.StandardInput;
...
}
where sw is a streamwriter defined outside of the ConnectButton_Click event handler.
Thanks.
回答1:
This is a deadlock. button1_Click
is running on your UI thread, as is the Invoke
inside of DataReceived
.
SSH.NET does not internally use async -- all of it's "async" methods simply wrap a sync method in a thread pool and lock.
The flow will be this:
BeginRead
grabs hold of the lock and starts reading.Disconnect
waits on the lock, in the UI thread, until `BeginRead is done with it.BeginRead
finishes the read, and callsDataReceived
which callsInvoke
while still holding the lock.Invoke
waits for the UI thread to process its message -- but it never will, because the UI thread is waiting onDisconnect
.- Now
BeginRead
andDisconnect
are both waiting for each-other to finish -- a deadlock.
A quick way to test this would be to change the Invoke
call to BeginInvoke
, which will remove the deadlock.
As far as accessing these objects from anywhere in your Form
, just make them members of the class.
回答2:
I also stumbled accross this problem and the way I see it there is no way getting around this without modifying SSH.NET code. There is following block of code in SSH.NET, (Session.cs:584):
ExecuteThread(() =>
{
try
{
MessageListener();
}
finally
{
_messageListenerCompleted.Set();
}
});
This leads to following block of code(Session.NET.cs:220):
partial void SocketRead(int length, ref byte[] buffer)
..
catch (SocketException exp) {
if (exp.SocketErrorCode == SocketError.ConnectionAborted)
{
buffer = new byte[length];
Disconnect();
inside disconnect it waits for _messageListenerCompleted.WaitOne() But this can never happen because _messageListenerCompleted.Set() is called in try{}finally{} block. So to solve this we should either call: _messageListenerCompleted.Set() before calling disconnect() during socket exception or handling Dissconnect somewhere outside
SocketRead(int length, ref byte[] buffer)
function.
来源:https://stackoverflow.com/questions/25230255/ssh-net-library-sshclient-dispose-public-connection