tags: C#

C#-SSH.net

Install

  • 可以在Visual Studio的NuGet管理員裡,搜尋ssh.net並安裝
  • 原始碼:SSH.net@Github
  • 離線安裝:SSH.net@NuGet
    下載後,可以在Visual Studio安裝此插件 (ssh.net.2020.0.1.nupkg)
  • 直接取用Renci.SshNet.dll
    若無法安裝NuGet插件,可以解壓縮.nupkg檔案,在裡面找到對應.net版本的Renci.SshNet.dll,將其放在bin/debug目錄即可.
using Renci.SshNet;
using Renci.SshNet.Common;

API

僅使用帳號密碼,不使用ssh key的連線方式

ConnectionInfo conInfo = 
    new ConnectionInfo(_host, 22, _username,
        new AuthenticationMethod[] { 
            new PasswordAuthenticationMethod(_username, _password) 
        }
); 

連線

SshClient sshClient = new SshClient(conInfo);

送指令

SshCommand sshCmd = sshClient.RunCommand("ls");
string results = sshCmd.Result;

可能遇到的問題

不能「交互」

sshClient.RunCommand可以送lsuname -a,這種類型的都沒有問題,但是像以下的情況,就會卡死。

# hello.py
a = input("please input a number: ")
print(a)

我在server寫了一個簡易的python程式做測試,當用ssh.net送出時,C#就會卡死。應該跟我用錯API有關係,我用的是以下這個

SshCommand sshCmd = sshClient.RunCommand("python hello.py");
if (!string.IsNullOrWhiteSpace(sshCmd.Error)) {
    Log(sshCmd.Error);
}
else {
    Log(sshCmd.Result.Replace("\n", "\r\n"));
}

可能因為hello.py一直在等user輸入,所以sshClient.RunCommand("python hello.py")會卡死,無法跳到下一行。

改用Shell

用CreateShell產生一個Shell,這樣就可以「交互」了

sshClient.Connect();

Stream streamInput = new PipeStream();
Stream streamOutput = new PipeStream();
Stream streamOutputEx = new PipeStream();

Shell shell = sshClient.CreateShell(streamInput, streamOutput, streamOutputEx);
shell.Start();

利用在 InputStream 寫資料的方式,送出指令

StreamWriter inputStreamWriter = new StreamWriter(streamInput) { AutoFlush = true };
inputStreamWriter.Write("python hello.py" + "\n");

無窮迴圈讀取 OutputStream,取得輸出

Task taskOutput = Task.Run(async () => {
    StreamReader streamReader = new StreamReader(streamOutput);
    while (sshClient.IsConnected) {
        if (streamOutput.CanRead) {
            string str = streamReader.ReadToEnd();        
            if (str != string.Empty) {
                _logEventTrigger(str);
            }
        }        
        await Task.Delay(20);
    }
});

CPU使用率居高不下

爬了下文,好像是library有缺陷。shell.Start();這行會讓CPU使用率飆高。

參考資料
SSH.NET中有一個已知的錯誤,OutputStream當沒有收到實際的新數據時,該命令會不斷地吐出空數據。

解決辦法

捨棄Shell,改用ShellStream。
將inputStream與outputStart合併成一個ShellStream。

sshClient.Connect();                 
shellstream = sshClient.CreateShellStream("xxx", 80, 24, 800, 600, 1024);
// xxx是什麼不重要

讀取改用shellStream

Task taskOutput = Task.Run(async () => {
    StreamReader streamReader = new StreamReader(shellstream);
    while (sshClient.IsConnected) {
        if (shellstream.CanRead) {
            string str = streamReader.ReadToEnd();                   
            if (str != string.Empty) {
                _logEventTrigger(str);
            }
        }       
        await Task.Delay(20);
    }
});

送出也改用shellStream

streamWriter = new StreamWriter(shellstream) { AutoFlush = true };

參考資料