如何改进Renci SSH.NET的SSH函数,取消延迟并获取完整输出?
解决Renci SSH.NET ShellStream命令输出不完整的问题(替代Thread.Sleep的方案)
你当前用ShellStream加固定Thread.Sleep的方式不可靠——命令执行时长不稳定,延迟设短了输出不全,设长了浪费时间。下面给两个更可靠的方案:
方案1:用RunCommand替代ShellStream(推荐,单命令场景)
Renci SSH.NET提供了SshClient.RunCommand方法,专门用于执行单条非交互式命令,它会自动等待命令执行完成后返回完整输出,完全不需要手动加延迟。
替换后的代码:
Public Function SendSSHCommand(ByVal command As String) As String Dim host As String = "10.0.1.77" Dim port As Integer = 228 Dim user As String = "User11" Dim password As String = "pass11" Dim result As String = String.Empty Dim connectionInfo As New ConnectionInfo(host, port, user, New PasswordAuthenticationMethod(user, password)) Using client As New SshClient(connectionInfo) Try client.Connect() Dim cmd As SshCommand = client.RunCommand(command) ' 合并标准输出和错误输出 result = cmd.Result & If(String.IsNullOrEmpty(cmd.Error), "", vbCrLf & "Error: " & cmd.Error) client.Disconnect() Catch ex As Exception result = "Error: " & ex.Message End Try End Using Return result End Function
方案2:必须用ShellStream的场景(交互式操作)
如果需要执行交互式命令(比如多命令依赖、需要输入交互内容),可以通过检测服务器命令提示符判断命令是否执行完成,替代固定延迟。
示例代码:
Public Function SendSSHCommandWithShellStream(ByVal command As String) As String Dim host As String = "10.0.1.77" Dim port As Integer = 228 Dim user As String = "User11" Dim password As String = "pass11" ' 根据服务器实际提示符修改,比如"$ "、"# "或完整的"user@server:~$ " Dim prompt As String = "$ " Dim timeoutMs As Integer = 10000 ' 10秒超时 Dim result As String = String.Empty Dim connectionInfo As New ConnectionInfo(host, port, user, New PasswordAuthenticationMethod(user, password)) Using client As New SshClient(connectionInfo) Try client.Connect() Using shell As ShellStream = client.CreateShellStream("custom", 80, 24, 800, 600, 1024) Dim reader As New StreamReader(shell) Dim writer As New StreamWriter(shell) writer.AutoFlush = True ' 读取初始提示符,确认Shell已就绪 Dim initialOutput As String = reader.ReadToEnd() If Not initialOutput.Contains(prompt) Then result = "Error: 无法识别服务器提示符" Return result End If ' 发送命令 writer.WriteLine(command) ' 循环读取输出直到出现提示符或超时 Dim startTime As DateTime = DateTime.Now Dim outputBuilder As New StringBuilder() Do While (DateTime.Now - startTime).TotalMilliseconds < timeoutMs If shell.DataAvailable Then Dim line As String = reader.ReadLine() If line Is Nothing Then Exit Do outputBuilder.AppendLine(line) ' 检测到提示符,说明命令执行完成 If line.Contains(prompt) Then ' 截断最后的提示符(按需调整) Dim finalOutput As String = outputBuilder.ToString() result = finalOutput.Substring(0, finalOutput.LastIndexOf(prompt)) Exit Do End If Else ' 短暂休眠避免CPU占用过高 Thread.Sleep(100) End If Loop ' 判断是否超时 If result = String.Empty Then result = "Error: 命令执行超时" End If shell.Close() End Using client.Disconnect() Catch ex As Exception result = "Error: " & ex.Message End Try End Using Return result End Function
注意事项
- 优先用方案1,
RunCommand是SSH.NET为单命令场景设计的API,逻辑简洁且可靠。 - 方案2中,提示符必须和服务器实际显示的一致,如果服务器提示符随用户/路径变化,需要动态调整
prompt变量。 - 超时时间可根据命令实际耗时灵活调整,避免误判长时间运行的合法命令。
内容的提问来源于stack exchange,提问作者E_Blue
相关产品推荐
相关产品推荐

