Explorar o código

Introduce TrySendMessage method on Session which will not let SocketException or SshException exception bubble up, but instead return false in this case.
Use TrySendMessage to send SSH_MSG_DISCONNECT, SSH_MSG_CHANNEL_EOF, SSH_MSG_CHANNEL_CLOSE and SSH_MSG_IGNORE messages to avoid throwing exceptions while closing/disposing or sending keep-alive messages.
Avoid sending multiple keep-alive messages concurrently for the same client.
Fixed regression in multi-factor authentication.

Gert Driesen %!s(int64=11) %!d(string=hai) anos
pai
achega
3d3d76fe52
Modificáronse 29 ficheiros con 1529 adicións e 108 borrados
  1. 69 0
      Renci.SshClient/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAlivesNotSentConcurrently.cs
  2. 8 6
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelDirectTcpipTest.cs
  3. 8 4
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelDirectTcpipTest_Close_SessionIsConnectedAndChannelIsOpen.cs
  4. 8 6
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Close_SessionIsConnectedAndChannelIsOpen.cs
  5. 4 4
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_Closed.cs
  6. 131 0
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived_SendChannelCloseMessageFailure.cs
  7. 131 0
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived_SendChannelCloseMessageSuccess.cs
  8. 128 0
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseReceived_SendChannelCloseMessageFailure.cs
  9. 5 2
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseReceived_SendChannelCloseMessageSuccess.cs
  10. 127 0
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelEofReceived_SendChannelCloseMessageFailure.cs
  11. 138 0
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelEofReceived_SendChannelCloseMessageSuccess.cs
  12. 9 5
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_NoChannelCloseOrChannelEofReceived.cs
  13. 142 0
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_NoChannelCloseOrChannelEofReceived_SendChannelEofMessageFailure.cs
  14. 127 0
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsNotConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived.cs
  15. 124 0
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsNotConnectedAndChannelIsOpen_ChannelCloseReceived.cs
  16. 119 0
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsNotConnectedAndChannelIsOpen_NoChannelCloseOrChannelEofReceived.cs
  17. 4 1
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_OnSessionChannelCloseReceived_SessionIsConnectedAndChannelIsOpen.cs
  18. 6 6
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Close_SessionIsConnectedAndChannelIsOpen_EofNotReceived.cs
  19. 5 5
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Close_SessionIsConnectedAndChannelIsOpen_EofReceived.cs
  20. 5 5
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelCloseReceived_SessionIsConnectedAndChannelIsOpen_EofNotReceived.cs
  21. 6 4
      Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelCloseReceived_SessionIsConnectedAndChannelIsOpen_EofReceived.cs
  22. 1 1
      Renci.SshClient/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_DifferentAllowedAuthenticationsAfterPartialSuccess.cs
  23. 57 0
      Renci.SshClient/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PostponePartialAccessAuthenticationMethod.cs
  24. 13 2
      Renci.SshClient/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
  25. 24 3
      Renci.SshClient/Renci.SshNet/BaseClient.cs
  26. 45 30
      Renci.SshClient/Renci.SshNet/Channels/Channel.cs
  27. 21 10
      Renci.SshClient/Renci.SshNet/ClientAuthentication.cs
  28. 14 5
      Renci.SshClient/Renci.SshNet/ISession.cs
  29. 50 9
      Renci.SshClient/Renci.SshNet/Session.cs

+ 69 - 0
Renci.SshClient/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAlivesNotSentConcurrently.cs

@@ -0,0 +1,69 @@
+using System;
+using System.Threading;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Messages.Transport;
+
+namespace Renci.SshNet.Tests.Classes
+{
+    [TestClass]
+    public class BaseClientTest_Connected_KeepAlivesNotSentConcurrently
+    {
+        private Mock<IServiceFactory> _serviceFactoryMock;
+        private Mock<ISession> _sessionMock;
+        private BaseClient _client;
+        private ConnectionInfo _connectionInfo;
+
+        [TestInitialize]
+        public void Setup()
+        {
+            Arrange();
+            Act();
+        }
+
+        [TestCleanup]
+        public void Cleanup()
+        {
+        }
+
+        protected void Arrange()
+        {
+            _connectionInfo = new ConnectionInfo("host", "user", new PasswordAuthenticationMethod("user", "pwd"));
+
+            _serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+
+            _serviceFactoryMock.Setup(p => p.CreateSession(_connectionInfo)).Returns(_sessionMock.Object);
+            _sessionMock.Setup(p => p.Connect());
+            _sessionMock.Setup(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()))
+                .Returns(true)
+                .Callback(() => Thread.Sleep(300));
+
+            _client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object)
+                {
+                    KeepAliveInterval = TimeSpan.FromMilliseconds(50d)
+                };
+            _client.Connect();
+        }
+
+        protected void Act()
+        {
+            // should keep-alive message be sent concurrently, then multiple keep-alive
+            // message would be sent during this sleep period
+            Thread.Sleep(200);
+        }
+
+        [TestMethod]
+        public void SendMessageOnSessionShouldBeInvokedOnce()
+        {
+            _sessionMock.Verify(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()), Times.Once);
+        }
+
+        private class MyClient : BaseClient
+        {
+            public MyClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory) : base(connectionInfo, ownsConnectionInfo, serviceFactory)
+            {
+            }
+        }
+    }
+}

+ 8 - 6
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelDirectTcpipTest.cs

@@ -158,7 +158,8 @@ namespace Renci.SshNet.Tests.Classes.Channels
                             _remoteWindowSize, _remotePacketSize, _remoteChannelNumber))));
             _sessionMock.Setup(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()))
                 .Callback<WaitHandle>(p => p.WaitOne(-1));
-            _sessionMock.Setup(p => p.SendMessage(It.IsAny<ChannelEofMessage>()))
+            _sessionMock.Setup(p => p.TrySendMessage(It.IsAny<ChannelEofMessage>()))
+                .Returns(true)
                 .Callback<Message>(
                     m => new Thread(() =>
                         {
@@ -166,7 +167,8 @@ namespace Renci.SshNet.Tests.Classes.Channels
                             _sessionMock.Raise(s => s.ChannelEofReceived += null,
                                 new MessageEventArgs<ChannelEofMessage>(new ChannelEofMessage(_localChannelNumber)));
                         }).Start());
-            _sessionMock.Setup(p => p.SendMessage(It.IsAny<ChannelCloseMessage>()))
+            _sessionMock.Setup(p => p.TrySendMessage(It.IsAny<ChannelCloseMessage>()))
+                .Returns(true)
                 .Callback<Message>(
                     m => new Thread(() =>
                         {
@@ -206,13 +208,13 @@ namespace Renci.SshNet.Tests.Classes.Channels
                 Assert.IsNotNull(handler);
                 Assert.IsFalse(handler.Connected);
 
-                _sessionMock.Verify(p => p.SendMessage(It.IsAny<ChannelEofMessage>()), Times.Once);
-                _sessionMock.Verify(p => p.SendMessage(It.IsAny<ChannelCloseMessage>()), Times.Once);
+                _sessionMock.Verify(p => p.TrySendMessage(It.IsAny<ChannelEofMessage>()), Times.Once);
+                _sessionMock.Verify(p => p.TrySendMessage(It.IsAny<ChannelCloseMessage>()), Times.Once);
 
                 channel.Close();
 
-                _sessionMock.Verify(p => p.SendMessage(It.IsAny<ChannelEofMessage>()), Times.Once);
-                _sessionMock.Verify(p => p.SendMessage(It.IsAny<ChannelCloseMessage>()), Times.Once);
+                _sessionMock.Verify(p => p.TrySendMessage(It.IsAny<ChannelEofMessage>()), Times.Once);
+                _sessionMock.Verify(p => p.TrySendMessage(It.IsAny<ChannelCloseMessage>()), Times.Once);
             }
         }
     }

+ 8 - 4
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelDirectTcpipTest_Close_SessionIsConnectedAndChannelIsOpen.cs

@@ -94,10 +94,14 @@ namespace Renci.SshNet.Tests.Classes.Channels
                     });
             _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
             _sessionMock.InSequence(sequence)
-                .Setup(p => p.SendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)));
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(true);
             _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
             _sessionMock.InSequence(sequence)
-                .Setup(p => p.SendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)));
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(true);
             _sessionMock.InSequence(sequence)
                 .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
                 .Callback<WaitHandle>(
@@ -180,13 +184,13 @@ namespace Renci.SshNet.Tests.Classes.Channels
         [TestMethod]
         public void ChannelEofMessageShouldBeSentOnce()
         {
-            _sessionMock.Verify(p => p.SendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
+            _sessionMock.Verify(p => p.TrySendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
         }
 
         [TestMethod]
         public void ChannelCloseMessageShouldBeSentOnce()
         {
-            _sessionMock.Verify(p => p.SendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
+            _sessionMock.Verify(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
         }
 
         [TestMethod]

+ 8 - 6
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Close_SessionIsConnectedAndChannelIsOpen.cs

@@ -89,10 +89,14 @@ namespace Renci.SshNet.Tests.Classes.Channels
                     ));
             _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
             _sessionMock.InSequence(sequence)
-                .Setup(p => p.SendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)));
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(true);
             _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
             _sessionMock.InSequence(sequence)
-                .Setup(p => p.SendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)));
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(true);
             _sessionMock.InSequence(sequence)
                 .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
                 .Callback<WaitHandle>(
@@ -141,9 +145,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
 
         private void Act()
         {
-            Console.WriteLine("Before Close=" + Thread.CurrentThread.ManagedThreadId + " | " + DateTime.Now.ToString("O"));
             _channel.Close();
-            Console.WriteLine("After Close=" + Thread.CurrentThread.ManagedThreadId + " | " + DateTime.Now.ToString("O"));
         }
 
         [TestMethod]
@@ -164,13 +166,13 @@ namespace Renci.SshNet.Tests.Classes.Channels
         [TestMethod]
         public void ChannelEofMessageShouldBeSentOnce()
         {
-            _sessionMock.Verify(p => p.SendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
+            _sessionMock.Verify(p => p.TrySendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
         }
 
         [TestMethod]
         public void ChannelCloseMessageShouldBeSentOnce()
         {
-            _sessionMock.Verify(p => p.SendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
+            _sessionMock.Verify(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
         }
 
         [TestMethod]

+ 4 - 4
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_Closed.cs

@@ -78,10 +78,10 @@ namespace Renci.SshNet.Tests.Classes.Channels
                     });
             _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
             _sessionMock.InSequence(sequence)
-                .Setup(p => p.SendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)));
+                .Setup(p => p.TrySendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber))).Returns(true);
             _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
             _sessionMock.InSequence(sequence)
