fix: changed SecureTcpClient to private. fix: removed the ConnectionHasHungCallbackDelegate, was unused.

This commit is contained in:
Jason DeVito
2021-09-15 08:01:47 -05:00
parent 1d04442625
commit e8eeda36c1

View File

@@ -8,9 +8,12 @@ using Crestron.SimplSharp.CrestronSockets;
namespace PepperDash.Core namespace PepperDash.Core
{ {
/// <summary>
/// A class to handle secure TCP/IP communications with a server
/// </summary>
public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
{ {
private const string SplusKey = "Uninitialized Secure Tcp Client"; private const string SplusKey = "Uninitialized Secure Tcp _client";
/// <summary> /// <summary>
/// Stream debugging /// Stream debugging
/// </summary> /// </summary>
@@ -26,12 +29,41 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived; public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
#region GenericSecureTcpIpClient Events & Delegates
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange; //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
public event EventHandler<GenericTcpServerSocketStatusChangeEventArgs> ConnectionChange; public event EventHandler<GenericTcpServerSocketStatusChangeEventArgs> ConnectionChange;
// TODO [ ] review event handler to determine best implementation for secure class
event EventHandler<GenericSocketStatusChageEventArgs> ISocketStatus.ConnectionChange
{
add { throw new NotImplementedException(); }
remove { throw new NotImplementedException(); }
}
/// <summary>
/// Auto reconnect evant handler
/// </summary>
public event EventHandler AutoReconnectTriggered;
/// <summary>
/// Event for Receiving text. Once subscribed to this event the receive callback will start a thread that dequeues the messages and invokes the event on a new thread.
/// It is not recommended to use both the TextReceived event and the TextReceivedQueueInvoke event.
/// </summary>
public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceivedQueueInvoke;
/// <summary>
/// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require
/// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.
/// </summary>
public event EventHandler<GenericTcpServerClientReadyForcommunicationsEventArgs> ClientReadyForCommunications;
#endregion
#region GenricTcpIpClient properties #region GenricTcpIpClient properties
private string _hostname; private string _hostname;
@@ -45,9 +77,9 @@ namespace PepperDash.Core
set set
{ {
_hostname = value; _hostname = value;
if (Client != null) if (_client != null)
{ {
Client.AddressClientConnectedTo = _hostname; _client.AddressClientConnectedTo = _hostname;
} }
} }
} }
@@ -74,14 +106,14 @@ namespace PepperDash.Core
/// <summary> /// <summary>
/// Internal secure client /// Internal secure client
/// </summary> /// </summary>
public SecureTCPClient Client { get; private set; } private SecureTCPClient _client;
/// <summary> /// <summary>
/// Bool showing if socket is connected /// Bool showing if socket is connected
/// </summary> /// </summary>
public bool IsConnected public bool IsConnected
{ {
get { return Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; } get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
} }
/// <summary> /// <summary>
@@ -93,13 +125,13 @@ namespace PepperDash.Core
} }
/// <summary> /// <summary>
/// Client socket status Read only /// _client socket status Read only
/// </summary> /// </summary>
public SocketStatus ClientStatus public SocketStatus ClientStatus
{ {
get get
{ {
return Client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : Client.ClientStatus; return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;
} }
} }
@@ -151,7 +183,7 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public bool Connected public bool Connected
{ {
get { return Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; } get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
} }
// private Timer for auto reconnect // private Timer for auto reconnect
@@ -248,6 +280,23 @@ namespace PepperDash.Core
bool ProgramIsStopping; bool ProgramIsStopping;
/// <summary>
/// Queue lock
/// </summary>
CCriticalSection DequeueLock = new CCriticalSection();
/// <summary>
/// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before
/// calling initialize.
/// </summary>
public int ReceiveQueueSize { get; set; }
/// <summary>
/// Queue to temporarily store received messages with the source IP and Port info. Defaults to size 20. Use constructor or set queue size property before
/// calling initialize.
/// </summary>
private CrestronQueue<GenericTcpServerCommMethodReceiveTextArgs> MessageQueue;
#endregion #endregion
#region Constructors #region Constructors
@@ -367,7 +416,7 @@ namespace PepperDash.Core
{ {
if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused) if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused)
{ {
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing Client connection"); Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing _client connection");
ProgramIsStopping = true; ProgramIsStopping = true;
Disconnect(); Disconnect();
} }
@@ -376,9 +425,9 @@ namespace PepperDash.Core
public override bool Deactivate() public override bool Deactivate()
{ {
if (Client != null) if (_client != null)
{ {
Client.SocketStatusChange -= this.Client_SocketStatusChange; _client.SocketStatusChange -= this.Client_SocketStatusChange;
DisconnectClient(); DisconnectClient();
} }
return true; return true;
@@ -428,18 +477,18 @@ namespace PepperDash.Core
} }
// clean up previous client // clean up previous client
if (Client != null) if (_client != null)
{ {
Disconnect(); Disconnect();
} }
DisconnectCalledByUser = false; DisconnectCalledByUser = false;
Client = new SecureTCPClient(Hostname, Port, BufferSize); _client = new SecureTCPClient(Hostname, Port, BufferSize);
Client.SocketStatusChange += Client_SocketStatusChange; _client.SocketStatusChange += Client_SocketStatusChange;
if (HeartbeatEnabled) if (HeartbeatEnabled)
Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5); _client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
Client.AddressClientConnectedTo = Hostname; _client.AddressClientConnectedTo = Hostname;
Client.PortNumber = Port; _client.PortNumber = Port;
// SecureClient = c; // SecureClient = c;
//var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff"); //var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
@@ -461,7 +510,7 @@ namespace PepperDash.Core
}, 30000); }, 30000);
Debug.Console(2, this, "Making Connection Count:{0}", ConnectionCount); Debug.Console(2, this, "Making Connection Count:{0}", ConnectionCount);
Client.ConnectToServerAsync(o => _client.ConnectToServerAsync(o =>
{ {
Debug.Console(2, this, "ConnectToServerAsync Count:{0} Ran!", ConnectionCount); Debug.Console(2, this, "ConnectToServerAsync Count:{0} Ran!", ConnectionCount);
@@ -473,7 +522,7 @@ namespace PepperDash.Core
if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED) if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
{ {
Debug.Console(2, this, "Client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient); Debug.Console(2, this, "_client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);
o.ReceiveDataAsync(Receive); o.ReceiveDataAsync(Receive);
if (SharedKeyRequired) if (SharedKeyRequired)
@@ -509,7 +558,7 @@ namespace PepperDash.Core
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Client connection exception: {0}", ex.Message); Debug.Console(0, this, Debug.ErrorLogLevel.Error, "_client connection exception: {0}", ex.Message);
IsTryingToConnect = false; IsTryingToConnect = false;
CheckClosedAndTryReconnect(); CheckClosedAndTryReconnect();
} }
@@ -531,7 +580,7 @@ namespace PepperDash.Core
RetryTimer = null; RetryTimer = null;
} }
if (Client != null) if (_client != null)
{ {
DisconnectClient(); DisconnectClient();
Debug.Console(1, this, "Disconnected"); Debug.Console(1, this, "Disconnected");
@@ -543,19 +592,19 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public void DisconnectClient() public void DisconnectClient()
{ {
if (Client == null) return; if (_client == null) return;
Debug.Console(1, this, "Disconnecting client"); Debug.Console(1, this, "Disconnecting client");
if (IsConnected) if (IsConnected)
Client.DisconnectFromServer(); _client.DisconnectFromServer();
// close up client. ALWAYS use this when disconnecting. // close up client. ALWAYS use this when disconnecting.
IsTryingToConnect = false; IsTryingToConnect = false;
Debug.Console(2, this, "Disconnecting Client {0}", DisconnectCalledByUser ? ", Called by user" : ""); Debug.Console(2, this, "Disconnecting _client {0}", DisconnectCalledByUser ? ", Called by user" : "");
Client.SocketStatusChange -= Client_SocketStatusChange; _client.SocketStatusChange -= Client_SocketStatusChange;
Client.Dispose(); _client.Dispose();
Client = null; _client = null;
if (ConnectFailTimer == null) return; if (ConnectFailTimer == null) return;
ConnectFailTimer.Stop(); ConnectFailTimer.Stop();
@@ -570,13 +619,13 @@ namespace PepperDash.Core
//{ //{
// IsTryingToConnect = false; // IsTryingToConnect = false;
// if (Client != null) // if (_client != null)
// { // {
// //SecureClient.DisconnectFromServer(); // //SecureClient.DisconnectFromServer();
// Debug.Console(2, this, "Disconnecting Client {0}", DisconnectCalledByUser ? ", Called by user" : ""); // Debug.Console(2, this, "Disconnecting _client {0}", DisconnectCalledByUser ? ", Called by user" : "");
// Client.SocketStatusChange -= Client_SocketStatusChange; // _client.SocketStatusChange -= Client_SocketStatusChange;
// Client.Dispose(); // _client.Dispose();
// Client = null; // _client = null;
// } // }
// if (ConnectFailTimer != null) // if (ConnectFailTimer != null)
// { // {
@@ -586,136 +635,16 @@ namespace PepperDash.Core
// } // }
//} //}
/// <summary>
/// Band aid delegate for choked server
/// </summary>
internal delegate void ConnectionHasHungCallbackDelegate();
#region Events
//public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
event EventHandler<GenericCommMethodReceiveTextArgs> ICommunicationReceiver.TextReceived
{
add { throw new NotImplementedException(); }
remove { throw new NotImplementedException(); }
}
public event EventHandler AutoReconnectTriggered;
/// <summary>
/// Event for Receiving text. Once subscribed to this event the receive callback will start a thread that dequeues the messages and invokes the event on a new thread.
/// It is not recommended to use both the TextReceived event and the TextReceivedQueueInvoke event.
/// </summary>
public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceivedQueueInvoke;
/// <summary>
/// This is something of a band-aid callback. If the client times out during the connection process, because the server
/// is stuck, this will fire. It is intended to be used by the Server class monitor client, to help
/// keep a watch on the server and reset it if necessary.
/// </summary>
internal ConnectionHasHungCallbackDelegate ConnectionHasHungCallback;
/// <summary>
/// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require
/// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.
/// </summary>
public event EventHandler<GenericTcpServerClientReadyForcommunicationsEventArgs> ClientReadyForCommunications;
#endregion
#region Properties & Variables
event EventHandler<GenericSocketStatusChageEventArgs> ISocketStatus.ConnectionChange
{
add { throw new NotImplementedException(); }
remove { throw new NotImplementedException(); }
}
/// <summary>
/// Queue lock
/// </summary>
CCriticalSection DequeueLock = new CCriticalSection();
/// <summary>
/// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before
/// calling initialize.
/// </summary>
public int ReceiveQueueSize { get; set; }
/// <summary>
/// Queue to temporarily store received messages with the source IP and Port info. Defaults to size 20. Use constructor or set queue size property before
/// calling initialize.
/// </summary>
private CrestronQueue<GenericTcpServerCommMethodReceiveTextArgs> MessageQueue;
#endregion
#region Methods #region Methods
/// <summary>
/// <summary>ff
/// Called from Connect failure or Socket Status change if /// Called from Connect failure or Socket Status change if
/// auto reconnect and socket disconnected (Not disconnected by user) /// auto reconnect and socket disconnected (Not disconnected by user)
/// </summary> /// </summary>
void CheckClosedAndTryReconnect() void CheckClosedAndTryReconnect()
{ {
if (Client != null) if (_client != null)
{ {
Debug.Console(2, this, "Cleaning up remotely closed/failed connection."); Debug.Console(2, this, "Cleaning up remotely closed/failed connection.");
Disconnect(); Disconnect();
@@ -751,7 +680,7 @@ namespace PepperDash.Core
{ {
var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray(); var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
Debug.Console(2, this, "Client Received:\r--------\r{0}\r--------", str); Debug.Console(2, this, "_client Received:\r--------\r{0}\r--------", str);
if (!string.IsNullOrEmpty(checkHeartbeat(str))) if (!string.IsNullOrEmpty(checkHeartbeat(str)))
{ {
@@ -926,7 +855,7 @@ namespace PepperDash.Core
} }
catch (Exception ex) catch (Exception ex)
{ {
ErrorLog.Error("Heartbeat timeout Error on Client: {0}, {1}", Key, ex); ErrorLog.Error("Heartbeat timeout Error on _client: {0}, {1}", Key, ex);
} }
} }
@@ -952,9 +881,9 @@ namespace PepperDash.Core
try try
{ {
var bytes = Encoding.GetEncoding(28591).GetBytes(text); var bytes = Encoding.GetEncoding(28591).GetBytes(text);
if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED) if (_client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
{ {
Client.SendDataAsync(bytes, bytes.Length, (c, n) => _client.SendDataAsync(bytes, bytes.Length, (c, n) =>
{ {
// HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING????? // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????
if (n <= 0) if (n <= 0)
@@ -980,8 +909,8 @@ namespace PepperDash.Core
{ {
try try
{ {
if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED) if (_client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
Client.SendData(bytes, bytes.Length); _client.SendData(bytes, bytes.Length);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -1008,7 +937,7 @@ namespace PepperDash.Core
OnConnectionChange(); OnConnectionChange();
// The client could be null or disposed by this time... // The client could be null or disposed by this time...
if (Client == null || Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED) if (_client == null || _client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
{ {
HeartbeatStop(); HeartbeatStop();
OnClientReadyForcommunications(false); // socket has gone low OnClientReadyForcommunications(false); // socket has gone low
@@ -1027,8 +956,10 @@ namespace PepperDash.Core
void OnConnectionChange() void OnConnectionChange()
{ {
var handler = ConnectionChange; var handler = ConnectionChange;
if (handler != null) if (handler == null) return;
ConnectionChange(this, new GenericTcpServerSocketStatusChangeEventArgs(this, Client.ClientStatus));
handler(this, new GenericTcpServerSocketStatusChangeEventArgs(this, _client.ClientStatus));
} }
/// <summary> /// <summary>
@@ -1037,10 +968,13 @@ namespace PepperDash.Core
void OnClientReadyForcommunications(bool isReady) void OnClientReadyForcommunications(bool isReady)
{ {
IsReadyForCommunication = isReady; IsReadyForCommunication = isReady;
if (this.IsReadyForCommunication) { HeartbeatStart(); } if (IsReadyForCommunication)
HeartbeatStart();
var handler = ClientReadyForCommunications; var handler = ClientReadyForCommunications;
if (handler != null) if (handler == null) return;
handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
} }
#endregion #endregion
} }