【发布时间】:2016-12-08 03:34:55
【问题描述】:
我想连接一个 SSH 服务器,该服务器在连接时运行一个自定义命令,该命令需要 XML 作为输入(UTF-8 编码,多行,每个 XML 文档末尾有一个特殊的行标记其结束)和输出 XML(UTF-8 编码,多行,末尾有相同的特殊行表示文档结束)。
目前有一种使用 plink.exe(以 PuTTy 闻名)的机制可以工作(意思是:服务器端确实按指定工作),但我正在消除对外部进程的需要。
我正在尝试使用 Renci.SshNet 库,但我发现文档不足,并且找不到我需要的示例。
到目前为止我的尝试:
using System;
using Renci.SshNet;
namespace ServerConnect
{
public class ServerConnection
{
private const string EOC= "*-* COMMAND DELIMITER *-*";
private SshClient scon;
private System.IO.Stream stdin, stdout, stderr;
private System.IO.StreamWriter stdin_writer;
private System.IO.StreamReader stdout_reader;
private Shell shell;
public ServerConnection (string keyFileName, string keyFilePassphrase)
{
ConnectionInfo ci= new ConnectionInfo("my.server.local", 22, "datamgr",
new PrivateKeyAuthenticationMethod("datamgr", new PrivateKeyFile(keyFileName, keyFilePassphrase)));
this.scon= new SshClient(ci);
}
public ServerConnection (string keyFilePassphrase): this("client_id_rsa", keyFilePassphrase) {}
public void Connect() {
this.scon.Connect();
this.stdin= new System.IO.MemoryStream();
this.stdin_writer= new System.IO.StreamWriter(stdin);
this.stdout= new System.IO.MemoryStream();
this.stdout_reader= new System.IO.StreamReader(stdout);
this.stderr= new System.IO.MemoryStream();
this.shell= this.scon.CreateShell(this.stdin, this.stdout, this.stderr);
this.shell.Start();
}
public void Disconnect() {
this.scon.Disconnect();
}
public string Command(string text) {
Console.WriteLine("sending command");
this.stdin_writer.WriteLine(text);
Console.WriteLine("sending EOC");
this.stdin_writer.WriteLine(EOC);
this.stdin_writer.Flush();
for (;;) {
string line;
line= this.stdout_reader.ReadLine();
if (line == null) {
Console.WriteLine("got null");
break;
}
Console.WriteLine("READ");
Console.WriteLine(line);
if (line.StartsWith(EOC)) break;
}
return ""; // yes, this should return the built output
}
}
}
鉴于这是一个程序集,我正处于尝试连接到服务器的阶段(为方便起见,测试通过 booish 运行):
>>> import ServerConnect
>>> a= ServerConnection("passphrase")
ServerConnect.ServerConnection
>>> a.Connect()
>>> a.Command("<i command=\"ping\"/>")
sending command
sending EOC
got null
''
我可以看到(在服务器的 auth.log 中)在a.Connect() 调用之后建立了连接。但是,似乎我写给stdin_writer 的任何内容都不会发送到服务器。
我怎样才能完成这个机制?向服务器发送数据,然后发送分隔符,然后读取数据,直到我读取分隔符?在连接期间,该循环将重复多次。
请注意,我不是一个有经验的 C# 程序员,所以我可能对 C# 流模型缺乏基本的了解。
PS 我已经看到this 的回答,但似乎CreateShellStream 调用阻塞,可能在PseudoTty 分配中(该过程不需要伪tty,只需输入/输出)。
PS2 我从codeplex.com下载并编译了最新版本的Renci.SshNet,现在我到了这一点(摘自Connect方法):
this.scon.Connect();
this.stream= this.scon.CreateShellStream("dumb", 80, 24, 800, 600, 1024);
this.stdin_writer= new System.IO.StreamWriter(this.stream);
this.stdout_reader= new System.IO.StreamReader(this.stream);
this.shell= this.scon.CreateShell(this.stream, this.stream, this.stream);
this.shell.Start();
但是,对 stdin_writer 的任何写入(以及随后的刷新)似乎都没有通过。
【问题讨论】:
-
我找不到解决方案,所以我在 Python 中实现了一个自定义 SSL 服务器,在 .NET 中实现了一个 SSL 客户端,因此我设法继续。我不回答这个问题,因为有人可以在某个时候回答,这可能对其他人有用。