-                .Setup(p => p.SendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)));
+                .Setup(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber))).Returns(true);
             _sessionMock.InSequence(sequence)
                 .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
                 .Callback<WaitHandle>(
@@ -113,13 +113,13 @@ namespace Renci.SshNet.Tests.Classes.Channels
         [TestMethod]
         public void ChannelEofMessageShouldBeSentOnce()
         {
-            _sessionMock.Verify(p => p.SendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
+            _sessionMock.Verify(p => p.TrySendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
         }
 
         [TestMethod]
         public void ChannelCloseMessageShouldBeSentOnce()
         {
-            _sessionMock.Verify(p => p.SendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
+            _sessionMock.Verify(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
         }
 
         [TestMethod]

+ 131 - 0
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived_SendChannelCloseMessageFailure.cs

@@ -0,0 +1,131 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Channels;
+using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Connection;
+
+namespace Renci.SshNet.Tests.Classes.Channels
+{
+    [TestClass]
+    public class ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived_SendChannelCloseMessageFailure
+    {
+        private Mock<ISession> _sessionMock;
+        private uint _localChannelNumber;
+        private uint _localWindowSize;
+        private uint _localPacketSize;
+        private uint _remoteChannelNumber;
+        private uint _remoteWindowSize;
+        private uint _remotePacketSize;
+        private IList<ChannelEventArgs> _channelClosedRegister;
+        private List<ExceptionEventArgs> _channelExceptionRegister;
+        private ChannelSession _channel;
+        private Mock<IConnectionInfo> _connectionInfoMock;
+        private MockSequence _sequence;
+        private SemaphoreLight _sessionSemaphore;
+        private int _initialSessionSemaphoreCount;
+
+        [TestInitialize]
+        public void Initialize()
+        {
+            Arrange();
+            Act();
+        }
+
+        private void Arrange()
+        {
+            var random = new Random();
+            _localChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _localWindowSize = (uint)random.Next(0, int.MaxValue);
+            _localPacketSize = (uint)random.Next(0, int.MaxValue);
+            _remoteChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _remoteWindowSize = (uint)random.Next(0, int.MaxValue);
+            _remotePacketSize = (uint)random.Next(0, int.MaxValue);
+            _channelClosedRegister = new List<ChannelEventArgs>();
+            _channelExceptionRegister = new List<ExceptionEventArgs>();
+            _initialSessionSemaphoreCount = random.Next(10, 20);
+            _sessionSemaphore = new SemaphoreLight(_initialSessionSemaphoreCount);
+
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+
+            _sequence = new MockSequence();
+            _sessionMock.InSequence(_sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
+            _connectionInfoMock.InSequence(_sequence).Setup(p => p.RetryAttempts).Returns(1);
+            _sessionMock.Setup(p => p.SessionSemaphore).Returns(_sessionSemaphore);
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p =>
+                        p.SendMessage(
+                            It.Is<ChannelOpenMessage>(
+                                m =>
+                                    m.LocalChannelNumber == _localChannelNumber &&
+                                    m.InitialWindowSize == _localWindowSize && m.MaximumPacketSize == _localPacketSize &&
+                                    m.Info is SessionChannelOpenInfo)));
+            _sessionMock.InSequence(_sequence)
+                .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
+                .Callback<WaitHandle>(
+                    w =>
+                    {
+                        _sessionMock.Raise(
+                            s => s.ChannelOpenConfirmationReceived += null,
+                            new MessageEventArgs<ChannelOpenConfirmationMessage>(
+                                new ChannelOpenConfirmationMessage(
+                                    _localChannelNumber,
+                                    _remoteWindowSize,
+                                    _remotePacketSize,
+                                    _remoteChannelNumber)));
+                        w.WaitOne();
+                    });
+            _sessionMock.Setup(p => p.IsConnected).Returns(true);
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(false);
+
+            _channel = new ChannelSession(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
+            _channel.Closed += (sender, args) => _channelClosedRegister.Add(args);
+            _channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
+            _channel.Open();
+
+            _sessionMock.Raise(
+                p => p.ChannelEofReceived += null,
+                new MessageEventArgs<ChannelEofMessage>(new ChannelEofMessage(_localChannelNumber)));
+            _sessionMock.Raise(
+                p => p.ChannelCloseReceived += null,
+                new MessageEventArgs<ChannelCloseMessage>(new ChannelCloseMessage(_localChannelNumber)));
+        }
+
+        private void Act()
+        {
+            _channel.Close();
+        }
+
+        [TestMethod]
+        public void CurrentCountOfSessionSemaphoreShouldBeEqualToInitialCount()
+        {
+            Assert.AreEqual(_initialSessionSemaphoreCount, _sessionSemaphore.CurrentCount);
+        }
+
+        [TestMethod]
+        public void ExceptionShouldNeverHaveFired()
+        {
+            Assert.AreEqual(0, _channelExceptionRegister.Count);
+        }
+
+        [TestMethod]
+        public void ClosedEventShouldHaveFiredOnce()
+        {
+            Assert.AreEqual(1, _channelClosedRegister.Count);
+            Assert.AreEqual(_localChannelNumber, _channelClosedRegister[0].ChannelNumber);
+        }
+
+        [TestMethod]
+        public void IsOpenShouldReturnFalse()
+        {
+            Assert.IsFalse(_channel.IsOpen);
+        }
+    }
+}

+ 131 - 0
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived_SendChannelCloseMessageSuccess.cs

@@ -0,0 +1,131 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Channels;
+using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Connection;
+
+namespace Renci.SshNet.Tests.Classes.Channels
+{
+    [TestClass]
+    public class ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived_SendChannelCloseMessageSuccess
+    {
+        private Mock<ISession> _sessionMock;
+        private uint _localChannelNumber;
+        private uint _localWindowSize;
+        private uint _localPacketSize;
+        private uint _remoteChannelNumber;
+        private uint _remoteWindowSize;
+        private uint _remotePacketSize;
+        private IList<ChannelEventArgs> _channelClosedRegister;
+        private List<ExceptionEventArgs> _channelExceptionRegister;
+        private ChannelSession _channel;
+        private Mock<IConnectionInfo> _connectionInfoMock;
+        private MockSequence _sequence;
+        private SemaphoreLight _sessionSemaphore;
+        private int _initialSessionSemaphoreCount;
+
+        [TestInitialize]
+        public void Initialize()
+        {
+            Arrange();
+            Act();
+        }
+
+        private void Arrange()
+        {
+            var random = new Random();
+            _localChannelNumber = (uint) random.Next(0, int.MaxValue);
+            _localWindowSize = (uint) random.Next(0, int.MaxValue);
+            _localPacketSize = (uint) random.Next(0, int.MaxValue);
+            _remoteChannelNumber = (uint) random.Next(0, int.MaxValue);
+            _remoteWindowSize = (uint) random.Next(0, int.MaxValue);
+            _remotePacketSize = (uint) random.Next(0, int.MaxValue);
+            _channelClosedRegister = new List<ChannelEventArgs>();
+            _channelExceptionRegister = new List<ExceptionEventArgs>();
+            _initialSessionSemaphoreCount = random.Next(10, 20);
+            _sessionSemaphore = new SemaphoreLight(_initialSessionSemaphoreCount);
+
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+
+            _sequence = new MockSequence();
+            _sessionMock.InSequence(_sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
+            _connectionInfoMock.InSequence(_sequence).Setup(p => p.RetryAttempts).Returns(1);
+            _sessionMock.Setup(p => p.SessionSemaphore).Returns(_sessionSemaphore);
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p =>
+                        p.SendMessage(
+                            It.Is<ChannelOpenMessage>(
+                                m =>
+                                    m.LocalChannelNumber == _localChannelNumber &&
+                                    m.InitialWindowSize == _localWindowSize && m.MaximumPacketSize == _localPacketSize &&
+                                    m.Info is SessionChannelOpenInfo)));
+            _sessionMock.InSequence(_sequence)
+                .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
+                .Callback<WaitHandle>(
+                    w =>
+                    {
+                        _sessionMock.Raise(
+                            s => s.ChannelOpenConfirmationReceived += null,
+                            new MessageEventArgs<ChannelOpenConfirmationMessage>(
+                                new ChannelOpenConfirmationMessage(
+                                    _localChannelNumber,
+                                    _remoteWindowSize,
+                                    _remotePacketSize,
+                                    _remoteChannelNumber)));
+                        w.WaitOne();
+                    });
+            _sessionMock.Setup(p => p.IsConnected).Returns(true);
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(true);
+
+            _channel = new ChannelSession(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
+            _channel.Closed += (sender, args) => _channelClosedRegister.Add(args);
+            _channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
+            _channel.Open();
+
+            _sessionMock.Raise(
+                p => p.ChannelEofReceived += null,
+                new MessageEventArgs<ChannelEofMessage>(new ChannelEofMessage(_localChannelNumber)));
+            _sessionMock.Raise(
+                p => p.ChannelCloseReceived += null,
+                new MessageEventArgs<ChannelCloseMessage>(new ChannelCloseMessage(_localChannelNumber)));
+        }
+
+        private void Act()
+        {
+            _channel.Close();
+        }
+
+        [TestMethod]
+        public void CurrentCountOfSessionSemaphoreShouldBeEqualToInitialCount()
+        {
+            Assert.AreEqual(_initialSessionSemaphoreCount, _sessionSemaphore.CurrentCount);
+        }
+
+        [TestMethod]
+        public void ExceptionShouldNeverHaveFired()
+        {
+            Assert.AreEqual(0, _channelExceptionRegister.Count);
+        }
+
+        [TestMethod]
+        public void ClosedEventShouldHaveFiredOnce()
+        {
+            Assert.AreEqual(1, _channelClosedRegister.Count);
+            Assert.AreEqual(_localChannelNumber, _channelClosedRegister[0].ChannelNumber);
+        }
+
+        [TestMethod]
+        public void IsOpenShouldReturnFalse()
+        {
+            Assert.IsFalse(_channel.IsOpen);
+        }
+    }
+}

+ 128 - 0
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseReceived_SendChannelCloseMessageFailure.cs

@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Channels;
+using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Connection;
+
+namespace Renci.SshNet.Tests.Classes.Channels
+{
+    [TestClass]
+    public class ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseReceived_SendChannelCloseMessageFailure
+    {
+        private Mock<ISession> _sessionMock;
+        private uint _localChannelNumber;
+        private uint _localWindowSize;
+        private uint _localPacketSize;
+        private uint _remoteChannelNumber;
+        private uint _remoteWindowSize;
+        private uint _remotePacketSize;
+        private IList<ChannelEventArgs> _channelClosedRegister;
+        private List<ExceptionEventArgs> _channelExceptionRegister;
+        private ChannelSession _channel;
+        private Mock<IConnectionInfo> _connectionInfoMock;
+        private MockSequence _sequence;
+        private SemaphoreLight _sessionSemaphore;
+        private int _initialSessionSemaphoreCount;
+
+        [TestInitialize]
+        public void Initialize()
+        {
+            Arrange();
+            Act();
+        }
+
+        private void Arrange()
+        {
+            var random = new Random();
+            _localChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _localWindowSize = (uint)random.Next(0, int.MaxValue);
+            _localPacketSize = (uint)random.Next(0, int.MaxValue);
+            _remoteChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _remoteWindowSize = (uint)random.Next(0, int.MaxValue);
+            _remotePacketSize = (uint)random.Next(0, int.MaxValue);
+            _channelClosedRegister = new List<ChannelEventArgs>();
+            _channelExceptionRegister = new List<ExceptionEventArgs>();
+            _initialSessionSemaphoreCount = random.Next(10, 20);
+            _sessionSemaphore = new SemaphoreLight(_initialSessionSemaphoreCount);
+
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+
+            _sequence = new MockSequence();
+            _sessionMock.InSequence(_sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
+            _connectionInfoMock.InSequence(_sequence).Setup(p => p.RetryAttempts).Returns(1);
+            _sessionMock.Setup(p => p.SessionSemaphore).Returns(_sessionSemaphore);
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p =>
+                        p.SendMessage(
+                            It.Is<ChannelOpenMessage>(
+                                m =>
+                                    m.LocalChannelNumber == _localChannelNumber &&
+                                    m.InitialWindowSize == _localWindowSize && m.MaximumPacketSize == _localPacketSize &&
+                                    m.Info is SessionChannelOpenInfo)));
+            _sessionMock.InSequence(_sequence)
+                .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
+                .Callback<WaitHandle>(
+                    w =>
+                    {
+                        _sessionMock.Raise(
+                            s => s.ChannelOpenConfirmationReceived += null,
+                            new MessageEventArgs<ChannelOpenConfirmationMessage>(
+                                new ChannelOpenConfirmationMessage(
+                                    _localChannelNumber,
+                                    _remoteWindowSize,
+                                    _remotePacketSize,
+                                    _remoteChannelNumber)));
+                        w.WaitOne();
+                    });
+            _sessionMock.Setup(p => p.IsConnected).Returns(true);
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(false);
+
+            _channel = new ChannelSession(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
+            _channel.Closed += (sender, args) => _channelClosedRegister.Add(args);
+            _channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
+            _channel.Open();
+
+            _sessionMock.Raise(
+                p => p.ChannelCloseReceived += null,
+                new MessageEventArgs<ChannelCloseMessage>(new ChannelCloseMessage(_localChannelNumber)));
+        }
+
+        private void Act()
+        {
+            _channel.Close();
+        }
+
+        [TestMethod]
+        public void CurrentCountOfSessionSemaphoreShouldBeEqualToInitialCount()
+        {
+            Assert.AreEqual(_initialSessionSemaphoreCount, _sessionSemaphore.CurrentCount);
+        }
+
+        [TestMethod]
+        public void ExceptionShouldNeverHaveFired()
+        {
+            Assert.AreEqual(0, _channelExceptionRegister.Count);
+        }
+
+        [TestMethod]
+        public void ClosedEventShouldHaveFiredOnce()
+        {
+            Assert.AreEqual(1, _channelClosedRegister.Count);
+            Assert.AreEqual(_localChannelNumber, _channelClosedRegister[0].ChannelNumber);
+        }
+
+        [TestMethod]
+        public void IsOpenShouldReturnFalse()
+        {
+            Assert.IsFalse(_channel.IsOpen);
+        }
+    }
+}

+ 5 - 2
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseReceived.cs → Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseReceived_SendChannelCloseMessageSuccess.cs

@@ -10,7 +10,7 @@ using Renci.SshNet.Messages.Connection;
 namespace Renci.SshNet.Tests.Classes.Channels
 {
     [TestClass]
-    public class ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseReceived
+    public class ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseReceived_SendChannelCloseMessageSuccess
     {
         private Mock<ISession> _sessionMock;
         private uint _localChannelNumber;
@@ -80,7 +80,10 @@ namespace Renci.SshNet.Tests.Classes.Channels
                         w.WaitOne();
                     });
             _sessionMock.Setup(p => p.IsConnected).Returns(true);
-            _sessionMock.InSequence(_sequence).Setup(p => p.SendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)));
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(true);
 
             _channel = new ChannelSession(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
             _channel.Closed += (sender, args) => _channelClosedRegister.Add(args);

+ 127 - 0
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelEofReceived_SendChannelCloseMessageFailure.cs

@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Channels;
+using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Connection;
+
+namespace Renci.SshNet.Tests.Classes.Channels
+{
+    [TestClass]
+    public class ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelEofReceived_SendChannelCloseMessageFailure
+    {
+        private Mock<ISession> _sessionMock;
+        private uint _localChannelNumber;
+        private uint _localWindowSize;
+        private uint _localPacketSize;
+        private uint _remoteChannelNumber;
+        private uint _remoteWindowSize;
+        private uint _remotePacketSize;
+        private IList<ChannelEventArgs> _channelClosedRegister;
+        private List<ExceptionEventArgs> _channelExceptionRegister;
+        private ChannelSession _channel;
+        private Mock<IConnectionInfo> _connectionInfoMock;
+        private MockSequence _sequence;
+        private SemaphoreLight _sessionSemaphore;
+        private int _initialSessionSemaphoreCount;
+
+        [TestInitialize]
+        public void Initialize()
+        {
+            Arrange();
+            Act();
+        }
+
+        private void Arrange()
+        {
+            var random = new Random();
+            _localChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _localWindowSize = (uint)random.Next(0, int.MaxValue);
+            _localPacketSize = (uint)random.Next(0, int.MaxValue);
+            _remoteChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _remoteWindowSize = (uint)random.Next(0, int.MaxValue);
+            _remotePacketSize = (uint)random.Next(0, int.MaxValue);
+            _channelClosedRegister = new List<ChannelEventArgs>();
+            _channelExceptionRegister = new List<ExceptionEventArgs>();
+            _initialSessionSemaphoreCount = random.Next(10, 20);
+            _sessionSemaphore = new SemaphoreLight(_initialSessionSemaphoreCount);
+
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+
+            _sequence = new MockSequence();
+            _sessionMock.InSequence(_sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
+            _connectionInfoMock.InSequence(_sequence).Setup(p => p.RetryAttempts).Returns(1);
+            _sessionMock.Setup(p => p.SessionSemaphore).Returns(_sessionSemaphore);
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p =>
+                        p.SendMessage(
+                            It.Is<ChannelOpenMessage>(
+                                m =>
+                                    m.LocalChannelNumber == _localChannelNumber &&
+                                    m.InitialWindowSize == _localWindowSize && m.MaximumPacketSize == _localPacketSize &&
+                                    m.Info is SessionChannelOpenInfo)));
+            _sessionMock.InSequence(_sequence)
+                .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
+                .Callback<WaitHandle>(
+                    w =>
+                    {
+                        _sessionMock.Raise(
+                            s => s.ChannelOpenConfirmationReceived += null,
+                            new MessageEventArgs<ChannelOpenConfirmationMessage>(
+                                new ChannelOpenConfirmationMessage(
+                                    _localChannelNumber,
+                                    _remoteWindowSize,
+                                    _remotePacketSize,
+                                    _remoteChannelNumber)));
+                        w.WaitOne();
+                    });
+            _sessionMock.Setup(p => p.IsConnected).Returns(true);
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(false);
+
+            _channel = new ChannelSession(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
+            _channel.Closed += (sender, args) => _channelClosedRegister.Add(args);
+            _channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
+            _channel.Open();
+
+            _sessionMock.Raise(
+                p => p.ChannelEofReceived += null,
+                new MessageEventArgs<ChannelEofMessage>(new ChannelEofMessage(_localChannelNumber)));
+        }
+
+        private void Act()
+        {
+            _channel.Close();
+        }
+
+        [TestMethod]
+        public void CurrentCountOfSessionSemaphoreShouldBeEqualToInitialCount()
+        {
+            Assert.AreEqual(_initialSessionSemaphoreCount, _sessionSemaphore.CurrentCount);
+        }
+
+        [TestMethod]
+        public void ExceptionShouldNeverHaveFired()
+        {
+            Assert.AreEqual(0, _channelExceptionRegister.Count);
+        }
+
+        [TestMethod]
+        public void ClosedEventShouldNeverHaveFired()
+        {
+            Assert.AreEqual(0, _channelClosedRegister.Count);
+        }
+
+        [TestMethod]
+        public void IsOpenShouldReturnFalse()
+        {
+            Assert.IsFalse(_channel.IsOpen);
+        }
+    }
+}

+ 138 - 0
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelEofReceived_SendChannelCloseMessageSuccess.cs

@@ -0,0 +1,138 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Channels;
+using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Connection;
+
+namespace Renci.SshNet.Tests.Classes.Channels
+{
+    [TestClass]
+    public class ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelEofReceived_SendChannelCloseMessageSuccess
+    {
+        private Mock<ISession> _sessionMock;
+        private uint _localChannelNumber;
+        private uint _localWindowSize;
+        private uint _localPacketSize;
+        private uint _remoteChannelNumber;
+        private uint _remoteWindowSize;
+        private uint _remotePacketSize;
+        private IList<ChannelEventArgs> _channelClosedRegister;
+        private List<ExceptionEventArgs> _channelExceptionRegister;
+        private ChannelSession _channel;
+        private Mock<IConnectionInfo> _connectionInfoMock;
+        private MockSequence _sequence;
+        private SemaphoreLight _sessionSemaphore;
+        private int _initialSessionSemaphoreCount;
+
+        [TestInitialize]
+        public void Initialize()
+        {
+            Arrange();
+            Act();
+        }
+
+        private void Arrange()
+        {
+            var random = new Random();
+            _localChannelNumber = (uint) random.Next(0, int.MaxValue);
+            _localWindowSize = (uint) random.Next(0, int.MaxValue);
+            _localPacketSize = (uint) random.Next(0, int.MaxValue);
+            _remoteChannelNumber = (uint) random.Next(0, int.MaxValue);
+            _remoteWindowSize = (uint) random.Next(0, int.MaxValue);
+            _remotePacketSize = (uint) random.Next(0, int.MaxValue);
+            _channelClosedRegister = new List<ChannelEventArgs>();
+            _channelExceptionRegister = new List<ExceptionEventArgs>();
+            _initialSessionSemaphoreCount = random.Next(10, 20);
+            _sessionSemaphore = new SemaphoreLight(_initialSessionSemaphoreCount);
+
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+
+            _sequence = new MockSequence();
+            _sessionMock.InSequence(_sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
+            _connectionInfoMock.InSequence(_sequence).Setup(p => p.RetryAttempts).Returns(1);
+            _sessionMock.Setup(p => p.SessionSemaphore).Returns(_sessionSemaphore);
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p =>
+                        p.SendMessage(
+                            It.Is<ChannelOpenMessage>(
+                                m =>
+                                    m.LocalChannelNumber == _localChannelNumber &&
+                                    m.InitialWindowSize == _localWindowSize && m.MaximumPacketSize == _localPacketSize &&
+                                    m.Info is SessionChannelOpenInfo)));
+            _sessionMock.InSequence(_sequence)
+                .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
+                .Callback<WaitHandle>(
+                    w =>
+                    {
+                        _sessionMock.Raise(
+                            s => s.ChannelOpenConfirmationReceived += null,
+                            new MessageEventArgs<ChannelOpenConfirmationMessage>(
+                                new ChannelOpenConfirmationMessage(
+                                    _localChannelNumber,
+                                    _remoteWindowSize,
+                                    _remotePacketSize,
+                                    _remoteChannelNumber)));
+                        w.WaitOne();
+                    });
+            _sessionMock.Setup(p => p.IsConnected).Returns(true);
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(true);
+            _sessionMock.InSequence(_sequence)
+                .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
+                .Callback<WaitHandle>(
+                    w =>
+                    {
+                        _sessionMock.Raise(
+                            s => s.ChannelCloseReceived += null,
+                            new MessageEventArgs<ChannelCloseMessage>(new ChannelCloseMessage(_localChannelNumber)));
+                        w.WaitOne();
+                    });
+
+            _channel = new ChannelSession(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
+            _channel.Closed += (sender, args) => _channelClosedRegister.Add(args);
+            _channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
+            _channel.Open();
+
+            _sessionMock.Raise(
+                p => p.ChannelEofReceived += null,
+                new MessageEventArgs<ChannelEofMessage>(new ChannelEofMessage(_localChannelNumber)));
+        }
+
+        private void Act()
+        {
+            _channel.Close();
+        }
+
+        [TestMethod]
+        public void CurrentCountOfSessionSemaphoreShouldBeEqualToInitialCount()
+        {
+            Assert.AreEqual(_initialSessionSemaphoreCount, _sessionSemaphore.CurrentCount);
+        }
+
+        [TestMethod]
+        public void ExceptionShouldNeverHaveFired()
+        {
+            Assert.AreEqual(0, _channelExceptionRegister.Count);
+        }
+
+        [TestMethod]
+        public void ClosedEventShouldHaveFiredOnce()
+        {
+            Assert.AreEqual(1, _channelClosedRegister.Count);
+            Assert.AreEqual(_localChannelNumber, _channelClosedRegister[0].ChannelNumber);
+        }
+
+        [TestMethod]
+        public void IsOpenShouldReturnFalse()
+        {
+            Assert.IsFalse(_channel.IsOpen);
+        }
+    }
+}

+ 9 - 5
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen.cs → Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_NoChannelCloseOrChannelEofReceived.cs

@@ -10,7 +10,7 @@ using Renci.SshNet.Messages.Connection;
 namespace Renci.SshNet.Tests.Classes.Channels
 {
     [TestClass]
-    public class ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen
+    public class ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_NoChannelCloseOrChannelEofReceived
     {
         private Mock<ISession> _sessionMock;
         private Mock<IConnectionInfo> _connectionInfoMock;
@@ -78,10 +78,14 @@ namespace Renci.SshNet.Tests.Classes.Channels
                     });
             _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
             _sessionMock.InSequence(sequence)
-                .Setup(p => p.SendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)));
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(true);
             _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
             _sessionMock.InSequence(sequence)
-                .Setup(p => p.SendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)));
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)))
+                    .Returns(true);
             _sessionMock.InSequence(sequence)
                 .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
                 .Callback<WaitHandle>(
@@ -107,13 +111,13 @@ namespace Renci.SshNet.Tests.Classes.Channels
         [TestMethod]
         public void ChannelEofMessageShouldBeSentOnce()
         {
-            _sessionMock.Verify(p => p.SendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
+            _sessionMock.Verify(p => p.TrySendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
         }
 
         [TestMethod]
         public void ChannelCloseMessageShouldBeSentOnce()
         {
-            _sessionMock.Verify(p => p.SendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
+            _sessionMock.Verify(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
         }
 
         [TestMethod]

+ 142 - 0
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_NoChannelCloseOrChannelEofReceived_SendChannelEofMessageFailure.cs

@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Channels;
+using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Connection;
+
+namespace Renci.SshNet.Tests.Classes.Channels
+{
+    [TestClass]
+    public class ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_NoChannelCloseOrChannelEofReceived_SendChannelEofMessageFailure
+    {
+        private Mock<ISession> _sessionMock;
+        private Mock<IConnectionInfo> _connectionInfoMock;
+        private ChannelSession _channel;
+        private uint _localChannelNumber;
+        private uint _localWindowSize;
+        private uint _localPacketSize;
+        private uint _remoteWindowSize;
+        private uint _remotePacketSize;
+        private uint _remoteChannelNumber;
+        private SemaphoreLight _sessionSemaphore;
+        private IList<ChannelEventArgs> _channelClosedRegister;
+        private List<ExceptionEventArgs> _channelExceptionRegister;
+
+        [TestInitialize]
+        public void Initialize()
+        {
+            Arrange();
+            Act();
+        }
+
+        private void Arrange()
+        {
+            var random = new Random();
+            _localChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _localWindowSize = (uint)random.Next(2000, 3000);
+            _localPacketSize = (uint)random.Next(1000, 2000);
+            _remoteChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _remoteWindowSize = (uint)random.Next(0, int.MaxValue);
+            _remotePacketSize = (uint)random.Next(100, 200);
+            _sessionSemaphore = new SemaphoreLight(1);
+            _channelClosedRegister = new List<ChannelEventArgs>();
+            _channelExceptionRegister = new List<ExceptionEventArgs>();
+
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+
+            var sequence = new MockSequence();
+            _sessionMock.InSequence(sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
+            _connectionInfoMock.InSequence(sequence).Setup(p => p.RetryAttempts).Returns(1);
+            _sessionMock.Setup(p => p.SessionSemaphore).Returns(_sessionSemaphore);
+            _sessionMock.InSequence(sequence)
+                .Setup(
+                    p =>
+                        p.SendMessage(
+                            It.Is<ChannelOpenMessage>(
+                                m =>
+                                    m.LocalChannelNumber == _localChannelNumber &&
+                                    m.InitialWindowSize == _localWindowSize && m.MaximumPacketSize == _localPacketSize &&
+                                    m.Info is SessionChannelOpenInfo)));
+            _sessionMock.InSequence(sequence)
+                .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
+                .Callback<WaitHandle>(
+                    w =>
+                    {
+                        _sessionMock.Raise(
+                            s => s.ChannelOpenConfirmationReceived += null,
+                            new MessageEventArgs<ChannelOpenConfirmationMessage>(
+                                new ChannelOpenConfirmationMessage(
+                                    _localChannelNumber,
+                                    _remoteWindowSize,
+                                    _remotePacketSize,
+                                    _remoteChannelNumber)));
+                        w.WaitOne();
+                    });
+            _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
+            _sessionMock.InSequence(sequence)
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(false);
+            _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
+            _sessionMock.InSequence(sequence)
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(true);
+            _sessionMock.InSequence(sequence)
+                .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
+                .Callback<WaitHandle>(
+                    w =>
+                    {
+                        _sessionMock.Raise(
+                            s => s.ChannelCloseReceived += null,
+                            new MessageEventArgs<ChannelCloseMessage>(new ChannelCloseMessage(_localChannelNumber)));
+                        w.WaitOne();
+                    });
+
+            _channel = new ChannelSession(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
+            _channel.Closed += (sender, args) => _channelClosedRegister.Add(args);
+            _channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
+            _channel.Open();
+        }
+
+        private void Act()
+        {
+            _channel.Close();
+        }
+
+        [TestMethod]
+        public void ChannelEofMessageShouldBeSentOnce()
+        {
+            _sessionMock.Verify(p => p.TrySendMessage(It.Is<ChannelEofMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
+        }
+
+        [TestMethod]
+        public void ChannelCloseMessageShouldBeSentOnce()
+        {
+            _sessionMock.Verify(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(m => m.LocalChannelNumber == _remoteChannelNumber)), Times.Once);
+        }
+
+        [TestMethod]
+        public void ExceptionShouldNeverHaveFired()
+        {
+            Assert.AreEqual(0, _channelExceptionRegister.Count);
+        }
+
+        [TestMethod]
+        public void ClosedEventShouldHaveFiredOnce()
+        {
+            Assert.AreEqual(1, _channelClosedRegister.Count);
+            Assert.AreEqual(_localChannelNumber, _channelClosedRegister[0].ChannelNumber);
+        }
+
+        [TestMethod]
+        public void IsOpenShouldReturnFalse()
+        {
+            Assert.IsFalse(_channel.IsOpen);
+        }
+    }
+}

+ 127 - 0
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsNotConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived.cs

@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Channels;
+using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Connection;
+
+namespace Renci.SshNet.Tests.Classes.Channels
+{
+    [TestClass]
+    public class ChannelSessionTest_Close_SessionIsNotConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived
+    {
+        private Mock<ISession> _sessionMock;
+        private uint _localChannelNumber;
+        private uint _localWindowSize;
+        private uint _localPacketSize;
+        private uint _remoteChannelNumber;
+        private uint _remoteWindowSize;
+        private uint _remotePacketSize;
+        private IList<ChannelEventArgs> _channelClosedRegister;
+        private List<ExceptionEventArgs> _channelExceptionRegister;
+        private ChannelSession _channel;
+        private Mock<IConnectionInfo> _connectionInfoMock;
+        private MockSequence _sequence;
+        private SemaphoreLight _sessionSemaphore;
+        private int _initialSessionSemaphoreCount;
+
+        [TestInitialize]
+        public void Initialize()
+        {
+            Arrange();
+            Act();
+        }
+
+        private void Arrange()
+        {
+            var random = new Random();
+            _localChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _localWindowSize = (uint)random.Next(0, int.MaxValue);
+            _localPacketSize = (uint)random.Next(0, int.MaxValue);
+            _remoteChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _remoteWindowSize = (uint)random.Next(0, int.MaxValue);
+            _remotePacketSize = (uint)random.Next(0, int.MaxValue);
+            _channelClosedRegister = new List<ChannelEventArgs>();
+            _channelExceptionRegister = new List<ExceptionEventArgs>();
+            _initialSessionSemaphoreCount = random.Next(10, 20);
+            _sessionSemaphore = new SemaphoreLight(_initialSessionSemaphoreCount);
+
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+
+            _sequence = new MockSequence();
+            _sessionMock.InSequence(_sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
+            _connectionInfoMock.InSequence(_sequence).Setup(p => p.RetryAttempts).Returns(1);
+            _sessionMock.Setup(p => p.SessionSemaphore).Returns(_sessionSemaphore);
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p =>
+                        p.SendMessage(
+                            It.Is<ChannelOpenMessage>(
+                                m =>
+                                    m.LocalChannelNumber == _localChannelNumber &&
+                                    m.InitialWindowSize == _localWindowSize && m.MaximumPacketSize == _localPacketSize &&
+                                    m.Info is SessionChannelOpenInfo)));
+            _sessionMock.InSequence(_sequence)
+                .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
+                .Callback<WaitHandle>(
+                    w =>
+                    {
+                        _sessionMock.Raise(
+                            s => s.ChannelOpenConfirmationReceived += null,
+                            new MessageEventArgs<ChannelOpenConfirmationMessage>(
+                                new ChannelOpenConfirmationMessage(
+                                    _localChannelNumber,
+                                    _remoteWindowSize,
+                                    _remotePacketSize,
+                                    _remoteChannelNumber)));
+                        w.WaitOne();
+                    });
+            _sessionMock.Setup(p => p.IsConnected).Returns(false);
+
+            _channel = new ChannelSession(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
+            _channel.Closed += (sender, args) => _channelClosedRegister.Add(args);
+            _channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
+            _channel.Open();
+
+            _sessionMock.Raise(
+                p => p.ChannelEofReceived += null,
+                new MessageEventArgs<ChannelEofMessage>(new ChannelEofMessage(_localChannelNumber)));
+            _sessionMock.Raise(
+                p => p.ChannelCloseReceived += null,
+                new MessageEventArgs<ChannelCloseMessage>(new ChannelCloseMessage(_localChannelNumber)));
+        }
+
+        private void Act()
+        {
+            _channel.Close();
+        }
+
+        [TestMethod]
+        public void CurrentCountOfSessionSemaphoreShouldBeEqualToInitialCount()
+        {
+            Assert.AreEqual(_initialSessionSemaphoreCount, _sessionSemaphore.CurrentCount);
+        }
+
+        [TestMethod]
+        public void ExceptionShouldNeverHaveFired()
+        {
+            Assert.AreEqual(0, _channelExceptionRegister.Count);
+        }
+
+        [TestMethod]
+        public void ClosedEventShouldHaveFiredOnce()
+        {
+            Assert.AreEqual(1, _channelClosedRegister.Count);
+            Assert.AreEqual(_localChannelNumber, _channelClosedRegister[0].ChannelNumber);
+        }
+
+        [TestMethod]
+        public void IsOpenShouldReturnFalse()
+        {
+            Assert.IsFalse(_channel.IsOpen);
+        }
+    }
+}

+ 124 - 0
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsNotConnectedAndChannelIsOpen_ChannelCloseReceived.cs

@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Channels;
+using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Connection;
+
+namespace Renci.SshNet.Tests.Classes.Channels
+{
+    [TestClass]
+    public class ChannelSessionTest_Close_SessionIsNotConnectedAndChannelIsOpen_ChannelCloseReceived
+    {
+        private Mock<ISession> _sessionMock;
+        private uint _localChannelNumber;
+        private uint _localWindowSize;
+        private uint _localPacketSize;
+        private uint _remoteChannelNumber;
+        private uint _remoteWindowSize;
+        private uint _remotePacketSize;
+        private IList<ChannelEventArgs> _channelClosedRegister;
+        private List<ExceptionEventArgs> _channelExceptionRegister;
+        private ChannelSession _channel;
+        private Mock<IConnectionInfo> _connectionInfoMock;
+        private MockSequence _sequence;
+        private SemaphoreLight _sessionSemaphore;
+        private int _initialSessionSemaphoreCount;
+
+        [TestInitialize]
+        public void Initialize()
+        {
+            Arrange();
+            Act();
+        }
+
+        private void Arrange()
+        {
+            var random = new Random();
+            _localChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _localWindowSize = (uint)random.Next(0, int.MaxValue);
+            _localPacketSize = (uint)random.Next(0, int.MaxValue);
+            _remoteChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _remoteWindowSize = (uint)random.Next(0, int.MaxValue);
+            _remotePacketSize = (uint)random.Next(0, int.MaxValue);
+            _channelClosedRegister = new List<ChannelEventArgs>();
+            _channelExceptionRegister = new List<ExceptionEventArgs>();
+            _initialSessionSemaphoreCount = random.Next(10, 20);
+            _sessionSemaphore = new SemaphoreLight(_initialSessionSemaphoreCount);
+
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+
+            _sequence = new MockSequence();
+            _sessionMock.InSequence(_sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
+            _connectionInfoMock.InSequence(_sequence).Setup(p => p.RetryAttempts).Returns(1);
+            _sessionMock.Setup(p => p.SessionSemaphore).Returns(_sessionSemaphore);
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p =>
+                        p.SendMessage(
+                            It.Is<ChannelOpenMessage>(
+                                m =>
+                                    m.LocalChannelNumber == _localChannelNumber &&
+                                    m.InitialWindowSize == _localWindowSize && m.MaximumPacketSize == _localPacketSize &&
+                                    m.Info is SessionChannelOpenInfo)));
+            _sessionMock.InSequence(_sequence)
+                .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
+                .Callback<WaitHandle>(
+                    w =>
+                    {
+                        _sessionMock.Raise(
+                            s => s.ChannelOpenConfirmationReceived += null,
+                            new MessageEventArgs<ChannelOpenConfirmationMessage>(
+                                new ChannelOpenConfirmationMessage(
+                                    _localChannelNumber,
+                                    _remoteWindowSize,
+                                    _remotePacketSize,
+                                    _remoteChannelNumber)));
+                        w.WaitOne();
+                    });
+            _sessionMock.Setup(p => p.IsConnected).Returns(false);
+
+            _channel = new ChannelSession(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
+            _channel.Closed += (sender, args) => _channelClosedRegister.Add(args);
+            _channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
+            _channel.Open();
+
+            _sessionMock.Raise(
+                p => p.ChannelCloseReceived += null,
+                new MessageEventArgs<ChannelCloseMessage>(new ChannelCloseMessage(_localChannelNumber)));
+        }
+
+        private void Act()
+        {
+            _channel.Close();
+        }
+
+        [TestMethod]
+        public void CurrentCountOfSessionSemaphoreShouldBeEqualToInitialCount()
+        {
+            Assert.AreEqual(_initialSessionSemaphoreCount, _sessionSemaphore.CurrentCount);
+        }
+
+        [TestMethod]
+        public void ExceptionShouldNeverHaveFired()
+        {
+            Assert.AreEqual(0, _channelExceptionRegister.Count);
+        }
+
+        [TestMethod]
+        public void ClosedEventShouldHaveFiredOnce()
+        {
+            Assert.AreEqual(1, _channelClosedRegister.Count);
+            Assert.AreEqual(_localChannelNumber, _channelClosedRegister[0].ChannelNumber);
+        }
+
+        [TestMethod]
+        public void IsOpenShouldReturnFalse()
+        {
+            Assert.IsFalse(_channel.IsOpen);
+        }
+    }
+}

+ 119 - 0
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_Close_SessionIsNotConnectedAndChannelIsOpen_NoChannelCloseOrChannelEofReceived.cs

@@ -0,0 +1,119 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Channels;
+using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Connection;
+
+namespace Renci.SshNet.Tests.Classes.Channels
+{
+    [TestClass]
+    public class ChannelSessionTest_Close_SessionIsNotConnectedAndChannelIsOpen_NoChannelCloseOrChannelEofReceived
+    {
+        private Mock<ISession> _sessionMock;
+        private uint _localChannelNumber;
+        private uint _localWindowSize;
+        private uint _localPacketSize;
+        private uint _remoteChannelNumber;
+        private uint _remoteWindowSize;
+        private uint _remotePacketSize;
+        private IList<ChannelEventArgs> _channelClosedRegister;
+        private List<ExceptionEventArgs> _channelExceptionRegister;
+        private ChannelSession _channel;
+        private Mock<IConnectionInfo> _connectionInfoMock;
+        private MockSequence _sequence;
+        private SemaphoreLight _sessionSemaphore;
+        private int _initialSessionSemaphoreCount;
+
+        [TestInitialize]
+        public void Initialize()
+        {
+            Arrange();
+            Act();
+        }
+
+        private void Arrange()
+        {
+            var random = new Random();
+            _localChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _localWindowSize = (uint)random.Next(0, int.MaxValue);
+            _localPacketSize = (uint)random.Next(0, int.MaxValue);
+            _remoteChannelNumber = (uint)random.Next(0, int.MaxValue);
+            _remoteWindowSize = (uint)random.Next(0, int.MaxValue);
+            _remotePacketSize = (uint)random.Next(0, int.MaxValue);
+            _channelClosedRegister = new List<ChannelEventArgs>();
+            _channelExceptionRegister = new List<ExceptionEventArgs>();
+            _initialSessionSemaphoreCount = random.Next(10, 20);
+            _sessionSemaphore = new SemaphoreLight(_initialSessionSemaphoreCount);
+
+            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
+            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
+
+            _sequence = new MockSequence();
+            _sessionMock.InSequence(_sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
+            _connectionInfoMock.InSequence(_sequence).Setup(p => p.RetryAttempts).Returns(1);
+            _sessionMock.Setup(p => p.SessionSemaphore).Returns(_sessionSemaphore);
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p =>
+                        p.SendMessage(
+                            It.Is<ChannelOpenMessage>(
+                                m =>
+                                    m.LocalChannelNumber == _localChannelNumber &&
+                                    m.InitialWindowSize == _localWindowSize && m.MaximumPacketSize == _localPacketSize &&
+                                    m.Info is SessionChannelOpenInfo)));
+            _sessionMock.InSequence(_sequence)
+                .Setup(p => p.WaitOnHandle(It.IsNotNull<WaitHandle>()))
+                .Callback<WaitHandle>(
+                    w =>
+                    {
+                        _sessionMock.Raise(
+                            s => s.ChannelOpenConfirmationReceived += null,
+                            new MessageEventArgs<ChannelOpenConfirmationMessage>(
+                                new ChannelOpenConfirmationMessage(
+                                    _localChannelNumber,
+                                    _remoteWindowSize,
+                                    _remotePacketSize,
+                                    _remoteChannelNumber)));
+                        w.WaitOne();
+                    });
+            _sessionMock.Setup(p => p.IsConnected).Returns(false);
+
+            _channel = new ChannelSession(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
+            _channel.Closed += (sender, args) => _channelClosedRegister.Add(args);
+            _channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
+            _channel.Open();
+        }
+
+        private void Act()
+        {
+            _channel.Close();
+        }
+
+        [TestMethod]
+        public void CurrentCountOfSessionSemaphoreShouldBeEqualToInitialCount()
+        {
+            Assert.AreEqual(_initialSessionSemaphoreCount, _sessionSemaphore.CurrentCount);
+        }
+
+        [TestMethod]
+        public void ExceptionShouldNeverHaveFired()
+        {
+            Assert.AreEqual(0, _channelExceptionRegister.Count);
+        }
+
+        [TestMethod]
+        public void ClosedEventShouldNotHaveFired()
+        {
+            Assert.AreEqual(0, _channelClosedRegister.Count);
+        }
+
+        [TestMethod]
+        public void IsOpenShouldReturnFalse()
+        {
+            Assert.IsFalse(_channel.IsOpen);
+        }
+    }
+}

+ 4 - 1
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelSessionTest_OnSessionChannelCloseReceived_SessionIsConnectedAndChannelIsOpen.cs

@@ -80,7 +80,10 @@ namespace Renci.SshNet.Tests.Classes.Channels
                         w.WaitOne();
                     });
             _sessionMock.Setup(p => p.IsConnected).Returns(true);
-            _sessionMock.InSequence(_sequence).Setup(p => p.SendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)));
+            _sessionMock.InSequence(_sequence)
+                .Setup(
+                    p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(true);
 
             _channel = new ChannelSession(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
             _channel.Closed += (sender, args) => _channelClosedRegister.Add(args);

+ 6 - 6
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Close_SessionIsConnectedAndChannelIsOpen_EofNotReceived.cs

@@ -46,9 +46,9 @@ namespace Renci.SshNet.Tests.Classes.Channels
 
             var sequence = new MockSequence();
             _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
-            _sessionMock.InSequence(sequence).Setup(p => p.SendMessage(It.Is<ChannelEofMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)));
+            _sessionMock.InSequence(sequence).Setup(p => p.TrySendMessage(It.Is<ChannelEofMessage>(c => c.LocalChannelNumber == _remoteChannelNumber))).Returns(true);
             _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
-            _sessionMock.InSequence(sequence).Setup(p => p.SendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)));
+            _sessionMock.InSequence(sequence).Setup(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber))).Returns(true);
             _sessionMock.InSequence(sequence).Setup(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()))
                 .Callback<WaitHandle>(w =>
                     {
@@ -97,18 +97,18 @@ namespace Renci.SshNet.Tests.Classes.Channels
         }
 
         [TestMethod]
-        public void SendMessageOnSessionShouldBeInvokedOnceForChannelCloseMessage()
+        public void TrySendMessageOnSessionShouldBeInvokedOnceForChannelCloseMessage()
         {
             _sessionMock.Verify(
-                p => p.SendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
+                p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
                 Times.Once);
         }
 
         [TestMethod]
-        public void SendMessageOnSessionShouldBeInvokedOnceForChannelEofMessage()
+        public void TrySendMessageOnSessionShouldBeInvokedOnceForChannelEofMessage()
         {
             _sessionMock.Verify(
-                p => p.SendMessage(It.Is<ChannelEofMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
+                p => p.TrySendMessage(It.Is<ChannelEofMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
                 Times.Once);
         }
 

+ 5 - 5
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Close_SessionIsConnectedAndChannelIsOpen_EofReceived.cs

@@ -46,7 +46,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
 
             var sequence = new MockSequence();
             _sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
-            _sessionMock.InSequence(sequence).Setup(p => p.SendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)));
+            _sessionMock.InSequence(sequence).Setup(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber))).Returns(true);
             _sessionMock.InSequence(sequence).Setup(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()))
                 .Callback<WaitHandle>(w =>
                     {
@@ -99,18 +99,18 @@ namespace Renci.SshNet.Tests.Classes.Channels
         }
 
         [TestMethod]
-        public void SendMessageOnSessionShouldBeInvokedOnceForChannelCloseMessage()
+        public void TrySendMessageOnSessionShouldBeInvokedOnceForChannelCloseMessage()
         {
             _sessionMock.Verify(
-                p => p.SendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
+                p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
                 Times.Once);
         }
 
         [TestMethod]
-        public void SendMessageOnSessionShouldNeverBeInvokedForChannelEofMessage()
+        public void TrySendMessageOnSessionShouldNeverBeInvokedForChannelEofMessage()
         {
             _sessionMock.Verify(
-                p => p.SendMessage(It.Is<ChannelEofMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
+                p => p.TrySendMessage(It.Is<ChannelEofMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
                 Times.Never);
         }
 

+ 5 - 5
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelCloseReceived_SessionIsConnectedAndChannelIsOpen_EofNotReceived.cs

@@ -42,7 +42,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
             _sessionMock = new Mock<ISession>(MockBehavior.Strict);
 
             _sessionMock.Setup(p => p.IsConnected).Returns(true);
-            _sessionMock.Setup(p => p.SendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)));
+            _sessionMock.Setup(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber))).Returns(true);
 
             _channel = new ChannelStub(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
             _channel.Closed += (sender, args) =>
@@ -69,18 +69,18 @@ namespace Renci.SshNet.Tests.Classes.Channels
         }
 
         [TestMethod]
-        public void SendMessageOnSessionShouldBeInvokedOnceForChannelCloseMessage()
+        public void TrySendMessageOnSessionShouldBeInvokedOnceForChannelCloseMessage()
         {
             _sessionMock.Verify(
-                p => p.SendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
+                p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
                 Times.Once);
         }
 
         [TestMethod]
-        public void SendMessageOnSessionShouldNeverBeInvokedForChannelEofMessage()
+        public void TrySendMessageOnSessionShouldNeverBeInvokedForChannelEofMessage()
         {
             _sessionMock.Verify(
-                p => p.SendMessage(It.Is<ChannelEofMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
+                p => p.TrySendMessage(It.Is<ChannelEofMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
                 Times.Never);
         }
 

+ 6 - 4
Renci.SshClient/Renci.SshNet.Tests/Classes/Channels/ChannelTest_OnSessionChannelCloseReceived_SessionIsConnectedAndChannelIsOpen_EofReceived.cs

@@ -42,7 +42,9 @@ namespace Renci.SshNet.Tests.Classes.Channels
             _sessionMock = new Mock<ISession>(MockBehavior.Strict);
 
             _sessionMock.Setup(p => p.IsConnected).Returns(true);
-            _sessionMock.Setup(p => p.SendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)));
+            _sessionMock.Setup(
+                p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)))
+                .Returns(true);
 
             _channel = new ChannelStub(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
             _channel.Closed += (sender, args) =>
@@ -72,10 +74,10 @@ namespace Renci.SshNet.Tests.Classes.Channels
         }
 
         [TestMethod]
