diff --git a/Renci.SshClient/Renci.SshClient/Session.cs b/Renci.SshClient/Renci.SshClient/Session.cs
index 586f893b..888a7801 100644
--- a/Renci.SshClient/Renci.SshClient/Session.cs
+++ b/Renci.SshClient/Renci.SshClient/Session.cs
@@ -26,9 +26,8 @@ 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);
- //socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1000 * 15);
- //socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1000 * 5);
// Get server version from the server,
// ignore text lines which are sent before if any
@@ -72,6 +71,11 @@ namespace Renci.SshClient
private Exception _exceptionToThrow;
+ ///
+ /// Specifies that disconnect command was issued by the client
+ ///
+ private bool _isDisconnectByClient;
+
public event EventHandler MessageReceived;
protected HMAC ServerMac { get; private set; }
@@ -86,6 +90,23 @@ namespace Renci.SshClient
protected Compression ClientCompression { get; private set; }
+ ///
+ /// Gets a value indicating whether socket connected.
+ ///
+ ///
+ /// true if socket connected; otherwise, false.
+ ///
+ protected bool IsSocketConnected
+ {
+ get
+ {
+ if (this._socket == null)
+ return false;
+
+ return !(this._socket.Poll(1000, SelectMode.SelectRead) & (this._socket.Available == 0));
+ }
+ }
+
// TODO: Consider refactor to make setter private
public IEnumerable SessionId { get; set; }
@@ -95,21 +116,17 @@ namespace Renci.SshClient
public ConnectionInfo ConnectionInfo { get; private set; }
- public bool IsConnected
- {
- get
- {
- // TODO: Add condition that user is authenticated
- // TODO: Add condition that connection was not terminated byt MSG_DISCONNECT
- return this._socket.Connected;
- }
- }
-
protected Session(ConnectionInfo connectionInfo, Socket socket, string serverVersion)
{
this.ConnectionInfo = connectionInfo;
this._socket = socket;
- //this._socket.ReceiveTimeout = this._waitTimeout;
+ this._socket.NoDelay = true;
+ this._socket.Blocking = true;
+ //this._socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1000);
+ //this._socket.ReceiveTimeout = 1000;
+ //this._socket.LingerState = new LingerOption(false, 1);
+ //this._socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
+ //this._socket.IOControl(IOControlCode.KeepAliveValues, _inValue, _outvalue);
this.ServerVersion = serverVersion;
this.ClientVersion = string.Format("SSH-2.0-Renci.SshClient.{0}", this.GetType().Assembly.GetName().Version);
@@ -123,7 +140,7 @@ namespace Renci.SshClient
public T CreateChannel() where T : Channel
{
- if (!this._socket.Connected)
+ if (!this.IsSocketConnected)
{
throw new InvalidOperationException("Not connected");
}
@@ -143,6 +160,8 @@ namespace Renci.SshClient
Message.RegisterMessageType(MessageTypes.KeyExchangeInit);
Message.RegisterMessageType(MessageTypes.NewKeys);
+ this._isDisconnectByClient = false;
+
// Start incoming request listener
this._messageListener = new BackgroundWorker();
this._messageListener.DoWork += MessageListener_DoWork;
@@ -187,21 +206,21 @@ namespace Renci.SshClient
waitHandle,
};
- //EventWaitHandle.WaitAny(waitHandles);
+ EventWaitHandle.WaitAny(waitHandles);
- var index = EventWaitHandle.WaitAny(waitHandles, this._waitTimeout);
+ //var index = EventWaitHandle.WaitAny(waitHandles, this._waitTimeout);
- if (this._exceptionToThrow != null)
- {
- var exception = this._exceptionToThrow;
- this._exceptionToThrow = null;
- throw exception;
- }
- else if (index > waitHandles.Length)
- {
- // TODO: Issue timeout disconnect message if approapriate
- throw new TimeoutException();
- }
+ //if (this._exceptionToThrow != null)
+ //{
+ // var exception = this._exceptionToThrow;
+ // this._exceptionToThrow = null;
+ // throw exception;
+ //}
+ //else if (index > waitHandles.Length)
+ //{
+ // // TODO: Issue timeout disconnect message if approapriate
+ // throw new TimeoutException();
+ //}
}
protected abstract Message ReceiveMessage();
@@ -302,8 +321,10 @@ namespace Renci.SshClient
protected void Disconnect(DisconnectReasonCodes reasonCode, string message)
{
- if (this._socket != null && this._socket.Connected)
+ if (this.IsSocketConnected)
{
+ this._isDisconnectByClient = true;
+
this.SendMessage(new DisconnectMessage
{
ReasonCode = reasonCode,
@@ -320,23 +341,12 @@ namespace Renci.SshClient
SocketError socketErrorCode = SocketError.Success;
- int bytesRead = 0;
-
- while (bytesRead == 0 && this._socket.Connected && socketErrorCode == SocketError.Success)
- {
- var asynchResult = this._socket.BeginReceive(buffer, 0, length, SocketFlags.None, null, null);
-
- while (!asynchResult.IsCompleted && this._socket.Connected)
- {
- asynchResult.AsyncWaitHandle.WaitOne(this._waitTimeout);
- }
- bytesRead = this._socket.EndReceive(asynchResult, out socketErrorCode);
- }
+ int bytesRead = this._socket.Receive(buffer, 0, length, SocketFlags.None, out socketErrorCode);
// Check for errors
- if (!this._socket.Connected)
+ if (!this.IsSocketConnected)
{
- // If socket was closed throw an exception
+ // Connection was terminated either by remote server or local client
throw new SocketException(995); // WSA_OPERATION_ABORTED
}
if (bytesRead != length && socketErrorCode == SocketError.Success)
@@ -356,14 +366,18 @@ namespace Renci.SshClient
// TODO: Make sure socket is connected
SocketError socketErrorCode;
- this._socket.Send(data, 0, data.Length, SocketFlags.None, out socketErrorCode);
+ int bytesSent = this._socket.Send(data, 0, data.Length, SocketFlags.None, out socketErrorCode);
// Check for socket errors
if (socketErrorCode != SocketError.Success)
{
throw new SocketException((int)socketErrorCode);
}
- else if (!this._socket.Connected)
+ else if (bytesSent < data.Length)
+ {
+ throw new InvalidOperationException(string.Format("Sent {0} bytes, expected {1}.", bytesSent, data.Length));
+ }
+ else if (!this.IsSocketConnected)
{
// If socket was closed throw an exception
throw new SocketException(995); // WSA_OPERATION_ABORTED
@@ -390,7 +404,7 @@ namespace Renci.SshClient
}
// Close socket connection if still open
- if (this._socket.Connected)
+ if (this.IsSocketConnected)
{
this._socket.Disconnect(false);
}
@@ -398,9 +412,11 @@ namespace Renci.SshClient
private void MessageListener_DoWork(object sender, DoWorkEventArgs e)
{
- while (this._socket.Connected)
+ Exception excpetion = null;
+
+ try
{
- try
+ while (this.IsSocketConnected)
{
dynamic message = this.ReceiveMessage();
@@ -415,21 +431,36 @@ namespace Renci.SshClient
// Raise an event that message received
this.RaiseMessageReceived(this, new MessageReceivedEventArgs(message));
}
- catch (Exception exp)
+ }
+ catch (SocketException exp)
+ {
+ // If disconnect was issued by the client then exit listnting loop
+ if (exp.ErrorCode == 995 && this._isDisconnectByClient)
{
- // 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());
-
- // Set exception that need to be thrown by the main thread
- this._exceptionToThrow = exp;
-
- this._exceptionWaitHandle.Set();
- // TODO: Set exp to some excpetion that can be thrown later by the main thread.
-
- break;
+ // Do nothing
}
+ else
+ {
+ // Set exception that need to be thrown by the main thread
+ excpetion = exp;
+ }
+ }
+ 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());
+
+ excpetion = exp;
+ }
+
+ if (excpetion != null)
+ {
+ // Set exception that need to be thrown by the main thread
+ this._exceptionToThrow = excpetion;
+
+ this._exceptionWaitHandle.Set();
}
}
diff --git a/Renci.SshClient/Renci.SshClient/SessionSSHv2.cs b/Renci.SshClient/Renci.SshClient/SessionSSHv2.cs
index ab4d8c4c..a521de6a 100644
--- a/Renci.SshClient/Renci.SshClient/SessionSSHv2.cs
+++ b/Renci.SshClient/Renci.SshClient/SessionSSHv2.cs
@@ -24,7 +24,7 @@ namespace Renci.SshClient
internal override void SendMessage(Message message)
{
- if (!this.IsConnected)
+ if (!this.IsSocketConnected)
return;
// TODO: Refactor so we lock only _outboundPacketSequence and _inboundPacketSequence relevant operations
@@ -96,7 +96,7 @@ namespace Renci.SshClient
protected override Message ReceiveMessage()
{
- if (!this.IsConnected)
+ if (!this.IsSocketConnected)
return null;
// No lock needed since all messages read by only one thread