Fix socket reading problem, fix Connect/Disconnect issue and reuse of the object

This commit is contained in:
olegkap_cp
2010-09-01 15:17:37 +00:00
parent 4b8509e884
commit a875cee7e2
4 changed files with 118 additions and 68 deletions
@@ -259,12 +259,12 @@ namespace Renci.SshClient.Channels
this.LocalWindowSize -= (uint)messageData.Length;
// Adjust window if window size is too low
if (this.LocalWindowSize < 1)
if (this.LocalWindowSize < this.PacketSize)
{
this.SendMessage(new ChannelWindowAdjustMessage
{
ChannelNumber = this.ServerChannelNumber,
BytesToAdd = this._initialWindowSize,
BytesToAdd = this._initialWindowSize - this.LocalWindowSize,
});
this.LocalWindowSize = this._initialWindowSize;
}
@@ -26,7 +26,7 @@ namespace Renci.SshClient.Channels
}
public ChannelExec(Session session, uint channelId)
: base(session, channelId, 0x010000, 0x8000)
: base(session, channelId, 0x100000, 0x8000)
{
}
+115 -64
View File
@@ -60,27 +60,27 @@ namespace Renci.SshClient
/// <summary>
/// WaitHandle to signal that key exchange was finished, weither it was succesfull or not.
/// </summary>
private EventWaitHandle _keyExhangedFinishedWaitHandle = new AutoResetEvent(false);
private EventWaitHandle _keyExhangedFinishedWaitHandle;
/// <summary>
/// WaitHandle to signale that last service request was accepted
/// </summary>
private EventWaitHandle _serviceAccepted = new AutoResetEvent(false);
private EventWaitHandle _serviceAccepted;
/// <summary>
/// WaitHandle to signal that exception was thrown by another thread.
/// </summary>
private EventWaitHandle _exceptionWaitHandle = new AutoResetEvent(false);
private EventWaitHandle _exceptionWaitHandle;
/// <summary>
/// WaitHandle to signal that listner ended
/// </summary>
private EventWaitHandle _listenerWaitHandle = new AutoResetEvent(false);
private EventWaitHandle _listenerWaitHandle;
/// <summary>
/// Keeps track of all open channels
/// </summary>
private Dictionary<uint, Channel> _openChannels = new Dictionary<uint, Channel>();
private Dictionary<uint, Channel> _openChannels;
/// <summary>
/// Exception that need to be thrown by waiting thread
@@ -100,7 +100,7 @@ namespace Renci.SshClient
/// <summary>
/// holds number to be used for session channels
/// </summary>
private BlockingStack<uint> _channelNumbers = new BlockingStack<uint>();
private BlockingStack<uint> _channelNumbers;
/// <summary>
/// Occurs when new message received.
@@ -182,12 +182,6 @@ namespace Renci.SshClient
{
this.ConnectionInfo = connectionInfo;
this.ClientVersion = string.Format("SSH-2.0-Renci.SshClient.{0}", this.GetType().Assembly.GetName().Version);
// Prepopulate chanel numbers that will be used
for (int i = connectionInfo.MaxSessions - 1; i > 0; i--)
{
this._channelNumbers.Push((uint)i - 1 + 100);
}
}
private static IDictionary<Type, Func<Session, uint, Channel>> _channels = new Dictionary<Type, Func<Session, uint, Channel>>()
@@ -256,6 +250,15 @@ namespace Renci.SshClient
if (this.IsConnected)
return;
this.Initialize();
// Prepopulate chanel numbers that will be used
for (int i = connectionInfo.MaxSessions - 1; i > 0; i--)
{
this._channelNumbers.Push((uint)i - 1);
}
var ep = new IPEndPoint(Dns.GetHostAddresses(connectionInfo.Host)[0], connectionInfo.Port);
this._socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
@@ -513,19 +516,13 @@ namespace Renci.SshClient
// Read rest of the packet data
int bytesToRead = (int)(packetLength - (blockSize - 4));
while (bytesToRead > 0)
if (this.ServerCipher == null)
{
data = new List<byte>(this.Read(blockSize));
if (this.ServerCipher == null)
{
decryptedData.AddRange(data);
}
else
{
decryptedData.AddRange(this.ServerCipher.Decrypt(data));
}
bytesToRead -= blockSize;
decryptedData.AddRange(this.Read(bytesToRead));
}
else
{
decryptedData.AddRange(this.ServerCipher.Decrypt(this.Read(bytesToRead)));
}
// Validate message against MAC
@@ -704,29 +701,28 @@ namespace Renci.SshClient
protected byte[] Read(int length)
{
var buffer = new byte[length];
var totalBytesRead = 0;
while (totalBytesRead < length)
var offset = 0;
int received = 0; // how many bytes is already received
do
{
int bytesRead = this._socket.Receive(buffer, length, SocketFlags.None);
try
{
received += this._socket.Receive(buffer, offset + received, length - received, SocketFlags.None);
}
catch (SocketException exp)
{
if (exp.SocketErrorCode == SocketError.WouldBlock ||
exp.SocketErrorCode == SocketError.IOPending ||
exp.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
{
// socket buffer is probably empty, wait and try again
Thread.Sleep(30);
}
else
throw exp; // any serious error occurr
}
} while (received < length);
if (bytesRead == 0)
{
this._socket.Close();
throw new IOException("Unable to read data to the transport connection: An established connection was aborted by the software in your host machine");
}
if (bytesRead > 0)
{
totalBytesRead += bytesRead;
}
else
{
totalBytesRead = bytesRead;
break;
}
}
return buffer;
}
@@ -737,7 +733,29 @@ namespace Renci.SshClient
/// <param name="data">The data.</param>
protected void Write(byte[] data)
{
this._socket.Send(data);
int sent = 0; // how many bytes is already sent
int length = data.Length;
do
{
try
{
sent += this._socket.Send(data, sent, length - sent, SocketFlags.None);
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.WouldBlock ||
ex.SocketErrorCode == SocketError.IOPending ||
ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
{
// socket buffer is probably full, wait and try again
Thread.Sleep(30);
}
else
throw ex; // any serious error occurr
}
} while (sent < length);
}
/// <summary>
@@ -798,6 +816,36 @@ namespace Renci.SshClient
#endregion
/// <summary>
/// Initializes this session.
/// </summary>
/// <remarks>Initialization required when same session object being reconnected</remarks>
private void Initialize()
{
// TODO: Consider reseting objects to initial stage instead of creating a new one
// Initialize session
this._outboundPacketSequence = 0;
this._inboundPacketSequence = 0;
this._openChannels = new Dictionary<uint, Channel>();
this._keyExhangedFinishedWaitHandle = new AutoResetEvent(false);
this._serviceAccepted = new AutoResetEvent(false);
this._exceptionWaitHandle = new AutoResetEvent(false);
this._listenerWaitHandle = new AutoResetEvent(false);
this._channelNumbers = new BlockingStack<uint>();
this._exceptionToThrow = null;
this.SessionId = null;
this.ServerVersion = null;
this._keyExhcange = null;
this.ServerMac = null;
this.ClientMac = null;
this.ClientCipher = null;
this.ServerCipher = null;
this.ServerDecompression = null;
this.ClientCompression = null;
this._isAuthenticated = false;
this._isDisconnecting = false;
}
private bool ValidateHash(List<byte> decryptedData, byte[] serverHash, uint packetSequence)
{
var clientHashData = new List<byte>();
@@ -831,6 +879,12 @@ namespace Renci.SshClient
{
this._socket.Close();
}
if (this._messageListener != null)
{
this._messageListener.Wait();
this._messageListener = null;
}
}
/// <summary>
@@ -856,30 +910,27 @@ namespace Renci.SshClient
this.RaiseMessageReceived(this, new MessageReceivedEventArgs(message));
}
}
catch (IOException exp)
{
// Ignore this error since socket was disconected
// Ensure socket is disconnected
this._socket.Close();
if (!this._isDisconnecting)
{
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
// In case of error issue disconntect command
this.Disconnect(DisconnectReasonCodes.ByApplication, exp.ToString());
// 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._exceptionToThrow = exp;
this._exceptionToThrow = exp;
this._exceptionWaitHandle.Set();
this._exceptionWaitHandle.Set();
}
// Ensure socket is disconnected
this._socket.Close();
}
this._listenerWaitHandle.Set();
-1
View File
@@ -76,6 +76,5 @@ namespace Renci.SshClient
channelAsyncResult.Channel.EndExecute(asynchResult);
}
}
}