-        public void SendMessageOnSessionShouldBeInvokedOnceForChannelCloseMessage()
+        public void TrySendMessageOnSessionShouldBeInvokedOnceForChannelCloseMessage()
         {
             _sessionMock.Verify(
-                p => p.SendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
+                p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
                 Times.Once);
         }
 
@@ -83,7 +85,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
         public void SendMessageOnSessionShouldNeverBeInvokedForChannelEofMessage()
         {
             _sessionMock.Verify(
-                p => p.SendMessage(It.Is<ChannelEofMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
+                p => p.TrySendMessage(It.Is<ChannelEofMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
                 Times.Never);
         }
 

+ 1 - 1
Renci.SshClient/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_DifferentAllowedAuthenticationsAfterPartialSuccess.cs

@@ -27,7 +27,7 @@ namespace Renci.SshNet.Tests.Classes
                     PublicKeyAuthenticationMethodMock.Object,
                     KeyboardInteractiveAuthenticationMethodMock.Object,
                 });
-            NoneAuthenticationMethodMock.InSequence(seq).Setup(p => p.AllowedAuthentications).Returns(new[] { "password", "publickey" });
+            NoneAuthenticationMethodMock.InSequence(seq).Setup(p => p.AllowedAuthentications).Returns(new[] { "publickey", "password" });
             PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("password");
             PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey");
             KeyboardInteractiveAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("keyboard-interactive");

