diff --git a/Renci.SshClient/Renci.SshClient/Channels/Channel.cs b/Renci.SshClient/Renci.SshClient/Channels/Channel.cs index ea10adb4..f45fee22 100644 --- a/Renci.SshClient/Renci.SshClient/Channels/Channel.cs +++ b/Renci.SshClient/Renci.SshClient/Channels/Channel.cs @@ -1,6 +1,5 @@  using System; -using System.Text; using System.Threading; using Renci.SshClient.Common; using Renci.SshClient.Messages; @@ -22,10 +21,6 @@ namespace Renci.SshClient.Channels //private uint _maximumPacketSize = 0x4000; private uint _maximumPacketSize = 1024; - protected StringBuilder ChannelData { get; private set; } - - protected StringBuilder ChannelExtendedData { get; private set; } - public abstract ChannelTypes ChannelType { get; } public uint ClientChannelNumber { get; set; } @@ -62,8 +57,6 @@ namespace Renci.SshClient.Channels } this.Session = session; - this.ChannelData = new StringBuilder((int)this._initialWindowSize); - this.ChannelExtendedData = new StringBuilder((int)this._initialWindowSize); this.WindowSize = this._initialWindowSize; // Initial window size this.PacketSize = this._maximumPacketSize; // Maximum packet size } diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelAsyncResult.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelAsyncResult.cs new file mode 100644 index 00000000..e5f7bfad --- /dev/null +++ b/Renci.SshClient/Renci.SshClient/Channels/ChannelAsyncResult.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading; + +namespace Renci.SshClient.Channels +{ + public class ChannelAsyncResult : IAsyncResult + { + /// + /// Gets or sets the channel that async result was created for. + /// + /// The channel. + internal ChannelExec Channel { get; private set; } + + public int BytesReceived { get; set; } + + #region IAsyncResult Members + + public object AsyncState { get; internal set; } + + public WaitHandle AsyncWaitHandle { get; internal set; } + + public bool CompletedSynchronously { get; internal set; } + + public bool IsCompleted { get; internal set; } + + #endregion + + internal ChannelAsyncResult(ChannelExec channel) + { + this.Channel = channel; + } + } +} diff --git a/Renci.SshClient/Renci.SshClient/Channels/ChannelExec.cs b/Renci.SshClient/Renci.SshClient/Channels/ChannelExec.cs index 60372669..3a0c6c46 100644 --- a/Renci.SshClient/Renci.SshClient/Channels/ChannelExec.cs +++ b/Renci.SshClient/Renci.SshClient/Channels/ChannelExec.cs @@ -10,14 +10,16 @@ namespace Renci.SshClient.Channels internal class ChannelExec : Channel { - private EventWaitHandle _channelExecutionWaitHandle = new AutoResetEvent(false); - private Stream _channelData; private Stream _channelExtendedData; private Exception _exception; + private ChannelAsyncResult _asyncResult; + + private AsyncCallback _callback; + public override ChannelTypes ChannelType { get { return ChannelTypes.Session; } @@ -28,8 +30,24 @@ namespace Renci.SshClient.Channels { } - internal void Execute(string command, Stream output, Stream extendedOutput) + internal ChannelAsyncResult BeginExecute(string command, Stream output, Stream extendedOutput, AsyncCallback callback, object state) { + // Prevent from executing BeginExecute before calling EndExecute + if (this._asyncResult != null) + { + throw new InvalidOperationException(""); + } + + // Create new AsyncResult object + this._asyncResult = new ChannelAsyncResult(this) + { + AsyncWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset), + IsCompleted = false, + AsyncState = state, + }; + + this._callback = callback; + this._channelData = output; this._channelExtendedData = extendedOutput; @@ -44,11 +62,25 @@ namespace Renci.SshClient.Channels Command = command, }); + return _asyncResult; + } - this.Session.WaitHandle(this._channelExecutionWaitHandle); + internal void EndExecute(IAsyncResult result) + { + ChannelAsyncResult channelAsyncResult = result as ChannelAsyncResult; + + if (channelAsyncResult.Channel != this) + { + throw new InvalidOperationException("Invalid IAsyncResult parameter"); + } + + //Make sure that operation completed if not wait for it to finish + this._asyncResult.AsyncWaitHandle.WaitOne(); this.Close(); + this._asyncResult = null; + if (this._exception != null) { var exception = this._exception; @@ -61,14 +93,14 @@ namespace Renci.SshClient.Channels { base.OnChannelEof(); - this._channelExecutionWaitHandle.Set(); + this.ExecutionCompleted(); } protected override void OnChannelFailed(uint reasonCode, string description) { base.OnChannelFailed(reasonCode, description); this._exception = new InvalidOperationException(string.Format("Channel failed to open. Code: {0}, Reason {1}", reasonCode, description)); - this._channelExecutionWaitHandle.Set(); + this.ExecutionCompleted(); } protected override void OnChannelData(string data) @@ -92,10 +124,14 @@ namespace Renci.SshClient.Channels } } - private void Init() + private void ExecutionCompleted() { - this.ChannelData.Length = 0; - this.ChannelExtendedData.Length = 0; + this._asyncResult.IsCompleted = true; + if (this._callback != null) + { + this._callback(this._asyncResult); + } + ((EventWaitHandle)_asyncResult.AsyncWaitHandle).Set(); } } } diff --git a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj index fe73c195..72e90aeb 100644 --- a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj +++ b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj @@ -44,6 +44,7 @@ + diff --git a/Renci.SshClient/Renci.SshClient/Session.cs b/Renci.SshClient/Renci.SshClient/Session.cs index 5d2cdc0d..2558c177 100644 --- a/Renci.SshClient/Renci.SshClient/Session.cs +++ b/Renci.SshClient/Renci.SshClient/Session.cs @@ -27,7 +27,13 @@ namespace Renci.SshClient var ep = new IPEndPoint(Dns.GetHostAddresses(connectionInfo.Host)[0], connectionInfo.Port); var socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); socket.ExclusiveAddressUse = true; - socket.Connect(ep); + + // Connect socket with 5 seconds timeout + var connectResult = socket.BeginConnect(ep, null, null); + + connectResult.AsyncWaitHandle.WaitOne(1000 * 15); + + socket.EndConnect(connectResult); // Get server version from the server, // ignore text lines which are sent before if any @@ -208,9 +214,9 @@ namespace Renci.SshClient internal abstract void SendMessage(Message message); - internal void WaitHandle(EventWaitHandle waitHandle) + internal void WaitHandle(WaitHandle waitHandle) { - var waitHandles = new EventWaitHandle[] + var waitHandles = new WaitHandle[] { this._disconnectWaitHandle, this._exceptionWaitHandle, diff --git a/Renci.SshClient/Renci.SshClient/Shell.cs b/Renci.SshClient/Renci.SshClient/Shell.cs index 416ae228..94db9541 100644 --- a/Renci.SshClient/Renci.SshClient/Shell.cs +++ b/Renci.SshClient/Renci.SshClient/Shell.cs @@ -1,4 +1,5 @@  +using System; using System.IO; using System.Text; using Renci.SshClient.Channels; @@ -15,28 +16,41 @@ namespace Renci.SshClient public string Execute(string command) { - //var channel = new ChannelSession(this._session); - - MemoryStream resultStream = new MemoryStream(); - - - var channel = this._session.CreateChannel(); - - channel.Execute(command, resultStream, null); - - return Encoding.ASCII.GetString(resultStream.ToArray()); + return this.Execute(command, null); } public string Execute(string command, Stream extended) { MemoryStream resultStream = new MemoryStream(); - var channel = this._session.CreateChannel(); - - channel.Execute(command, resultStream, extended); + this.Execute(command, resultStream, extended); return Encoding.ASCII.GetString(resultStream.ToArray()); } + public void Execute(string command, Stream output, Stream extended) + { + this.EndExecute(this.BeginExecute(command, output, extended, null, null)); + } + + public IAsyncResult BeginExecute(string command, Stream output, AsyncCallback callback, object state) + { + return this.BeginExecute(command, output, null, callback, state); + } + + public IAsyncResult BeginExecute(string command, Stream output, Stream extendedOutput, AsyncCallback callback, object state) + { + var channel = this._session.CreateChannel(); + + return channel.BeginExecute(command, output, extendedOutput, callback, state); + } + + public void EndExecute(IAsyncResult asynchResult) + { + ChannelAsyncResult channelAsyncResult = asynchResult as ChannelAsyncResult; + + channelAsyncResult.Channel.EndExecute(asynchResult); + } + } }