Fix disconnect and hung issues.

This commit is contained in:
olegkap_cp
2010-10-04 19:03:49 +00:00
parent e436a98242
commit 6ce6b45a7c
+71 -56
View File
@@ -76,7 +76,7 @@ namespace Renci.SshClient
/// <summary>
/// WaitHandle to signal that listner ended
/// </summary>
private EventWaitHandle _listenerWaitHandle = new AutoResetEvent(false);
private EventWaitHandle _listenerWaitHandle = new ManualResetEvent(false);
/// <summary>
/// Keeps track of all open channels
@@ -94,9 +94,10 @@ namespace Renci.SshClient
private bool _isAuthenticated;
/// <summary>
/// Specifies weither Disconnect method was called
/// Specifies DisconnectMessage that was sent by the client to the server
/// </summary>
private bool _isDisconnecting;
private DisconnectMessage _disconnectMessage;
/// <summary>
/// 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
/// <returns>New channel of specified type</returns>
public T CreateChannel<T>() 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
/// </summary>
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;
}
}
/// <summary>
@@ -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
/// <param name="message">The message.</param>
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
/// <remarks>Initialization required when same session object being reconnected</remarks>
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;
}
/// <summary>
@@ -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;
}
/// <summary>
@@ -863,11 +890,11 @@ namespace Renci.SshClient
/// </summary>
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;
}
}