+ 57 - 0
Renci.SshClient/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PostponePartialAccessAuthenticationMethod.cs

@@ -0,0 +1,57 @@
+using System.Collections.Generic;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+
+namespace Renci.SshNet.Tests.Classes
+{
+    [TestClass]
+    public class ClientAuthenticationTest_Success_MultiList_PostponePartialAccessAuthenticationMethod : ClientAuthenticationTestBase
+    {
+        protected override void SetupMocks()
+        {
+            var seq = new MockSequence();
+
+            SessionMock.InSequence(seq).Setup(p => p.RegisterMessage("SSH_MSG_USERAUTH_FAILURE"));
+            SessionMock.InSequence(seq).Setup(p => p.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS"));
+            SessionMock.InSequence(seq).Setup(p => p.RegisterMessage("SSH_MSG_USERAUTH_BANNER"));
+
+            ConnectionInfoMock.InSequence(seq).Setup(p => p.CreateNoneAuthenticationMethod())
+                .Returns(NoneAuthenticationMethodMock.Object);
+
+            NoneAuthenticationMethodMock.InSequence(seq).Setup(p => p.Authenticate(SessionMock.Object))
+                .Returns(AuthenticationResult.Failure);
+            ConnectionInfoMock.InSequence(seq).Setup(p => p.AuthenticationMethods)
+                            .Returns(new List<IAuthenticationMethod>
+                {
+                    KeyboardInteractiveAuthenticationMethodMock.Object,
+                    PasswordAuthenticationMethodMock.Object,
+                    PublicKeyAuthenticationMethodMock.Object
+                });
+            NoneAuthenticationMethodMock.InSequence(seq).Setup(p => p.AllowedAuthentications).Returns(new[] { "password" });
+            KeyboardInteractiveAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("keyboard-interactive");
+            PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("password");
+            PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey");
+
+            PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Authenticate(SessionMock.Object))
+                .Returns(AuthenticationResult.PartialSuccess);
+            PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.AllowedAuthentications)
+                .Returns(new[] { "password", "publickey" });
+            KeyboardInteractiveAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("keyboard-interactive");
+            PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("password");
+            PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey");
+
+            PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Authenticate(SessionMock.Object)).Returns(AuthenticationResult.Failure);
+            PublicKeyAuthenticationMethodMock.InSequence(seq).Setup(p => p.Name).Returns("publickey");
+            PasswordAuthenticationMethodMock.InSequence(seq).Setup(p => p.Authenticate(SessionMock.Object)).Returns(AuthenticationResult.Success);
+
+            SessionMock.InSequence(seq).Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_FAILURE"));
+            SessionMock.InSequence(seq).Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_SUCCESS"));
+            SessionMock.InSequence(seq).Setup(p => p.UnRegisterMessage("SSH_MSG_USERAUTH_BANNER"));
+        }
+
+        protected override void Act()
+        {
+            ClientAuthentication.Authenticate(ConnectionInfoMock.Object, SessionMock.Object);
+        }
+    }
+}

