【问题标题】:SSH.NET Library - SshClient.Dispose(), public connectionSSH.NET 库 - SshClient.Dispose(),公共连接
【发布时间】:2014-08-10 15:44:57
【问题描述】:

我在使用 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、用户名和密码(例如,将它们写在一些文本框中),建立连接,然后通过执行一些命令并获取输入来与之交互。据我了解,传递凭据需要一些事件处理程序(例如 button1_Click),但是如果我想通过传递命令与创建的 stream = ssh.CreateShellStream(...) 进行交互,例如button2_Click,我不能这样做,因为“当前上下文中不存在名称“流”。

之前我一直通过 plik(来自 PuTTY)和 C# 的 Process 功能来做:

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

其中 sw 是在 ConnectButton_Click 事件处理程序之外定义的流写入器。

谢谢。

【问题讨论】:

  • 如果您还没有完成/开始阅读,为什么要断开连接?如果它是异步的,你怎么知道读取是否结束?
  • 这更像是一个笼统的问题,并不是由特定问题引起的。我根本不知道这里有什么问题。假设我想为了结束连接而结束连接。 :) 我想为我毫无疑问的无知道歉。我不是程序员。我只需要创建一个简单的工具,并且有一些我无法解决的问题。
  • 不要卖空自己。您可能还没有经验,但您正在编写代码。你是个程序员;)。

标签: c# multithreading winforms ssh.net


【解决方案1】:

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

SSH.NET 内部不使用异步——所有的“异步”方法只是将同步方法包装在线程池中并锁定。

流程是这样的:

  1. BeginRead 抓住锁并开始阅读。
  2. Disconnect 在 UI 线程中等待锁,直到 `BeginRead 完成。
  3. BeginRead 完成读取,并调用DataReceived,后者调用Invoke,同时仍持有锁。
  4. Invoke 等待 UI 线程处理其消息 - 但它永远不会,因为 UI 线程正在等待 Disconnect
  5. 现在BeginReadDisconnect 都在等待对方完成——这是一个死锁。

一种快速的测试方法是将Invoke 调用更改为BeginInvoke,这将消除死锁。

只要从您的Form 中的任何位置访问这些对象,只需将它们设为类的成员即可。

【讨论】:

  • 它正在工作。非常感谢你。您刚刚向我展示的这种编程理解水平令人敬畏。 :) 你知道如何处理第二个问题吗?如果是这样,你能分享一下知识吗?
  • 对不起,我不知道该怎么做。我尝试创建一个单独的类,其中将有一个定义连接的方法,但话又说回来 - “流”只能从我也启动连接的这个特定方法中访问。另外,正如我所见,我无法定义公共变量,什么可以解决问题。
  • 好的,抱歉我完全缺乏知识。 Var 是一种仅限于本地的变量类型,最初表示“待分配”类型。在我的例子中,var ssh 变成了 SshClient 类型变量,而 var stream 变成了 ShellStream 类型变量,两者都可以公开声明。它现在正在工作。谢谢你的帮助。
【解决方案2】:

我也偶然发现了这个问题,我认为如果不修改 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()。 所以要解决这个问题,我们应该调用: _messageListenerCompleted.Set() 在套接字异常期间调用 disconnect() 之前或在外部某处处理 Dissconnect

  SocketRead(int length, ref byte[] buffer)

功能。

【讨论】:

    猜你喜欢
    • 2015-12-12
    • 1970-01-01
    • 2014-04-10
    • 2012-06-04
    • 1970-01-01
    • 2011-08-08
    • 2020-12-21
    • 2015-01-11
    • 2019-05-08
    相关资源
    最近更新 更多