###### tags: `C#`
# C#-SSH.net
## Install
* 可以在Visual Studio的NuGet管理員裡,搜尋ssh.net並安裝
* 原始碼:[SSH.net@Github](https://github.com/sshnet/SSH.NET)
* 離線安裝:[SSH.net@NuGet](https://www.nuget.org/packages/ssh.net/)
下載後,可以在Visual Studio安裝此插件 ([ssh.net.2020.0.1.nupkg](https://www.nuget.org/api/v2/package/SSH.NET/2020.0.1))
* **直接取用Renci.SshNet.dll**
若無法安裝NuGet插件,可以解壓縮.nupkg檔案,在裡面找到對應.net版本的**Renci.SshNet.dll**,將其放在bin/debug目錄即可.
```csharp
using Renci.SshNet;
using Renci.SshNet.Common;
```
## API
#### 僅使用帳號密碼,不使用ssh key的連線方式
```csharp
ConnectionInfo conInfo =
new ConnectionInfo(_host, 22, _username,
new AuthenticationMethod[] {
new PasswordAuthenticationMethod(_username, _password)
}
);
```
#### 連線
```csharp
SshClient sshClient = new SshClient(conInfo);
```
#### 送指令
```csharp
SshCommand sshCmd = sshClient.RunCommand("ls");
string results = sshCmd.Result;
```
## 可能遇到的問題
### 不能「交互」
```sshClient.RunCommand```可以送```ls```或```uname -a```,這種類型的都沒有問題,但是像以下的情況,就會卡死。
```python
# hello.py
a = input("please input a number: ")
print(a)
```
我在server寫了一個簡易的python程式做測試,當用ssh.net送出時,C#就會卡死。應該跟我用錯API有關係,我用的是以下這個
```csharp
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,這樣就可以「交互」了
```csharp
sshClient.Connect();
Stream streamInput = new PipeStream();
Stream streamOutput = new PipeStream();
Stream streamOutputEx = new PipeStream();
Shell shell = sshClient.CreateShell(streamInput, streamOutput, streamOutputEx);
shell.Start();
```
#### 利用在 InputStream 寫資料的方式,送出指令
```csharp
StreamWriter inputStreamWriter = new StreamWriter(streamInput) { AutoFlush = true };
inputStreamWriter.Write("python hello.py" + "\n");
```
#### 無窮迴圈讀取 OutputStream,取得輸出
```csharp
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使用率飆高。
>[參考資料](https://stackoverflow.com/questions/66483431/ssh-net-real-time-logging-high-cpu)
[SSH.NET中有一個已知的錯誤](https://github.com/sshnet/SSH.NET/issues/702),OutputStream當沒有收到實際的新數據時,該命令會不斷地吐出空數據。
### 解決辦法
捨棄Shell,改用ShellStream。
將inputStream與outputStart合併成一個ShellStream。
```csharp
sshClient.Connect();
shellstream = sshClient.CreateShellStream("xxx", 80, 24, 800, 600, 1024);
// xxx是什麼不重要
```
讀取改用shellStream
```csharp
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
```csharp
streamWriter = new StreamWriter(shellstream) { AutoFlush = true };
```
## 參考資料
* [Is it possible to execute multiple SSH commands from a single login session with SSH.NET?](https://stackoverflow.com/questions/46847304/is-it-possible-to-execute-multiple-ssh-commands-from-a-single-login-session-with)
* [SSH.NET doesn't process my shell input commands](https://stackoverflow.com/questions/44528158/ssh-net-doesnt-process-my-shell-input-commands/44542531#44542531)