+ 13 - 2
Renci.SshClient/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj

@@ -66,13 +66,23 @@
     </CodeAnalysisDependentAssemblyPaths>
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Classes\BaseClientTest_Connected_KeepAlivesNotSentConcurrently.cs" />
     <Compile Include="Classes\Channels\ChannelDirectTcpipTest.cs" />
     <Compile Include="Classes\Channels\ChannelDirectTcpipTest_Close_SessionIsConnectedAndChannelIsOpen.cs" />
     <Compile Include="Classes\Channels\ChannelSessionTest_Close_Closed.cs" />
-    <Compile Include="Classes\Channels\ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseReceived.cs" />
+    <Compile Include="Classes\Channels\ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived_SendChannelCloseMessageFailure.cs" />
+    <Compile Include="Classes\Channels\ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived_SendChannelCloseMessageSuccess.cs" />
+    <Compile Include="Classes\Channels\ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseReceived_SendChannelCloseMessageFailure.cs" />
+    <Compile Include="Classes\Channels\ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelCloseReceived_SendChannelCloseMessageSuccess.cs" />
+    <Compile Include="Classes\Channels\ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelEofReceived_SendChannelCloseMessageFailure.cs" />
+    <Compile Include="Classes\Channels\ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_ChannelEofReceived_SendChannelCloseMessageSuccess.cs" />
+    <Compile Include="Classes\Channels\ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_NoChannelCloseOrChannelEofReceived_SendChannelEofMessageFailure.cs" />
+    <Compile Include="Classes\Channels\ChannelSessionTest_Close_SessionIsNotConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived.cs" />
+    <Compile Include="Classes\Channels\ChannelSessionTest_Close_SessionIsNotConnectedAndChannelIsOpen_ChannelCloseReceived.cs" />
+    <Compile Include="Classes\Channels\ChannelSessionTest_Close_SessionIsNotConnectedAndChannelIsOpen_NoChannelCloseOrChannelEofReceived.cs" />
     <Compile Include="Classes\Channels\ChannelSessionTest_Disposed_Closed.cs" />
     <Compile Include="Classes\Channels\ChannelSessionTest_OnSessionChannelCloseReceived_SessionIsConnectedAndChannelIsOpen.cs" />
