mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-10 09:15:47 +00:00
Fix max open channels for session type channel
Fix some CA warnings
This commit is contained in:
@@ -41,6 +41,10 @@ namespace Renci.SshClient.Channels
|
||||
get { return this._session.IsConnected; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the connection info.
|
||||
/// </summary>
|
||||
/// <value>The connection info.</value>
|
||||
protected ConnectionInfo ConnectionInfo
|
||||
{
|
||||
get
|
||||
@@ -49,6 +53,21 @@ namespace Renci.SshClient.Channels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the session semaphore to control number of session channels
|
||||
/// </summary>
|
||||
/// <value>The session semaphore.</value>
|
||||
protected SemaphoreSlim SessionSemaphore
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._session.SessionSemaphore;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Channel"/> class.
|
||||
/// </summary>
|
||||
internal Channel()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace Renci.SshClient.Channels
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exp)
|
||||
catch (Exception)
|
||||
{
|
||||
readerTaskError.Set();
|
||||
throw;
|
||||
|
||||
@@ -7,34 +7,19 @@ namespace Renci.SshClient.Channels
|
||||
{
|
||||
internal abstract class ChannelSession : Channel
|
||||
{
|
||||
// TODO: Some debug information to be removed later
|
||||
private static volatile int _totalOpenRequests;
|
||||
private static volatile int _totalConfirmation = 0;
|
||||
private static volatile int _totalClose = 0;
|
||||
private static volatile int _totalFailed = 0;
|
||||
|
||||
private volatile static int _channelSessionCounter = 0;
|
||||
|
||||
private static object _lock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Counts faile channel open attempts
|
||||
/// </summary>
|
||||
private int _failedOpenAttempts;
|
||||
|
||||
/// <summary>
|
||||
/// Wait handle to signal when response was received to open the channel
|
||||
/// </summary>
|
||||
private EventWaitHandle _channelOpenResponseWaitHandle = new AutoResetEvent(false);
|
||||
|
||||
public bool CanCreateChannel
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ChannelSession._channelSessionCounter < 10)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the channel
|
||||
/// </summary>
|
||||
public virtual void Open()
|
||||
{
|
||||
if (!this.IsOpen)
|
||||
@@ -53,46 +38,58 @@ namespace Renci.SshClient.Channels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when chanel is open
|
||||
/// </summary>
|
||||
/// <param name="remoteChannelNumber">The remote channel number.</param>
|
||||
/// <param name="initialWindowSize">Initial size of the window.</param>
|
||||
/// <param name="maximumPacketSize">Maximum size of the packet.</param>
|
||||
protected override void OnOpenConfirmation(uint remoteChannelNumber, uint initialWindowSize, uint maximumPacketSize)
|
||||
{
|
||||
base.OnOpenConfirmation(remoteChannelNumber, initialWindowSize, maximumPacketSize);
|
||||
|
||||
ChannelSession._channelSessionCounter++;
|
||||
Debug.WriteLine(string.Format("channel {0} open. open channels {1}", this.RemoteChannelNumber, ChannelSession._channelSessionCounter));
|
||||
|
||||
_totalConfirmation++;
|
||||
Debug.WriteLine(string.Format("channel {0} open.", this.RemoteChannelNumber));
|
||||
|
||||
this._channelOpenResponseWaitHandle.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when channel is closed
|
||||
/// </summary>
|
||||
protected override void OnClose()
|
||||
{
|
||||
base.OnClose();
|
||||
|
||||
ChannelSession._channelSessionCounter--;
|
||||
Debug.WriteLine(string.Format("channel {0} closed", this.RemoteChannelNumber));
|
||||
|
||||
Debug.WriteLine(string.Format("channel {0} closed. open channels {1}", this.RemoteChannelNumber, ChannelSession._channelSessionCounter));
|
||||
|
||||
_totalClose++;
|
||||
// This timeout needed since when channel is closed it does not immidiatly becomes availble
|
||||
// but it takes time for the server to clean up resource and allow new channels to be created.
|
||||
Thread.Sleep(100);
|
||||
|
||||
_slim.Release();
|
||||
this.SessionSemaphore.Release();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when channel failed to open
|
||||
/// </summary>
|
||||
/// <param name="reasonCode">The reason code.</param>
|
||||
/// <param name="description">The description.</param>
|
||||
/// <param name="language">The language.</param>
|
||||
protected override void OnOpenFailure(uint reasonCode, string description, string language)
|
||||
{
|
||||
// TODO: See why occasionaly open channel will fail when try to utilze maximum number of channels
|
||||
|
||||
this._failedOpenAttempts++;
|
||||
|
||||
Debug.WriteLine(string.Format("Local channel: {0} attempts: {1} max channels: {2}", this.LocalChannelNumber, this._failedOpenAttempts, ChannelSession._channelSessionCounter));
|
||||
Debug.WriteLine(string.Format("Local channel: {0} attempts: {1}.", this.LocalChannelNumber, this._failedOpenAttempts));
|
||||
|
||||
_totalFailed++;
|
||||
|
||||
_slim.Release();
|
||||
this.SessionSemaphore.Release();
|
||||
|
||||
this._channelOpenResponseWaitHandle.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when object is being disposed.
|
||||
/// </summary>
|
||||
protected override void OnDisposing()
|
||||
{
|
||||
if (this._channelOpenResponseWaitHandle != null)
|
||||
@@ -101,23 +98,24 @@ namespace Renci.SshClient.Channels
|
||||
}
|
||||
}
|
||||
|
||||
private static SemaphoreSlim _slim = new SemaphoreSlim(10);
|
||||
|
||||
/// <summary>
|
||||
/// Sends the channel open message.
|
||||
/// </summary>
|
||||
protected void SendChannelOpenMessage()
|
||||
{
|
||||
_slim.Wait();
|
||||
|
||||
Debug.WriteLine(string.Format("send open new channel, total channels {0}", ChannelSession._channelSessionCounter));
|
||||
|
||||
this.SendMessage(new ChannelOpenMessage
|
||||
lock (this.SessionSemaphore)
|
||||
{
|
||||
ChannelType = ChannelTypes.Session,
|
||||
LocalChannelNumber = this.LocalChannelNumber,
|
||||
InitialWindowSize = this.LocalWindowSize,
|
||||
MaximumPacketSize = this.PacketSize,
|
||||
});
|
||||
// Ensure that channels are available
|
||||
this.SessionSemaphore.Wait();
|
||||
|
||||
_totalOpenRequests++;
|
||||
this.SendMessage(new ChannelOpenMessage
|
||||
{
|
||||
ChannelType = ChannelTypes.Session,
|
||||
LocalChannelNumber = this.LocalChannelNumber,
|
||||
InitialWindowSize = this.LocalWindowSize,
|
||||
MaximumPacketSize = this.PacketSize,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,16 @@ namespace Renci.SshClient.Channels
|
||||
{
|
||||
internal class ChannelSessionExec : ChannelSession
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds channel data stream
|
||||
/// </summary>
|
||||
private Stream _channelData;
|
||||
|
||||
/// <summary>
|
||||
/// Holds channel extended data stream
|
||||
/// </summary>
|
||||
private Stream _channelExtendedData;
|
||||
|
||||
private Exception _exception;
|
||||
|
||||
private ChannelAsyncResult _asyncResult;
|
||||
|
||||
private AsyncCallback _callback;
|
||||
@@ -23,16 +27,36 @@ namespace Renci.SshClient.Channels
|
||||
get { return ChannelTypes.Session; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this channel has error.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance has error; otherwise, <c>false</c>.</value>
|
||||
public bool HasError { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the exit status.
|
||||
/// </summary>
|
||||
/// <value>The exit status.</value>
|
||||
public uint ExitStatus { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ChannelSessionExec"/> class.
|
||||
/// </summary>
|
||||
public ChannelSessionExec()
|
||||
: base()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begins the execute.
|
||||
/// </summary>
|
||||
/// <param name="command">The command.</param>
|
||||
/// <param name="output">The output.</param>
|
||||
/// <param name="extendedOutput">The extended output.</param>
|
||||
/// <param name="callback">The callback.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <returns></returns>
|
||||
internal ChannelAsyncResult BeginExecute(string command, Stream output, Stream extendedOutput, AsyncCallback callback, object state)
|
||||
{
|
||||
// Prevent from executing BeginExecute before calling EndExecute
|
||||
@@ -67,6 +91,10 @@ namespace Renci.SshClient.Channels
|
||||
return _asyncResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the execute.
|
||||
/// </summary>
|
||||
/// <param name="result">The result.</param>
|
||||
internal void EndExecute(IAsyncResult result)
|
||||
{
|
||||
ChannelAsyncResult channelAsyncResult = result as ChannelAsyncResult;
|
||||
@@ -82,15 +110,11 @@ namespace Renci.SshClient.Channels
|
||||
this.Close();
|
||||
|
||||
this._asyncResult = null;
|
||||
|
||||
if (this._exception != null)
|
||||
{
|
||||
var exception = this._exception;
|
||||
this._exception = null; // Clean exception
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when channel is closed
|
||||
/// </summary>
|
||||
protected override void OnClose()
|
||||
{
|
||||
base.OnClose();
|
||||
@@ -114,6 +138,10 @@ namespace Renci.SshClient.Channels
|
||||
((EventWaitHandle)_asyncResult.AsyncWaitHandle).Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when channel receives data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
protected override void OnData(string data)
|
||||
{
|
||||
base.OnData(data);
|
||||
@@ -132,6 +160,11 @@ namespace Renci.SshClient.Channels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when channel receives extended data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="dataTypeCode">The data type code.</param>
|
||||
protected override void OnExtendedData(string data, uint dataTypeCode)
|
||||
{
|
||||
base.OnExtendedData(data, dataTypeCode);
|
||||
@@ -150,6 +183,14 @@ namespace Renci.SshClient.Channels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when channel request command is called.
|
||||
/// </summary>
|
||||
/// <param name="requestName">Name of the request.</param>
|
||||
/// <param name="wantReply">if set to <c>true</c> then need to send reply to server.</param>
|
||||
/// <param name="command">The command.</param>
|
||||
/// <param name="subsystemName">Name of the subsystem.</param>
|
||||
/// <param name="exitStatus">The exit status.</param>
|
||||
protected override void OnRequest(ChannelRequestNames requestName, bool wantReply, string command, string subsystemName, uint exitStatus)
|
||||
{
|
||||
base.OnRequest(requestName, wantReply, command, subsystemName, exitStatus);
|
||||
@@ -183,7 +224,9 @@ namespace Renci.SshClient.Channels
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called when object is being disposed.
|
||||
/// </summary>
|
||||
protected override void OnDisposing()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using Renci.SshClient.Channels;
|
||||
using Renci.SshClient.Messages.Connection;
|
||||
namespace Renci.SshClient
|
||||
{
|
||||
public class ForwardedPortRemote : ForwardedPort
|
||||
public class ForwardedPortRemote : ForwardedPort, IDisposable
|
||||
{
|
||||
private bool _requestStatus;
|
||||
|
||||
@@ -96,5 +96,47 @@ namespace Renci.SshClient
|
||||
|
||||
this._globalRequestResponse.Set();
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
private bool disposed = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// Check to see if Dispose has already been called.
|
||||
if (!this.disposed)
|
||||
{
|
||||
// If disposing equals true, dispose all managed
|
||||
// and unmanaged resources.
|
||||
if (disposing)
|
||||
{
|
||||
// Dispose managed resources.
|
||||
if (this._globalRequestResponse != null)
|
||||
{
|
||||
this._globalRequestResponse.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Note disposing has been done.
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~ForwardedPortRemote()
|
||||
{
|
||||
// Do not re-create Dispose clean-up code here.
|
||||
// Calling Dispose(false) is optimal in terms of
|
||||
// readability and maintainability.
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,35 @@ namespace Renci.SshClient
|
||||
/// </summary>
|
||||
private DisconnectMessage _disconnectMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Hold session specific semaphores
|
||||
/// </summary>
|
||||
private List<SemaphoreSlim> _semaphores = new List<SemaphoreSlim>();
|
||||
|
||||
private SemaphoreSlim _sessionSemaphore;
|
||||
/// <summary>
|
||||
/// Gets the session semaphore that controls session channels.
|
||||
/// </summary>
|
||||
/// <value>The session semaphore.</value>
|
||||
public SemaphoreSlim SessionSemaphore
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._sessionSemaphore == null)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (this._sessionSemaphore == null)
|
||||
{
|
||||
this._sessionSemaphore = new SemaphoreSlim(this.ConnectionInfo.MaxSessions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this._sessionSemaphore;
|
||||
}
|
||||
}
|
||||
|
||||
private uint _nextChannelNumber;
|
||||
/// <summary>
|
||||
/// Gets the next channel number.
|
||||
@@ -1140,6 +1169,11 @@ namespace Renci.SshClient
|
||||
{
|
||||
this._keyExhcange.Dispose();
|
||||
}
|
||||
|
||||
if (this._sessionSemaphore != null)
|
||||
{
|
||||
this._sessionSemaphore.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Note disposing has been done.
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Renci.SshClient
|
||||
{
|
||||
public class SshClient
|
||||
public class SshClient : IDisposable
|
||||
{
|
||||
private Session _session;
|
||||
|
||||
private ConnectionInfo _connectionInfo;
|
||||
|
||||
private List<ForwardedPort> _forwardedPorts = new List<ForwardedPort>();
|
||||
|
||||
public ConnectionInfo ConnectionInfo { get; private set; }
|
||||
|
||||
private Sftp _sftp;
|
||||
/// <summary>
|
||||
/// Gets the shell.
|
||||
@@ -37,8 +38,8 @@ namespace Renci.SshClient
|
||||
|
||||
public SshClient(ConnectionInfo connectionInfo)
|
||||
{
|
||||
this._connectionInfo = connectionInfo;
|
||||
this._session = new Session(this._connectionInfo);
|
||||
this.ConnectionInfo = connectionInfo;
|
||||
this._session = new Session(connectionInfo);
|
||||
}
|
||||
|
||||
public SshClient(string host, int port, string username, string password)
|
||||
@@ -85,7 +86,7 @@ namespace Renci.SshClient
|
||||
|
||||
public void Connect()
|
||||
{
|
||||
this._session = new Session(this._connectionInfo);
|
||||
this._session = new Session(this.ConnectionInfo);
|
||||
this._session.Connect();
|
||||
}
|
||||
|
||||
@@ -131,5 +132,47 @@ namespace Renci.SshClient
|
||||
{
|
||||
this._forwardedPorts.Remove(port);
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
private bool disposed = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// Check to see if Dispose has already been called.
|
||||
if (!this.disposed)
|
||||
{
|
||||
// If disposing equals true, dispose all managed
|
||||
// and unmanaged resources.
|
||||
if (disposing)
|
||||
{
|
||||
// Dispose managed resources.
|
||||
if (this._session != null)
|
||||
{
|
||||
this._session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Note disposing has been done.
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~SshClient()
|
||||
{
|
||||
// Do not re-create Dispose clean-up code here.
|
||||
// Calling Dispose(false) is optimal in terms of
|
||||
// readability and maintainability.
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,6 @@ namespace Renci.SshClient
|
||||
return this.Execute();
|
||||
}
|
||||
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
private bool disposed = false;
|
||||
|
||||
Reference in New Issue
Block a user