I want to connect to an SSH server, which, when connected, runs a user command that expects XML to be entered (UTF-8 encoding, a few lines with a special line at the end of each XML document marking its end) and returns XML ( UTF-8 encoding, several lines, with the same special line at the end, which means the end of the document).
There is currently a mechanism for using plink.exe (from the fame of PuTTy) (which means: the server side works as indicated), but I'm in the process of eliminating the need for an external process.
I am reading the Renci.SshNet library, but I find the documentation inadequate and cannot find examples of what I need.
My attempt:
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 } } }
Given this as an assembly, I am at a stage where I am trying to connect to the server (tests pass through booish to facilitate):
>>> import ServerConnect >>> a= ServerConnection("passphrase") ServerConnect.ServerConnection >>> a.Connect() >>> a.Command("<i command=\"ping\"/>") sending command sending EOC got null ''
I see (on auth.log server) that the connection is established after a.Connect() called. However, it seems that everything I write to stdin_writer is not sent to the server.
How can I execute this mechanism? Send data to a server, then send a separator, and then read the data until I read the separator? The cycle is repeated many times during the connection.
Please note that I am not an experienced C # programmer, so itβs possible that I donβt have a basic understanding of the C # stream model.
PS I saw this answer, but it seems that the CreateShellStream call blocks may have a PseudoTty distribution (this process does not need pseudo -ttys, only input / output).
PS2 I downloaded and compiled the latest version of Renci.SshNet from codeplex.com, and now I am in this place (excerpt from the Connect method):
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();
However, any entries in stdin_writer (followed by flushes) do not seem to go through.