SSH.NET库 - SshClient.Dispose(),公共连接

时间:2014-08-10 15:44:57

标签: c# multithreading winforms ssh.net

使用SSH.NET库时遇到2个问题。

我想创建SSH连接然后断开连接。但是,如果我开始异步读取,断开连接会导致问题。当我尝试结束连接时,我的程序会冻结。

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
}

我也不知道如何创建一个可以从代码中的任何位置获得的连接。我希望能够定义IP,UserName和Password(例如,在一些textBox中写入它们),建立连接,然后通过执行一些命令和获取输入与它进行交互。根据我的理解,传递凭证需要一些事件处理器(例如button1_Click),但是如果我想通过传递命令来与创建的stream = ssh.CreateShellStream(...)进行交互。 button2_Click,我不能这样做,因为“名称”流“在当前上下文中不存在”。

早些时候我一直在使用plik(来自PuTTY)和C#的进程功能:

private void ConnectButton_Click(object sender, EventArgs e)
{
    ...
    p = new Process();
    ...
    sw = p.StandardInput;
    ...
}

其中sw是在ConnectButton_Click事件处理程序之外定义的streamwriter。

感谢。

2 个答案:

答案 0 :(得分:3)

这是一个僵局。 button1_Click正在您的UI线程上运行,Invoke内的DataReceived也是如此。

SSH.NET内部不使用异步 - 所有这些“异步”方法都只是将一个同步方法包装在一个线程池中并锁定。

流程将是:

  1. BeginRead抓住锁并开始阅读。
  2. Disconnect等待UI线程中的锁,直到`BeginRead完成。
  3. BeginRead完成了阅读,并在仍然保持锁定时调用DataReceived来调用Invoke
  4. Invoke等待UI线程处理其消息 - 但它永远不会,因为UI线程正在等待Disconnect
  5. 现在BeginReadDisconnect都在等待彼此完成 - 一个死锁。
  6. 快速测试方法是将Invoke调用更改为BeginInvoke,这将消除死锁。

    至于从Form中的任何位置访问这些对象,只需让它们成为该类的成员。

答案 1 :(得分:1)

我也偶然发现了这个问题和我看到它的方式,没有修改SSH.NET代码就无法解决这个问题。 SSH.NET中有以下代码块,(Session.cs:584):

ExecuteThread(() =>
                    {
                        try
                        {
                            MessageListener();
                        }
                        finally
                        {
                            _messageListenerCompleted.Set();
                        }
                    });

这导致下面的代码块(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();

内部断开连接等待 _messageListenerCompleted.WaitOne() 但这绝不会发生,因为在 try {} finally {} 块中调用 _messageListenerCompleted.Set()。 所以要解决这个问题,我们应该致电: __ssageListenerCompleted.Set()在套接字异常或处理外的某个地方调用disconnect()之前

  SocketRead(int length, ref byte[] buffer)

功能