-    <Compile Include="Classes\Channels\ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen.cs" />
+    <Compile Include="Classes\Channels\ChannelSessionTest_Close_SessionIsConnectedAndChannelIsOpen_NoChannelCloseOrChannelEofReceived.cs" />
     <Compile Include="Classes\Channels\ChannelSessionTest_Open_ExceptionWaitingOnOpenConfirmation.cs" />
     <Compile Include="Classes\Channels\ChannelSessionTest_Open_OnOpenFailureReceived_NoRetriesAvailable.cs" />
     <Compile Include="Classes\Channels\ChannelSessionTest_Open_OnOpenFailureReceived_RetriesAvalable.cs" />
@@ -103,6 +113,7 @@
     <Compile Include="Classes\ClientAuthenticationTest_Failure_SingleList_AuthenticationMethodFailed.cs" />
     <Compile Include="Classes\ClientAuthenticationTest_Failure_SingleList_AuthenticationMethodNotConfigured.cs" />
     <Compile Include="Classes\ClientAuthenticationTest_Success_MultiList_DifferentAllowedAuthenticationsAfterPartialSuccess.cs" />
+    <Compile Include="Classes\ClientAuthenticationTest_Success_MultiList_PostponePartialAccessAuthenticationMethod.cs" />
     <Compile Include="Classes\ClientAuthenticationTest_Success_MultiList_SameAllowedAuthenticationsAfterPartialSuccess.cs" />
     <Compile Include="Classes\ClientAuthenticationTest_Success_MultiList_SkipFailedAuthenticationMethod.cs" />
     <Compile Include="Classes\ClientAuthenticationTest_Success_SingleList_SameAllowedAuthenticationAfterPartialSuccess.cs" />

