如何使用 .net ssh sshclient 保持会话

本文关键字:会话 sshclient ssh 何使用 net | 更新日期: 2023-09-27 18:24:45

SshClient client;
var sethost = client.RunCommand("export DOCKER_HOST=:2375");
var infohost = client.RunCommand("docker info");
var ps = client.RunCommand("docker ps");

我正在将上述命令与 .Net 的 ssh 客户端一起使用,但它不会保留在接下来的两个命令中导出的第一个命令的状态。

如果我手动 ssh 并执行 3 个命令,它会按预期工作,但上面的 c# 代码似乎忘记了后续命令中的导出。

如何使用 .net ssh sshclient 保持会话

我假设您正在使用 SSH.NET 库。在下面的解决方案中,我使用了稳定版本(2013.4.7(,该版本也可作为NuGet包提供。

有两种可能的解决方案可以使其正常工作。第一个方法是将要运行的命令组合到一个 RunCommand 中。每次调用 RunCommand 时都会启动一个新的 shell,并因此忘记您执行的任何环境设置。通过将命令与 && or || 运算符链接,可以实现所有命令都可以在同一 Shell 中执行。

static void SolutionOne(ConnectionInfo connection)
{
    using (SshClient client = new SshClient(connection))
    {
        client.ErrorOccurred += (e, s) =>
        {
            Debug.WriteLine(s.Exception);
        };
        client.Connect();
        // each RunCommand starts its own/new Shell...
        var sethost = client.RunCommand("export DOCKER_HOST=:2375");
        // ... nothing is kept...
        var infohost = client.RunCommand("export -p");
        if (!infohost.Result.Contains("DOCKER_HOST"))
        {
            Console.WriteLine("sorry, a new shell was started for this command");
        }
        // ... across run commands
        var ps = client.RunCommand("echo has value: $DOCKER");
        Console.WriteLine(ps.Result);
        Console.WriteLine("");
        Console.WriteLine("Chain the linux commands...");
        // chain all commands with &&
        var concatAll = client.RunCommand("export DOCKER_HOST=:2375 && export -p | grep DO && echo has value: $DOCKER_HOST");
        // you should see DOCKER_HOST on the last line!
        Console.WriteLine("> see has value: 2375 at the end?");
        Console.WriteLine(concatAll.Result);
    }
}

第二种解决方案是从SshClient获取ShellStream。这使您有机会读取和写入字节,就像读取文件一样。
建立连接和流后,我们读取要阅读的所有内容。一旦流为空,我们通过写入流来发送第一个命令。然后,我们稍等片刻,让流获取要读取的数据,然后再次继续从流中读取,直到流为空。那时我们可以开始下一个命令,冲洗并重复,直到所有命令都完成。

static void SolutionTwo(ConnectionInfo connection)
{
    using (SshClient client = new SshClient(connection))
    {
        client.ErrorOccurred += (e, s) =>
        {
            Debug.WriteLine(s.Exception);
        };
        client.Connect();
        using (var stream = client.CreateShellStream("dumb", 512, 96, 800, 600, 8191))
        {
            stream.ErrorOccurred += (e, s) =>
            {
                Debug.WriteLine(s.Exception);
            };
            // read welcome message
            Console.WriteLine(ReadFromStream(stream));
            // first command and read its output
            WriteToStream(stream, "uname'n");
            Console.WriteLine(ReadFromStream(stream));
            // second and output
            WriteToStream(stream, "df'n");
            Console.WriteLine(ReadFromStream(stream));
            // third and output
            WriteToStream(stream, "ps aux'n");
            Console.WriteLine(ReadFromStream(stream));
        }
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine(" >>>>>we are all done");
        Console.ForegroundColor = ConsoleColor.White;
    }
}

static void WriteToStream(ShellStream stream, string command) 
{
    stream.Write(command);
}
static StringBuilder ReadFromStream(ShellStream stream)
{
    var result = new StringBuilder();
    // there seems to be a timing/concurrency issue
    // which I only could fix with using this 
    // useless Sleep calls
    Thread.Sleep(500);
    // wait for the Stream to have data
    while (stream.Length == 0)
    {
        // yes, I know ...
        Thread.Sleep(500);
    }
    // let's read!
    string line;
    while ((line = stream.ReadLine()) != null)
    {
        result.AppendLine(line);
    }
    return result;
}

正如您在ReadFromStream方法中注意到的那样,有一些可疑的黑客攻击。我很难从流中阅读可靠的内容。在我最初的尝试中,我使用了 DataReceived 事件,但无论我如何尝试,它要么适用于几行甚至字符,要么根本不起作用。最后,我又回到了一个讨厌的Thread.Sleep电话,该电话一直工作可靠。也许是库中的错误或本地设置中的某些错误,但我放弃了。