我正在使用C#和SSH.NET开发类似Putty的用户交互式SSH UI,这是一个用于建立SSH连接的开源库。我遇到的问题是,我能够找到所有示例的工作方式,它们似乎创建了一个新连接,或者以非交互模式执行,这意味着在先前命令中执行的任何操作都不会延续到以下命令。
我基本上不习惯使用流 - 在SSH.NET库中有一些方法似乎是使用流打开shell,这样你就可以将流分配给stdin,stdout和stderr,但在尝试使用时它们,我找不到关于如何设置流以将它们提供给方法调用的好信息。
MemoryStream
还是TextStream
?以下是SSH.NET中CreateShell
类中SshClient
方法的简短定义:
public Shell CreateShell(Stream input, Stream output, Stream extendedOutput)
{
return CreateShell(input, output, extendedOutput, string.Empty, 0, 0, 0, 0, null, 1024);
}
使用的长覆盖版本:
public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModes, int bufferSize)
{
EnsureSessionIsOpen();
return new Shell(Session, input, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, bufferSize);
}
在设置正确的连接流之后,我该如何读取和写入它们?我是否需要设置StreamReader
和/或StreamWriter
来访问流?我已经这样做了,以便在使用StdErr
方法使用现有SshCommand
对象后阅读.Execute()
,但我不确定如何将其与预先存在的流绑定我创造的。
最后,还有另一个选项似乎与单个Stream
对象建立连接 - 但我不确定这是否仅用于单元测试,或者它是否适用于“正常”操作:
public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, IDictionary<TerminalModes, uint> terminalModeValues)
{
EnsureSessionIsOpen();
return ServiceFactory.CreateShellStream(Session, terminalName, columns, rows, width, height, terminalModeValues, bufferSize);
}
ShellStream : Stream
类看起来也是一个很好的候选者,覆盖Stream
,CanRead
,CanSeek
,{CanWrite
的基础Flush
类{1}},Length
,Read
,Write
等
以下是ShellStream
类的基本定义,包含属性和构造函数:
public class ShellStream : Stream
{
private const string CrLf = "\r\n";
private readonly ISession _session;
private readonly Encoding _encoding;
private readonly int _bufferSize;
private readonly Queue<byte> _incoming;
private readonly Queue<byte> _outgoing;
private IChannelSession _channel;
private AutoResetEvent _dataReceived = new AutoResetEvent(false);
private bool _isDisposed;
/// <summary>
/// Occurs when data was received.
/// </summary>
public event EventHandler<ShellDataEventArgs> DataReceived;
/// <summary>
/// Occurs when an error occurred.
/// </summary>
public event EventHandler<ExceptionEventArgs> ErrorOccurred;
/// <summary>
/// Gets a value that indicates whether data is available on the <see cref="ShellStream"/> to be read.
/// </summary>
/// <value>
/// <c>true</c> if data is available to be read; otherwise, <c>false</c>.
/// </value>
public bool DataAvailable
{
get
{
lock (_incoming)
{
return _incoming.Count > 0;
}
}
}
/// <summary>
/// Gets the number of bytes that will be written to the internal buffer.
/// </summary>
/// <value>
/// The number of bytes that will be written to the internal buffer.
/// </value>
internal int BufferSize
{
get { return _bufferSize; }
}
/// <summary>
/// Initializes a new <see cref="ShellStream"/> instance.
/// </summary>
/// <param name="session">The SSH session.</param>
/// <param name="terminalName">The <c>TERM</c> environment variable.</param>
/// <param name="columns">The terminal width in columns.</param>
/// <param name="rows">The terminal width in rows.</param>
/// <param name="width">The terminal height in pixels.</param>
/// <param name="height">The terminal height in pixels.</param>
/// <param name="terminalModeValues">The terminal mode values.</param>
/// <param name="bufferSize">The size of the buffer.</param>
internal ShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModeValues, int bufferSize)
{
_encoding = session.ConnectionInfo.Encoding;
_session = session;
_bufferSize = bufferSize;
_incoming = new Queue<byte>();
_outgoing = new Queue<byte>();
_channel = _session.CreateChannelSession();
_channel.DataReceived += Channel_DataReceived;
_channel.Closed += Channel_Closed;
_session.Disconnected += Session_Disconnected;
_session.ErrorOccured += Session_ErrorOccured;
_channel.Open();
_channel.SendPseudoTerminalRequest(terminalName, columns, rows, width, height, terminalModeValues);
_channel.SendShellRequest();
}