Comments (11)
Please share the code that you used for this test, and include the following information:
- SSH server software (name and version)
- encryption algorithm used
- MAC algorithm used
- .NET Framework version
The only big area's of improvement I can think of are:
- encryption algorithms
- PipeStream (used in SshCommand and ScpClient)
- ShellStream
SSH.NET uses its proper encryption implementations (in order to support a broad set of target frameworks), while SharpSSH uses the BCL implementations (System.Security.Cryptography.*) which are surely highly tuned.
from ssh.net.
- SSH server software: SSH-2.0-OpenSSH_4.3
- encryption algorithm used: aes256-ctr
- MAC algorithm used: hmac-md5
- .NET Framework version: 4.0
Code:
IsshStream.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace SshTest
{
public interface ISshStream
{
void Connect(string host, string userName, string password);
void Close();
void Write(string data);
StreamReader GetStreamReader();
StreamWriter GetStreamWriter();
}
}
SshNetStream.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace SshTest
{
internal class SshNetStream : ISshStream
{
Renci.SshNet.ShellStream _shellStream;
Renci.SshNet.SshClient _sshClinet;
public void Connect(string host, string userName, string password)
{
_sshClinet = new Renci.SshNet.SshClient(host, userName, password);
_sshClinet.Connect();
_shellStream = _sshClinet.CreateShellStream("ShellStream", 512, 24, 512, 512, 1024);
}
public void Close()
{
if (_sshClinet != null)
_sshClinet.Disconnect();
}
public void Write(string data)
{
StreamWriter streamWriter = GetStreamWriter();
streamWriter.Write(data + "\r");
streamWriter.Flush();
}
StreamReader _streamReader = null;
public StreamReader GetStreamReader()
{
if (_streamReader != null)
{
return _streamReader;
}
else
{
if (_shellStream == null)
{
return null;
}
_streamReader = new StreamReader(_shellStream);
return _streamReader;
}
}
StreamWriter _streamWriter = null;
public StreamWriter GetStreamWriter()
{
if (_streamWriter != null)
{
return _streamWriter;
}
else
{
if (_shellStream == null)
{
return null;
}
_streamWriter = new StreamWriter(_shellStream);
return _streamWriter;
}
}
}
}
SharpSshStream.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace SshTest
{
internal class SharpSshStream : ISshStream
{
Tamir.SharpSsh.SshStream _sshStream;
public void Connect(string host, string userName, string password)
{
_sshStream = new Tamir.SharpSsh.SshStream(host, userName, password);
}
public void Close()
{
if (_sshStream != null)
_sshStream.Close();
}
public void Write(string data)
{
StreamWriter streamWriter = GetStreamWriter();
streamWriter.Write(data + "\r");
streamWriter.Flush();
}
StreamReader _streamReader = null;
public StreamReader GetStreamReader()
{
if (_streamReader != null)
{
return _streamReader;
}
else
{
if (_sshStream == null)
{
return null;
}
_streamReader = new StreamReader(_sshStream.GetInputStream());
return _streamReader;
}
}
StreamWriter _streamWriter = null;
public StreamWriter GetStreamWriter()
{
if (_streamWriter != null)
{
return _streamWriter;
}
else
{
if (_sshStream == null)
{
return null;
}
_streamWriter = new StreamWriter(_sshStream.GetOutputStream());
return _streamWriter;
}
}
}
}
SshStreamFactory.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SshTest
{
internal static class SshStreamFactory
{
public static ISshStream CreateSshStream(string sshStreamType)
{
switch (sshStreamType)
{
case "sharpssh":
return new SharpSshStream();
case "sshnet":
return new SshNetStream();
default:
throw new Exception("Invalid SshStream type:" + sshStreamType);
}
}
}
}
UnblockStreamReader.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
namespace SshTest
{
class UnblockStreamReaderLockObject
{
public char[] buffer;
public int len;
public int pos;
};
public class UnblockStreamReader
{
const int BUFFER_SIZE = 65536;
Thread readThread;
UnblockStreamReaderLockObject lockObject;
public int GetUnreadBufferLength()
{
return lockObject.len;
}
public int GetUnreadBufferPosition()
{
return lockObject.pos;
}
StreamReader streamReader;
public UnblockStreamReader(StreamReader streamReader)
{
lockObject = new UnblockStreamReaderLockObject();
lockObject.buffer = new char[BUFFER_SIZE + 1];
lockObject.len = 0;
lockObject.pos = 0;
this.streamReader = streamReader;
readThread = new Thread(this.ReadThreadProc);
readThread.Name = "UnblockStreamReader thread";
readThread.Start();
}
public void Close()
{
readThread.Abort();
lock (lockObject)
{
lockObject.len = 0;
lockObject.pos = 0;
}
}
private void ReadThreadProc(object param)
{
char[] buf = new char[1];
int readLen = 0;
bool isSleep = false;
try
{
while (true)
{
lock (lockObject)
{
if (lockObject.len >= BUFFER_SIZE)
{
isSleep = true;
}
}
if (isSleep == true)
{
isSleep = false;
Thread.Sleep(10);
continue;
}
readLen = this.streamReader.Read(buf, 0, 1);
if (readLen > 0)
{
lock (lockObject)
{
if ((lockObject.pos + lockObject.len) >= BUFFER_SIZE)
{
for (int i = 0; i < lockObject.len; i++)
{
lockObject.buffer[i] = lockObject.buffer[lockObject.pos + i];
}
lockObject.pos = 0;
}
lockObject.buffer[lockObject.pos + lockObject.len] = buf[0];
lockObject.len++;
}
}
else
Thread.Sleep(10);
}
}
catch (Exception e)
{
e.ToString();
return;
}
}
public int ReadChar(ref char buf)
{
lock (lockObject)
{
if (lockObject.len == 0)
{
return 0;
}
buf = lockObject.buffer[lockObject.pos];
lockObject.pos++;
lockObject.len--;
}
return 1;
}
public String ReadToEnd(bool isRemove = true)
{
String resultString;
lock (lockObject)
{
if (lockObject.len == 0)
{
return null;
}
resultString = new String(lockObject.buffer, lockObject.pos, lockObject.len);
if (isRemove)
{
lockObject.pos = 0;
lockObject.len = 0;
}
return resultString;
}
}
public String ReadLine(char lineEndFlag = '\n')
{
String resultString;
while (true)
{
lock (lockObject)
{
if (lockObject.len == 0)
{
Thread.Sleep(10);
continue;
}
for (int i = 0; i < lockObject.len; i++)
{
if (lockObject.buffer[lockObject.pos + i] == lineEndFlag)
{
resultString = new String(lockObject.buffer, lockObject.pos, i + 1);
lockObject.pos = lockObject.pos + i + 1;
lockObject.len = lockObject.len - i - 1;
return resultString;
}
}
}
Thread.Sleep(10);
}
}
}
}
UnblockStreamUtility.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
namespace SshTest
{
public class UnblockStreamUtility
{
internal static String[] ReadUntil(UnblockStreamReader reader, List<UntilInfo> untilInfoList, int noResponseTimeoutSeconds)
{
List<String> resultList = new List<String>();
char[] buffer = new char[65536];
int curBufferLen = 0;
int noResponseTimeoutMilliseconds = noResponseTimeoutSeconds * 1000;
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
while (true)
{
char readChar = new char();
int readCharLen = reader.ReadChar(ref readChar);
if (readCharLen == 0)
{
Thread.Sleep(10);
if (stopwatch.ElapsedMilliseconds >= noResponseTimeoutMilliseconds)
{
stopwatch.Stop();
throw new Exception("No Response Timeout!");
}
continue;
}
else { stopwatch.Restart(); }
buffer[curBufferLen] = readChar;
foreach (UntilInfo untilInfo in untilInfoList)
{
if (readChar == untilInfo.UntilCharArray[untilInfo.CompareLen])
{
untilInfo.CompareLen++;
if (untilInfo.CompareLen == untilInfo.UntilCharArray.Length)
{
untilInfo.CompareLen = 0;
String lineStr = new String(buffer, 0, curBufferLen + 1);
if (lineStr.EndsWith("\r\r\n"))
{
lineStr = lineStr.Substring(0, lineStr.Length - 3);
}
else if (lineStr.EndsWith("\r\n"))
{
lineStr = lineStr.Substring(0, lineStr.Length - 2);
}
else if (lineStr.EndsWith("\n"))
{
lineStr = lineStr.Substring(0, lineStr.Length - 1);
}
resultList.Add(lineStr);
curBufferLen = 0;
if (untilInfo.ExceptionMessage != null)
throw new Exception(untilInfo.ExceptionMessage);
else
return resultList.ToArray();
}
}
else
{
untilInfo.CompareLen = 0;
}
}
if (readChar == '\n')
{
String lineStr = new String(buffer, 0, curBufferLen + 1);
if (lineStr.EndsWith("\r\r\n"))
{
lineStr = lineStr.Substring(0, lineStr.Length - 3);
}
else if (lineStr.EndsWith("\r\n"))
{
lineStr = lineStr.Substring(0, lineStr.Length - 2);
}
else if (lineStr.EndsWith("\n"))
{
lineStr = lineStr.Substring(0, lineStr.Length - 1);
}
resultList.Add(lineStr);
curBufferLen = 0;
foreach (UntilInfo untilInfo in untilInfoList)
{
untilInfo.CompareLen = 0;
}
continue;
}
curBufferLen++;
}
}
}
}
UntilInfo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SshTest
{
public class UntilInfo
{
public UntilInfo(string untilString, string exceptionMessage = null)
{
this.UntilString = untilString;
this.ExceptionMessage = exceptionMessage;
this.UntilCharArray = untilString.ToCharArray();
this.CompareLen = 0;
}
public string UntilString
{
get;
private set;
}
public string ExceptionMessage
{
get;
private set;
}
public char[] UntilCharArray
{
get;
private set;
}
public int CompareLen
{
get;
set;
}
}
}
MySshClient.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.ComponentModel;
namespace SshTest
{
public class MySshClient : IDisposable
{
public MySshClient(string host, string userName, string password, string sshStreamType)
{
_host = host;
_userName = userName;
_password = password;
_sshStreamType = sshStreamType;
}
private string _host;
private string _userName;
private string _password;
private string _sshStreamType;
private int _noResponseTimeoutSeconds = 60;
private Component component = new Component();
private bool disposed = false;
~MySshClient()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
component.Dispose();
}
this.Close();
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private ISshStream SshStream
{
get;
set;
}
public void Connect()
{
SshStream = SshStreamFactory.CreateSshStream(_sshStreamType);
SshStream.Connect(_host, _userName, _password);
try
{
UnblockStreamReader = new UnblockStreamReader(SshStream.GetStreamReader());
InitEnv();
}
catch (Exception ex)
{
this.Close();
throw ex;
}
}
protected virtual void InitEnv()
{
string[] response = null;
this.SshStream.Write("set +o vi");
this.SshStream.Write("set +o viraw");
this.SshStream.Write("export PROMPT_COMMAND=");
this.SshStream.Write("export PS1=" + Prompt);
response = this.ReadResponse("export PS1=" + Prompt + "\r\n", _noResponseTimeoutSeconds);
response = this.ReadResponse(Prompt, _noResponseTimeoutSeconds);
string helloMessage = "Hello this is test message!";
this.SshStream.Write("echo '" + helloMessage + "'");
response = this.ReadResponse(helloMessage + "\r\n", _noResponseTimeoutSeconds);
response = this.ReadResponse(Prompt, _noResponseTimeoutSeconds);
this.SshStream.Write("stty columns 512");
response = this.ReadResponse(Prompt, _noResponseTimeoutSeconds);
this.SshStream.Write("stty rows 24");
response = this.ReadResponse(Prompt, _noResponseTimeoutSeconds);
this.SshStream.Write("export LANG=en_US.UTF-8");
response = this.ReadResponse(Prompt, _noResponseTimeoutSeconds);
this.SshStream.Write("export NLS_LANG=American_America.ZHS16GBK");
response = this.ReadResponse(Prompt, _noResponseTimeoutSeconds);
this.SshStream.Write("unalias grep");
response = this.ReadResponse(Prompt, _noResponseTimeoutSeconds);
}
protected virtual void Close()
{
if (UnblockStreamReader != null)
{
UnblockStreamReader.Close();
UnblockStreamReader = null;
}
if (SshStream != null)
{
SshStream.Close();
SshStream = null;
}
}
protected UnblockStreamReader UnblockStreamReader
{
get;
private set;
}
public String Prompt
{
get
{
return "[SHINE_COMMAND_PROMPT]";
}
}
public void Write(string data)
{
if (SshStream == null)
{
this.Connect();
}
if (UnblockStreamReader.GetUnreadBufferLength() > 0)
{
UnblockStreamReader.ReadToEnd();
}
this.SshStream.Write(data);
}
public string[] ReadResponse(string prompt, int noResponseTimeoutSeconds)
{
List<UntilInfo> untilInfoList = new List<UntilInfo>() { new UntilInfo(prompt) };
string[] response = UnblockStreamUtility.ReadUntil(UnblockStreamReader, untilInfoList, noResponseTimeoutSeconds);
return response;
}
public string[] ReadResponse(List<UntilInfo> untilInfoList, int noResponseTimeoutSeconds)
{
string[] response = UnblockStreamUtility.ReadUntil(UnblockStreamReader, untilInfoList, noResponseTimeoutSeconds);
return response;
}
public string[] RunCommand(string command)
{
SshStream.Write("command");
return ReadResponse(Prompt, _noResponseTimeoutSeconds);
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace SshTest
{
class Program
{
static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
Renci.SshNet.SshClient sshNet = new Renci.SshNet.SshClient("10.168.3.51", "oracle", "oracle");
stopwatch.Restart();
sshNet.Connect();
stopwatch.Stop();
Console.Write("sshNet.Connect() ");
Console.WriteLine(stopwatch.ElapsedMilliseconds + "milliseconds");
stopwatch.Restart();
Renci.SshNet.SshCommand sshCommand = sshNet.RunCommand("free -m");
stopwatch.Stop();
Console.Write("sshNet.RunCommand() ");
Console.WriteLine(stopwatch.ElapsedMilliseconds + "milliseconds");
Tamir.SharpSsh.SshExec sharpSsh = new Tamir.SharpSsh.SshExec("10.168.3.51", "oracle", "oracle");
stopwatch.Restart();
sharpSsh.Connect();
stopwatch.Stop();
Console.Write("sharpSsh.Connect() ");
Console.WriteLine(stopwatch.ElapsedMilliseconds + "milliseconds");
stopwatch.Restart();
string result = sharpSsh.RunCommand("free -m");
stopwatch.Stop();
Console.Write("sharpSsh.RunCommand() ");
Console.WriteLine(stopwatch.ElapsedMilliseconds + "milliseconds");
MySshClient mySshClient_SshNet = new MySshClient("10.168.3.51", "oracle", "oracle", "sshnet");
stopwatch.Restart();
mySshClient_SshNet.Connect();
stopwatch.Stop();
Console.Write("mySshClient_SshNet.Connect() ");
Console.WriteLine(stopwatch.ElapsedMilliseconds + "milliseconds");
stopwatch.Restart();
string[] results1 = mySshClient_SshNet.RunCommand("free -m");
stopwatch.Stop();
Console.Write("mySshClient_SshNet.RunCommand() ");
Console.WriteLine(stopwatch.ElapsedMilliseconds + "milliseconds");
MySshClient mySshClient_SharpSsh = new MySshClient("10.168.3.51", "oracle", "oracle", "sharpssh");
stopwatch.Restart();
mySshClient_SharpSsh.Connect();
stopwatch.Stop();
Console.Write("mySshClient_SharpSsh.Connect() ");
Console.WriteLine(stopwatch.ElapsedMilliseconds + "milliseconds");
stopwatch.Restart();
string[] results2 = mySshClient_SharpSsh.RunCommand("free -m");
stopwatch.Stop();
Console.Write("mySshClient_SharpSsh.RunCommand() ");
Console.WriteLine(stopwatch.ElapsedMilliseconds + "milliseconds");
Console.ReadLine();
}
}
}
Code execution results:
sshNet.Connect() 531milliseconds
sshNet.RunCommand() 320milliseconds
sharpSsh.Connect() 305milliseconds
sharpSsh.RunCommand() 268milliseconds
mySshClient_SshNet.Connect() 1369milliseconds
mySshClient_SshNet.RunCommand() 52milliseconds
mySshClient_SharpSsh.Connect() 454milliseconds
mySshClient_SharpSsh.RunCommand() 9milliseconds
from ssh.net.
Thanks for taking time to provide all the info.
I'll look into it this evening or this weekend.
from ssh.net.
I'm also having some performance issues using a ShellStream - did you find time to look into this @drieseng ?
from ssh.net.
What version of SharpSSH did you use? I tried version 1.1.1.13, but that version does not have GetInputStream() and GetOutputStream() methods on SshStream.
from ssh.net.
These two methods are my extension methods in SshSteam:
public Stream GetInputStream()
{
return m_in;
}
public Stream GetOutputStream()
{
return m_out;
}
I hope it can help you :-)
from ssh.net.
These are the results I get when run against the develop branch (which is almost the same as 2016.1.0-beta1):
sshNet.Connect() 113 milliseconds
sshNet.RunCommand() 101 milliseconds
sharpSsh.Connect() 155 milliseconds
sharpSsh.RunCommand() 114 milliseconds
mySshClient_SshNet.Connect() 203 milliseconds
mySshClient_SshNet.RunCommand() 10 milliseconds
mySshClient_SharpSsh.Connect() 344 milliseconds
mySshClient_SharpSsh.RunCommand() 10 milliseconds
What type of system did you run your test on?
Both SSH.NET and SharpSSH are substantially faster in my test.
What version of SSH.NET did you test against, and - if you built from source - did you build the release configuration?
Please re-run your test against a recent officially packaged version of SSH.NET.
from ssh.net.
There's also some weird stuff in the code you provided.
For example:
In MySshClient.RunCommand(string command)
, you're actually wrting the literal "command" to the underlying stream.
from ssh.net.
In my case, I am using 2016.0.0 and I am using the CreateShellStream-method. The weird thing is that I am configuring a router, and when I am using NetConfClient (that also uses an SSHStream if I am not misunderstanding something) my commands get finished in 300-1000ms, while using SSH CLI for the same commands it takes either ~7s or ~15s - strangely nothing inbetween, it's always either 7 or 15s.
This is my code :
this.client = new SshClient(connectionInfo);
this.client.Connect();
// http://stackoverflow.com/questions/30883237/how-to-run-commands-in-cmd-process
stream = client.CreateShellStream("", 0, 0, 0, 0, 1048);
reader = new StreamReader(stream);
writer = new StreamWriter(stream);
public string sendCommand(string customCmd)
{
_writeStream($"show clock\r\n{customCmd}\r\nshow clock\r\nexec echo \"MMS_END_COMMAND\"");
return ReadStream(reader);
}
private void _writeStream(string cmd)
{
writer.WriteLine(cmd);
writer.Flush();
}
private string ReadStream(StreamReader reader)
{
var result = new StringBuilder();
var lineBuilder = new StringBuilder();
while (true)
{
var i = reader.Read();
if (i >= 0)
{
var c = (char) i;
lineBuilder.Append(c);
if (c != '\n') continue;
var line = lineBuilder.ToString();
lineBuilder = new StringBuilder();
if (line.EndsWith("echo \"MMS_END_COMMAND\"\r\n")) continue;
if (line == "MMS_END_COMMAND\r\n") break;
result.Append(line);
}
else
{
// It doesn't matter if I sleep here or if I don't.
Thread.Sleep(10);
}
}
return result.ToString();
}
I am sending 200 commands seperated by \r\n - it doesn't make a difference if I write them one by one or all together. It seems like it has to do with the Writer, as reading the list I set gets finished in 500ms.
I will try with the 2016.1.0-beta1 now.
/edit: Same behavior with 2016.1.0-beta1
from ssh.net.
Hmm ok, forget what I wrote (sorry) - if I replace my commands with echo-commands it finishes in 1138ms - it seems like the internal behavior for the operations I perform (setting ACL-entries) of the router is causing my issues :/
P.S.: Letting my code stay there for a while, maybe the Op could try to use some of it and see if his problems persist.
from ssh.net.
Do you still get the same results with the code you originally provided?
In which case we need to dig deeper as the issue is probably not in ShellStream itself.
If not, I propose we close this issue.
from ssh.net.
Related Issues (20)
- Able to Connect but Unable to Execute command HOT 14
- Unable to connect [email protected] or GCM HOT 1
- Support for RSA 4096-bit modulus? HOT 2
- [SECURITY] Fedora 19 Update: bugzilla-4.2.11-1.fc19 HOT 1
- updates at fedoraproject.org
- Well HOT 2
- CHM file not in Asset list on latest release. (2024.0.0) HOT 4
- RSA Cipher Encrypt method may truncate leading byte from created Signature, if that byte is 00, causing server validation failure HOT 2
- Q. Any chance this would work from a web-assembly app? HOT 2
- Multifactor SSH Client HOT 4
- SftpClient: how to resume upload/download? I.e. process SFTP command 'reput' & 'reget' HOT 2
- Update from 2020.0.2 to 2024.0.0, could not load System.Security.Cryptography.ECCurve HOT 2
- SSH.Net 2024.0.0 works for UWP but does not work for Android and iOS HOT 1
- SftpClient.ConnectAsync hangs HOT 5
- System.NotSupportedException: Request 'ping' is not supported. HOT 6
- Missing async methods.
- Support creating shell without PTY HOT 3
- SftpClient upload file doesn't work in UWP and Console applications. HOT 2
- System.Net.Sockets.SocketException (0x80004005): A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond HOT 1
- Typo: SftpFileSytemInformation
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ssh.net.