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

时间:2015-11-28 08:04:23

标签: c# ssh

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

我正在使用ssh客户端的上述命令来处理.Net但是它没有保留第一个在下两个命令中执行导出的命令的状态。

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

1 个答案:

答案 0 :(得分:0)

我假设您使用的是SSH.NET库。在下面的解决方案中,我使用了stable version (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);
    }
}

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

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调用,它始终可靠。也许它是库中的一个错误或我本地设置中的某些东西,但我放弃了。