1

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.

3
  • Why would you want to disconnect if you haven't finished/started reading yet? how can you know if the read ended if its asynchronous? Commented Aug 10, 2014 at 15:48
  • It's more of a general question and does not arise from a particular problem. I simply do not know what is the problem here. Let's say I would like to end the connection for the sake of ending it. :) I would like to apologize for my unquestionable ignorance. I am not a programmer. I simply need to create a simple tool and there are some problems I cannot cope with. Commented Aug 10, 2014 at 15:59
  • 2
    Don't sell yourself short. You might not be experienced yet but you're writing code. You're a programmer ;). Commented Aug 10, 2014 at 16:08

2 Answers 2

3

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:

  1. BeginRead grabs hold of the lock and starts reading.
  2. Disconnect waits on the lock, in the UI thread, until `BeginRead is done with it.
  3. BeginRead finishes the read, and calls DataReceived which calls Invoke while still holding the lock.
  4. Invoke waits for the UI thread to process its message -- but it never will, because the UI thread is waiting on Disconnect.
  5. Now BeginRead and Disconnect 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.

Sign up to request clarification or add additional context in comments.

3 Comments

It is working now. Thank you very much. This level of programming understaing you just showed me is awe inspiring. :) And do you, perhaps, know how to deal with the second problem? And if so, could you share the knowledge?
Sorry, I don't know how to do it. I tried creating a separte class in which there would be a method defining the connection, but then again - "stream" would be accessible only from within this particular method where I also start the connection. Also as I can see I cannot define public var, what would solve the problen.
Ok, sorry for my complete lack of knowledge. Var is a local only type of variable that initially represents "to be assigned" type. In my case var ssh becomes SshClient type variable and var stream becomes ShellStream type variable, both of which can be declared publicly. It is working now. Thank you for you help.
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.