+ 24 - 3
Renci.SshClient/Renci.SshNet/BaseClient.cs

@@ -2,6 +2,7 @@
 using System.Net.Sockets;
 using System.Threading;
 using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Transport;
 
 namespace Renci.SshNet
 {
@@ -16,6 +17,7 @@ namespace Renci.SshNet
         private readonly bool _ownsConnectionInfo;
 
         private readonly IServiceFactory _serviceFactory;
+        private readonly object _keepAliveLock = new object();
         private TimeSpan _keepAliveInterval;
         private Timer _keepAliveTimer;
         private ConnectionInfo _connectionInfo;
@@ -243,15 +245,34 @@ namespace Renci.SshNet
         }
 
         /// <summary>
-        /// Sends keep-alive message to the server.
+        /// Sends a keep-alive message to the server.
         /// </summary>
+        /// <remarks>
+        /// Use <see cref="KeepAliveInterval"/> to configure the client to send a keep-alive at regular
+        /// intervals.
+        /// </remarks>
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        [Obsolete("Use KeepAliveInterval to send a keep-alive message at regular intervals.")]
         public void SendKeepAlive()
         {
             CheckDisposed();
 
-            if (Session != null && Session.IsConnected)
-                Session.SendKeepAlive();
+            // only send keep-alive message when we still have a session
+            if (Session != null)
+            {
+                // do not send multiple keep-alive messages concurrently
+                if (Monitor.TryEnter(_keepAliveLock))
+                {
+                    try
+                    {
+                        Session.TrySendMessage(new IgnoreMessage());
+                    }
+                    finally
+                    {
+                        Monitor.Exit(_keepAliveLock);
+                    }
+                }
+            }
         }
 
         /// <summary>

+ 45 - 30
Renci.SshClient/Renci.SshNet/Channels/Channel.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Net.Sockets;
 using System.Threading;
 using Renci.SshNet.Common;
 using Renci.SshNet.Messages;
@@ -12,6 +13,10 @@ namespace Renci.SshNet.Channels
     /// </summary>
     internal abstract class Channel : IChannel
     {
+        private const int Initial = 0;
+        private const int Considered = 1;
+        private const int Sent = 2;
+
         private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(false);
         private EventWaitHandle _channelServerWindowAdjustWaitHandle = new ManualResetEvent(false);
         private EventWaitHandle _errorOccuredWaitHandle = new ManualResetEvent(false);
@@ -23,13 +28,14 @@ namespace Renci.SshNet.Channels
         private ISession _session;
 
         /// <summary>
-        /// Holds a value indicating whether a SSH_MSG_CHANNEL_CLOSE has been sent to the other party.
+        /// Holds a value indicating whether the SSH_MSG_CHANNEL_CLOSE has been sent to the remote party.
         /// </summary>
         /// <value>
-        /// <c>true</c> when a SSH_MSG_CHANNEL_CLOSE message has been sent to the other party;
-        /// otherwise, <c>false</c>.
+        /// <c>0</c> when the SSH_MSG_CHANNEL_CLOSE message has not been sent or considered
+        /// <c>1</c> when sending a SSH_MSG_CHANNEL_CLOSE message to the remote party is under consideration
+        /// <c>2</c> when this message has been sent to the remote party
         /// </value>
-        private bool _closeMessageSent;
+        private int _closeMessageSent;
 
         /// <summary>
         /// Holds a value indicating whether a SSH_MSG_CHANNEL_CLOSE has been received from the other
@@ -51,11 +57,12 @@ namespace Renci.SshNet.Channels
         private bool _eofMessageReceived;
 
         /// <summary>
-        /// Holds a value indicating whether the SSH_MSG_CHANNEL_EOF has been sent to the other party.
+        /// Holds a value indicating whether the SSH_MSG_CHANNEL_EOF has been sent to the remote party.
         /// </summary>
         /// <value>
-        /// <c>0</c> when the SSH_MSG_CHANNEL_EOF message has not been sent to the other party, and
-        /// <c>1</c> when this message was already sent.
+        /// <c>0</c> when the SSH_MSG_CHANNEL_EOF message has not been sent or considered
+        /// <c>1</c> when sending a SSH_MSG_CHANNEL_EOF message to the remote party is under consideration
+        /// <c>2</c> when this message has been sent to the remote party
         /// </value>
         private int _eofMessageSent;
 
@@ -411,6 +418,23 @@ namespace Renci.SshNet.Channels
             }
         }
 
