diff --git a/Renci.SshClient/Renci.SshClient/Session.cs b/Renci.SshClient/Renci.SshClient/Session.cs index d3dc56c1..af69fa1f 100644 --- a/Renci.SshClient/Renci.SshClient/Session.cs +++ b/Renci.SshClient/Renci.SshClient/Session.cs @@ -76,7 +76,7 @@ namespace Renci.SshClient /// /// WaitHandle to signal that listner ended /// - private EventWaitHandle _listenerWaitHandle = new AutoResetEvent(false); + private EventWaitHandle _listenerWaitHandle = new ManualResetEvent(false); /// /// Keeps track of all open channels @@ -94,9 +94,10 @@ namespace Renci.SshClient private bool _isAuthenticated; /// - /// Specifies weither Disconnect method was called + /// Specifies DisconnectMessage that was sent by the client to the server /// - private bool _isDisconnecting; + private DisconnectMessage _disconnectMessage; + /// /// holds number to be used for session channels @@ -113,7 +114,7 @@ namespace Renci.SshClient { get { - return this._socket != null && this._socket.Connected && this._isAuthenticated; + return this._socket != null && this._socket.Connected && this._isAuthenticated && this._messageListener.Status == TaskStatus.Running; } } @@ -202,26 +203,25 @@ namespace Renci.SshClient /// New channel of specified type public T CreateChannel() where T : Channel { + // Gets next available channel number or waits for one to become available + var clientChannelId = this._channelNumbers.WaitAndPop(); + if (!this.IsConnected) { throw new InvalidOperationException("Not connected"); } - T channel; - // Gets newxt available channel number or waits for one to become available - var clientChannelId = this._channelNumbers.WaitAndPop(); - lock (this._openChannels) { - channel = _channels[typeof(T)](this, clientChannelId) as T; + var channel = _channels[typeof(T)](this, clientChannelId) as T; channel.Closed += Channel_Closed; channel.OpenFailed += Channel_Closed; this._openChannels.Add(clientChannelId, channel); - } - return channel; + return channel; + } } public void Connect() @@ -379,11 +379,14 @@ namespace Renci.SshClient /// public void Disconnect() { - this._isDisconnecting = true; - this.Disconnect(DisconnectReasonCodes.ByApplication, "Connection terminated by the client."); - this.DisconnectCleanup(); + if (this._messageListener != null) + { + // Wait for listner task to finish + this._messageListener.Wait(); + this._messageListener = null; + } } /// @@ -405,12 +408,16 @@ namespace Renci.SshClient #else index = EventWaitHandle.WaitAny(waitHandles, this.ConnectionInfo.Timeout); #endif - if (this._exceptionToThrow != null) + if (index == 0 && this._exceptionToThrow != null) { var exception = this._exceptionToThrow; this._exceptionToThrow = null; throw exception; } + else if (index == 1) + { + throw new SshException("Connection was terminated."); + } else if (index > waitHandles.Length) { this.Disconnect(DisconnectReasonCodes.ByApplication, "Operation timeout"); @@ -683,15 +690,15 @@ namespace Renci.SshClient /// The message. protected void Disconnect(DisconnectReasonCodes reasonCode, string message) { - if (this.IsConnected) + if (this._disconnectMessage == null) { - this.SendMessage(new DisconnectMessage - { - ReasonCode = reasonCode, - Description = message, - }); + this._disconnectMessage = new DisconnectMessage + { + ReasonCode = reasonCode, + Description = message, + }; - this.DisconnectCleanup(); + this.SendMessage(this._disconnectMessage); } } @@ -704,12 +711,22 @@ namespace Renci.SshClient { var buffer = new byte[length]; var offset = 0; - int received = 0; // how many bytes is already received + int receivedTotal = 0; // how many bytes is already received do { try { - received += this._socket.Receive(buffer, offset + received, length - received, SocketFlags.None); + var receivedBytes = this._socket.Receive(buffer, offset + receivedTotal, length - receivedTotal, SocketFlags.None); + if (receivedBytes > 0) + { + receivedTotal += receivedBytes; + continue; + } + else + { + this._socket.Disconnect(true); + throw new SshException("An established connection was aborted by the software in your host machine."); + } } catch (SocketException exp) { @@ -723,7 +740,7 @@ namespace Renci.SshClient else throw; // any serious error occurr } - } while (received < length); + } while (receivedTotal < length); return buffer; @@ -815,6 +832,14 @@ namespace Renci.SshClient /// Initialization required when same session object being reconnected private void Initialize() { + // Make sure that message listner finished all work if it was created + // before initializing connection again + if (this._messageListener != null) + { + this._messageListener.Wait(); + this._messageListener = null; + } + // Initialize session this._outboundPacketSequence = 0; this._inboundPacketSequence = 0; @@ -827,7 +852,7 @@ namespace Renci.SshClient this.ServerVersion = null; this._keyExhcange = null; this._isAuthenticated = false; - this._isDisconnecting = false; + //this._disconnectMessage = null; } /// @@ -836,12 +861,19 @@ namespace Renci.SshClient private void DisconnectCleanup() { // Close all open channels if any - if (this.IsConnected) + lock (this._openChannels) { foreach (var channel in this._openChannels.Values) { - channel.Close(); + this._channelNumbers.Push(channel.ClientChannelNumber); + + // TODO: See if possible redesign to avoid "connected" check at this place + if (this.IsConnected) + { + channel.Close(); + } } + this._openChannels.Clear(); } // Close socket connection if still open @@ -850,12 +882,7 @@ namespace Renci.SshClient this._socket.Close(); } - if (this._messageListener != null) - { - // Wait for listner task to finish - this._messageListener.Wait(); - this._messageListener = null; - } + this._disconnectMessage = null; } /// @@ -863,11 +890,11 @@ namespace Renci.SshClient /// private void MessageListener() { - while (this._socket.Connected) + while (true) { try { - var message = this.ReceiveMessage(); + var message = this._disconnectMessage ?? this.ReceiveMessage(); if (message == null) { @@ -876,7 +903,7 @@ namespace Renci.SshClient else if (message is DisconnectMessage) { // Always handle disconnect message first - this.HandleMessage(message); + this.HandleMessage((dynamic)message); break; // Exit message listener loop, no more messages should be handled } else if (this._keyExhcange.InProgress) @@ -904,32 +931,20 @@ namespace Renci.SshClient this._exceptionToThrow = exp; this._exceptionWaitHandle.Set(); + } catch (Exception exp) { // TODO: This exception can be swolloed if it occures while running in the background, look for possible solutions - // Ignore this error since socket was disconected - if (exp is SocketException && ((SocketException)exp).SocketErrorCode == SocketError.ConnectionAborted && this._isDisconnecting) - { - // Do nothing since connection was disconnected by the client - } - else - { - // In case of error issue disconntect command - this.Disconnect(DisconnectReasonCodes.ByApplication, exp.ToString()); + this.Disconnect(DisconnectReasonCodes.ByApplication, exp.ToString()); - this._exceptionToThrow = exp; + this._exceptionToThrow = exp; - this._exceptionWaitHandle.Set(); - } - - // Ensure socket is disconnected - this._socket.Close(); + this._exceptionWaitHandle.Set(); } } - this._listenerWaitHandle.Set(); } @@ -948,7 +963,7 @@ namespace Renci.SshClient #region IDisposable Members - private bool disposed = false; + private bool _disposed = false; public void Dispose() { @@ -960,7 +975,7 @@ namespace Renci.SshClient private void Dispose(bool disposing) { // Check to see if Dispose has already been called. - if (!this.disposed) + if (!this._disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. @@ -990,7 +1005,7 @@ namespace Renci.SshClient } // Note disposing has been done. - disposed = true; + this._disposed = true; } }