+        /// <summary>
+        /// Sends a message to the server.
+        /// </summary>
+        /// <param name="message">The message to send.</param>
+        /// <returns>
+        /// <c>true</c> if the message was sent to the server; otherwise, <c>false</c>.
+        /// </returns>
+        /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the protocol.</exception>
+        /// <remarks>
+        /// This methods returns <c>false</c> when the attempt to send the message results in a
+        /// <see cref="SocketException"/> or a <see cref="SshException"/>.
+        /// </remarks>
+        private bool TrySendMessage(Message message)
+        {
+            return _session.TrySendMessage(message);
+        }
+
         /// <summary>
         /// Sends SSH message to the server.
         /// </summary>
@@ -424,15 +448,6 @@ namespace Renci.SshNet.Channels
             _session.SendMessage(message);
         }
 
-        /// <summary>
-        /// Sends close channel message to the server, and marks the channel closed.
-        /// </summary>
-        /// <param name="message">The message to send.</param>
-        private void SendMessage(ChannelCloseMessage message)
-        {
-            _session.SendMessage(message);
-        }
-
         /// <summary>
         /// Sends channel data message to the servers.
         /// </summary>
@@ -555,23 +570,23 @@ namespace Renci.SshNet.Channels
             //
             // as a solution for this issue we only send a SSH_MSG_CHANNEL_EOF message if we haven't received a
             // SSH_MSG_CHANNEL_EOF or SSH_MSG_CHANNEL_CLOSE message from the remote party
-            if (!_closeMessageReceived && !_eofMessageReceived && IsOpen && IsConnected)
+            if (Interlocked.CompareExchange(ref _eofMessageSent, Considered, Initial) == Initial)
             {
-                if (Interlocked.CompareExchange(ref _eofMessageSent, 1, 0) == 0)
-                    SendMessage(new ChannelEofMessage(RemoteChannelNumber));
+                if (!_closeMessageReceived && !_eofMessageReceived && IsOpen && IsConnected)
+                {
+                    if (TrySendMessage(new ChannelEofMessage(RemoteChannelNumber)))
+                        _eofMessageSent = Sent;
+                }
             }
 
             // send message to close the channel on the server
-            // ignore sending close message when client not connected
-            if (!_closeMessageSent && IsOpen && IsConnected)
+            if (Interlocked.CompareExchange(ref _closeMessageSent, Considered, Initial) == Initial)
             {
-                lock (this)
+                // ignore sending close message when client is not connected or the channel is closed
+                if (IsOpen && IsConnected)
                 {
-                    if (!_closeMessageSent)
-                    {
-                        SendMessage(new ChannelCloseMessage(RemoteChannelNumber));
-                        _closeMessageSent = true;
-                    }
+                    if (TrySendMessage(new ChannelCloseMessage(RemoteChannelNumber)))
+                        _closeMessageSent = Sent;
                 }
             }
 
@@ -580,17 +595,17 @@ namespace Renci.SshNet.Channels
 
             // wait for channel to be closed if we actually sent a close message (either to initiate closing
             // the channel, or as response to a SSH_MSG_CHANNEL_CLOSE message sent by the server
-            if (wait && _closeMessageSent)
+            if (wait && _closeMessageSent == Sent)
             {
                 WaitOnHandle(_channelClosedWaitHandle);
             }
 
             // reset indicators in case we want to reopen the channel; these are safe to reset
             // since the channel is marked closed by now
-            _eofMessageSent = 0;
+            _eofMessageSent = Initial;
             _eofMessageReceived = false;
             _closeMessageReceived = false;
-            _closeMessageSent = false;
+            _closeMessageSent = Initial;
         }
 
         protected virtual void OnDisconnected()

+ 21 - 10
Renci.SshClient/Renci.SshNet/ClientAuthentication.cs

@@ -67,10 +67,8 @@ namespace Renci.SshNet
                 return false;
             }
 
-            for (var i = 0; i < matchingAuthenticationMethods.Count; i++)
+            foreach (var authenticationMethod in GetOrderedAuthenticationMethods(authenticationState, matchingAuthenticationMethods))
             {
-                var authenticationMethod = matchingAuthenticationMethods[i];
-
                 if (authenticationState.FailedAuthenticationMethods.Contains(authenticationMethod))
                     continue;
 
@@ -79,13 +77,7 @@ namespace Renci.SshNet
                 // a stack overflow for servers that do not update the list of allowed authentication
                 // methods after a partial success
 
-                if (authenticationState.ExecutedAuthenticationMethods.Contains(authenticationMethod))
-                {
-                    var isLastAuthenticationMethod = i == (matchingAuthenticationMethods.Count - 1);
-                    if (!isLastAuthenticationMethod)
-                        continue;
-                }
-                else
+                if (!authenticationState.ExecutedAuthenticationMethods.Contains(authenticationMethod))
                 {
                     // update state to reflect previosuly executed authentication methods
                     authenticationState.ExecutedAuthenticationMethods.Add(authenticationMethod);
@@ -116,6 +108,25 @@ namespace Renci.SshNet
             return false;
         }
 
+        private IEnumerable<IAuthenticationMethod> GetOrderedAuthenticationMethods(AuthenticationState authenticationState, IEnumerable<IAuthenticationMethod> matchingAuthenticationMethods)
+        {
+            var skippedAuthenticationMethods = new List<IAuthenticationMethod>();
+
+            foreach (var authenticationMethod in matchingAuthenticationMethods)
+            {
+                if (authenticationState.ExecutedAuthenticationMethods.Contains(authenticationMethod))
+                {
+                    skippedAuthenticationMethods.Add(authenticationMethod);
+                    continue;
+                }
+
+                yield return authenticationMethod;
+            }
+
+            foreach (var authenticationMethod in skippedAuthenticationMethods)
+                yield return authenticationMethod;
+        }
+
         private class AuthenticationState
         {
             private readonly IList<IAuthenticationMethod> _supportedAuthenticationMethods;

+ 14 - 5
Renci.SshClient/Renci.SshNet/ISession.cs

@@ -98,11 +98,6 @@ namespace Renci.SshNet
         /// <param name="messageName">The name of the message to register with the session.</param>
         void RegisterMessage(string messageName);
 
-        /// <summary>
-        /// Sends "keep alive" message to keep connection alive.
-        /// </summary>
-        void SendKeepAlive();
-
         /// <summary>
         /// Sends a message to the server.
         /// </summary>
@@ -112,6 +107,20 @@ namespace Renci.SshNet
         /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the protocol.</exception>
         void SendMessage(Message message);
 
+        /// <summary>
+        /// Sends a message to the server.
+        /// </summary>
+        /// <param name="message">The message to send.</param>
+        /// <returns>
+        /// <c>true</c> if the message was sent to the server; otherwise, <c>false</c>.
+        /// </returns>
+        /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the protocol.</exception>
+        /// <remarks>
+        /// This methods returns <c>false</c> when the attempt to send the message results in a
+        /// <see cref="SocketException"/> or a <see cref="SshException"/>.
+        /// </remarks>
+        bool TrySendMessage(Message message);
+
         /// <summary>
         /// Unregister SSH message from the session.
         /// </summary>

+ 50 - 9
Renci.SshClient/Renci.SshNet/Session.cs

@@ -676,14 +676,6 @@ namespace Renci.SshNet
             }
         }
 
-        /// <summary>
-        /// Sends "keep alive" message to keep connection alive.
-        /// </summary>
-        void ISession.SendKeepAlive()
-        {
-            SendMessage(new IgnoreMessage());
-        }
-
         /// <summary>
         /// Waits for the specified handle or the exception handle for the receive thread
         /// to signal within the connection timeout.
@@ -885,6 +877,37 @@ namespace Renci.SshNet
             }
         }
 
+        /// <summary>
+        /// Sends a message to the server.
+        /// </summary>
+        /// <param name="message">The message to send.</param>
+        /// <returns>
+        /// <c>true</c> if the message was sent to the server; otherwise, <c>false</c>.
+        /// </returns>
+        /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the protocol.</exception>
+        /// <remarks>
+        /// This methods returns <c>false</c> when the attempt to send the message results in a
+        /// <see cref="SocketException"/> or a <see cref="SshException"/>.
+        /// </remarks>
+        private bool TrySendMessage(Message message)
+        {
+            try
+            {
+                SendMessage(message);
+                return true;
+            }
+            catch (SshException ex)
+            {
+                Log(string.Format("Failure sending message server '{0}': '{1}' => {2}", message.GetType().Name, message, ex));
+                return false;
+            }
+            catch (SocketException ex)
+            {
+                Log(string.Format("Failure sending message server '{0}': '{1}' => {2}", message.GetType().Name, message, ex));
+                return false;
+            }
+        }
+
         private static IEnumerable<MessageMetadata> GetMessagesMetadata()
         {
             return new []
@@ -1028,7 +1051,8 @@ namespace Renci.SshNet
 
             var disconnectMessage = new DisconnectMessage(reasonCode, message);
 
-            SendMessage(disconnectMessage);
+            // send the disconnect message, but ignore the outcome
+            TrySendMessage(disconnectMessage);
 
             _isDisconnectMessageSent = true;
         }
@@ -2267,6 +2291,23 @@ namespace Renci.SshNet
             SendMessage(message);
         }
 
+        /// <summary>
+        /// Sends a message to the server.
+        /// </summary>
+        /// <param name="message">The message to send.</param>
+        /// <returns>
+        /// <c>true</c> if the message was sent to the server; otherwise, <c>false</c>.
+        /// </returns>
+        /// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the protocol.</exception>
+        /// <remarks>
+        /// This methods returns <c>false</c> when the attempt to send the message results in a
+        /// <see cref="SocketException"/> or a <see cref="SshException"/>.
+        /// </remarks>
+        bool ISession.TrySendMessage(Message message)
+        {
+            return TrySendMessage(message);
+        }
+
         #endregion ISession implementation
 
         private class MessageMetadata