mirror of
https://github.com/AlexMacocian/MTSC.git
synced 2026-07-21 01:39:31 +00:00
Reconnect policy for tcp client.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using MTSC.Client;
|
||||
using MTSC.Client.Handlers;
|
||||
using MTSC.ClientSide;
|
||||
using MTSC.Common.WebSockets.ClientModules;
|
||||
using MTSC.Logging;
|
||||
using System;
|
||||
@@ -15,7 +16,8 @@ namespace MTSC_TestClient
|
||||
ChatModule chatModule = new ChatModule();
|
||||
client
|
||||
.SetServerAddress("127.0.0.1")
|
||||
.SetPort(80)
|
||||
.SetPort(800)
|
||||
.WithReconnectPolicy(ReconnectPolicy.Forever)
|
||||
.AddHandler(websocketHandler.AddModule(chatModule))
|
||||
//.AddHandler(new EncryptionHandler())
|
||||
//.AddHandler(new BroadcastHandler())
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using MTSC.Common.Ftp.FtpModules;
|
||||
using MTSC.Common.Http.RoutingModules;
|
||||
using MTSC.Common.Http.ServerModules;
|
||||
using MTSC.Common.WebSockets.ServerModules;
|
||||
using MTSC.Exceptions;
|
||||
using MTSC.Logging;
|
||||
using MTSC.ServerSide;
|
||||
@@ -16,17 +17,14 @@ namespace MTSC_TestServer
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Server server = new Server(800);
|
||||
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024);
|
||||
server
|
||||
//.WithCertificate(new X509Certificate2("powershellcert.pfx", "123"))
|
||||
.AddLogger(new ConsoleLogger())
|
||||
.AddLogger(new DebugConsoleLogger())
|
||||
.AddServerUsageMonitor(new TickrateEnforcer()
|
||||
.SetTicksPerSecond(60)
|
||||
.SetSilent(true))
|
||||
.AddExceptionHandler(new ExceptionConsoleLogger())
|
||||
.AddHandler(new HttpRoutingHandler()
|
||||
.AddRoute(MTSC.Common.Http.HttpMessage.HttpMethods.Get, "hello", new Http200Module()))
|
||||
.AddHandler(new WebsocketHandler().AddWebsocketHandler(new EchoModule()))
|
||||
.AddHandler(new FtpHandler()
|
||||
.AddModule(new AuthenticationModule())
|
||||
.AddModule(new SystModule())
|
||||
@@ -36,7 +34,7 @@ namespace MTSC_TestServer
|
||||
.AddModule(new FileModule())
|
||||
.AddModule(new QuitModule())
|
||||
.AddModule(new UnknownCommandModule()))
|
||||
.WithClientCertificate(true)
|
||||
.WithClientCertificate(false)
|
||||
.Run();
|
||||
}
|
||||
|
||||
|
||||
+130
-70
@@ -1,10 +1,12 @@
|
||||
using MTSC.Client.Handlers;
|
||||
using MTSC.ClientSide;
|
||||
using MTSC.Common;
|
||||
using MTSC.Exceptions;
|
||||
using MTSC.Logging;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
@@ -20,15 +22,14 @@ namespace MTSC.Client
|
||||
public sealed class Client
|
||||
{
|
||||
#region Fields
|
||||
TcpClient tcpClient;
|
||||
CancellationTokenSource cancelMonitorToken;
|
||||
List<IHandler> handlers = new List<IHandler>();
|
||||
List<ILogger> loggers = new List<ILogger>();
|
||||
List<IExceptionHandler> exceptionHandlers = new List<IExceptionHandler>();
|
||||
Queue<byte[]> messageQueue = new Queue<byte[]>();
|
||||
SslStream sslStream = null;
|
||||
TimeoutSuppressedStream safeNetworkStream = null;
|
||||
static Hashtable certificateErrors = new Hashtable();
|
||||
private TcpClient tcpClient;
|
||||
private CancellationTokenSource cancelMonitorToken;
|
||||
private readonly List<IHandler> handlers = new List<IHandler>();
|
||||
private readonly List<ILogger> loggers = new List<ILogger>();
|
||||
private readonly List<IExceptionHandler> exceptionHandlers = new List<IExceptionHandler>();
|
||||
private readonly Queue<byte[]> messageQueue = new Queue<byte[]>();
|
||||
private SslStream sslStream = null;
|
||||
private TimeoutSuppressedStream safeNetworkStream = null;
|
||||
#endregion
|
||||
#region Properties
|
||||
public bool Connected
|
||||
@@ -46,6 +47,11 @@ namespace MTSC.Client
|
||||
public bool ForceSsl { get; private set; }
|
||||
public Func<IPAddress[], IPAddress> AddressResolution { get; set; } = (addresses) => addresses[0];
|
||||
public Func<string, bool> SchemeSslFilter { get; set; } = RequiresSsl;
|
||||
/// <summary>
|
||||
/// Callback function used to determine if the remote certificate is valid.
|
||||
/// </summary>
|
||||
public RemoteCertificateValidationCallback CertificateValidationCallback { get; set; }
|
||||
public ReconnectPolicy ReconnectPolicy { get; set; } = ReconnectPolicy.Never;
|
||||
#endregion
|
||||
#region Constructors
|
||||
public Client(bool useSsl = false)
|
||||
@@ -55,6 +61,26 @@ namespace MTSC.Client
|
||||
#endregion
|
||||
#region Public Methods
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ReconnectPolicy"/>.
|
||||
/// </summary>
|
||||
/// <param name="reconnectPolicy"></param>
|
||||
/// <returns>This client.</returns>
|
||||
public Client WithReconnectPolicy(ReconnectPolicy reconnectPolicy)
|
||||
{
|
||||
this.ReconnectPolicy = reconnectPolicy;
|
||||
return this;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the certificate validation callback for ssl connection.
|
||||
/// </summary>
|
||||
/// <param name="remoteCertificateValidationCallback"></param>
|
||||
/// <returns>This client object.</returns>
|
||||
public Client WithRemoteCertificateValidationCallback(RemoteCertificateValidationCallback remoteCertificateValidationCallback)
|
||||
{
|
||||
this.CertificateValidationCallback = remoteCertificateValidationCallback;
|
||||
return this;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the <see cref="SchemeSslFilter"/> property.
|
||||
/// </summary>
|
||||
/// <param name="schemeFilter">Function that should return true if the provided scheme should use Ssl.</param>
|
||||
@@ -160,10 +186,6 @@ namespace MTSC.Client
|
||||
return this;
|
||||
}
|
||||
/// <summary>
|
||||
/// Callback function used to determine if the remote certificate is valid.
|
||||
/// </summary>
|
||||
public RemoteCertificateValidationCallback CertificateValidationCallback { get; set; }
|
||||
/// <summary>
|
||||
/// Attemps to connect to the specified server.
|
||||
/// </summary>
|
||||
/// <returns>True if connection was successful.</returns>
|
||||
@@ -205,10 +227,7 @@ namespace MTSC.Client
|
||||
this.safeNetworkStream = new TimeoutSuppressedStream(this.tcpClient);
|
||||
if (shouldUseSsl)
|
||||
{
|
||||
if(this.CertificateValidationCallback == null)
|
||||
{
|
||||
this.CertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
|
||||
}
|
||||
this.CertificateValidationCallback = this.CertificateValidationCallback ?? new RemoteCertificateValidationCallback(ValidateServerCertificate);
|
||||
this.sslStream = new SslStream(this.safeNetworkStream, true, this.CertificateValidationCallback, null);
|
||||
this.sslStream.AuthenticateAsClient(this.Address);
|
||||
}
|
||||
@@ -247,15 +266,15 @@ namespace MTSC.Client
|
||||
/// <returns>True if the connection was successful.</returns>
|
||||
public Task<bool> ConnectAsync()
|
||||
{
|
||||
return new Task<bool>(Connect);
|
||||
return Task.Run(Connect);
|
||||
}
|
||||
/// <summary>
|
||||
/// Disconnects from the server.
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
cancelMonitorToken?.Cancel();
|
||||
tcpClient.Dispose();
|
||||
this.cancelMonitorToken?.Cancel();
|
||||
this.tcpClient.Dispose();
|
||||
}
|
||||
#endregion
|
||||
#region Private Methods
|
||||
@@ -272,7 +291,6 @@ namespace MTSC.Client
|
||||
if (sslPolicyErrors == SslPolicyErrors.None)
|
||||
return true;
|
||||
|
||||
// Do not allow this client to communicate with unauthenticated servers.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -287,61 +305,103 @@ namespace MTSC.Client
|
||||
{
|
||||
try
|
||||
{
|
||||
if(messageQueue.Count > 0)
|
||||
{
|
||||
byte[] messagebytes = messageQueue.Dequeue();
|
||||
Message sendMessage = CommunicationPrimitives.BuildMessage(messagebytes);
|
||||
for(int i = handlers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
IHandler handler = handlers[i];
|
||||
handler.HandleSendMessage(this, ref sendMessage);
|
||||
}
|
||||
CommunicationPrimitives.SendMessage(tcpClient, sendMessage, sslStream);
|
||||
}
|
||||
if (tcpClient.Available > 0)
|
||||
{
|
||||
/*
|
||||
* When a message has been received, process it.
|
||||
*/
|
||||
Message message = CommunicationPrimitives.GetMessage(safeNetworkStream, sslStream);
|
||||
LogDebug("Received a message of size: " + message.MessageLength);
|
||||
/*
|
||||
* Preprocess message.
|
||||
*/
|
||||
foreach (IHandler handler in handlers)
|
||||
{
|
||||
if (handler.PreHandleReceivedMessage(this, ref message))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Process the final message structure.
|
||||
*/
|
||||
foreach (IHandler handler in handlers)
|
||||
{
|
||||
if (handler.HandleReceivedMessage(this, message))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (IHandler handler in handlers)
|
||||
{
|
||||
handler.Tick(this);
|
||||
}
|
||||
this.SendQueuedMessages();
|
||||
this.ReceiveMessages();
|
||||
this.TickHandlers();
|
||||
}
|
||||
catch (IOException fatalException)
|
||||
{
|
||||
this.HandleException(fatalException);
|
||||
break;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LogDebug("Exception: " + e.Message);
|
||||
LogDebug("Stacktrace: " + e.StackTrace);
|
||||
foreach (IExceptionHandler exceptionHandler in exceptionHandlers)
|
||||
{
|
||||
exceptionHandler.HandleException(e);
|
||||
}
|
||||
this.HandleException(e);
|
||||
}
|
||||
Thread.Sleep(33);
|
||||
}
|
||||
|
||||
if (this.ReconnectPolicy == ReconnectPolicy.Forever)
|
||||
{
|
||||
do
|
||||
{
|
||||
this.Log("Connection failed. Attempting to reconnect...");
|
||||
} while (this.Connect() is false);
|
||||
}
|
||||
else if (this.ReconnectPolicy == ReconnectPolicy.Once)
|
||||
{
|
||||
this.Log("Connection failed. Attempting to reconnect...");
|
||||
this.Connect();
|
||||
}
|
||||
}
|
||||
|
||||
private void SendQueuedMessages()
|
||||
{
|
||||
if (this.messageQueue.Count > 0)
|
||||
{
|
||||
byte[] messagebytes = this.messageQueue.Dequeue();
|
||||
Message sendMessage = CommunicationPrimitives.BuildMessage(messagebytes);
|
||||
for (int i = handlers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
IHandler handler = handlers[i];
|
||||
handler.HandleSendMessage(this, ref sendMessage);
|
||||
}
|
||||
CommunicationPrimitives.SendMessage(this.tcpClient, sendMessage, this.sslStream);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReceiveMessages()
|
||||
{
|
||||
if (this.tcpClient.Available > 0)
|
||||
{
|
||||
/*
|
||||
* When a message has been received, process it.
|
||||
*/
|
||||
var message = CommunicationPrimitives.GetMessage(this.safeNetworkStream, this.sslStream);
|
||||
LogDebug("Received a message of size: " + message.MessageLength);
|
||||
this.PrehandleReceivedMessage(message);
|
||||
this.HandleReceivedMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void PrehandleReceivedMessage(Message message)
|
||||
{
|
||||
foreach (IHandler handler in this.handlers)
|
||||
{
|
||||
if (handler.PreHandleReceivedMessage(this, ref message))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleReceivedMessage(Message message)
|
||||
{
|
||||
foreach (IHandler handler in this.handlers)
|
||||
{
|
||||
if (handler.HandleReceivedMessage(this, message))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TickHandlers()
|
||||
{
|
||||
foreach (IHandler handler in this.handlers)
|
||||
{
|
||||
handler.Tick(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleException(Exception exception)
|
||||
{
|
||||
LogDebug("Exception: " + exception.Message);
|
||||
LogDebug("Stacktrace: " + exception.StackTrace);
|
||||
foreach (IExceptionHandler exceptionHandler in this.exceptionHandlers)
|
||||
{
|
||||
exceptionHandler.HandleException(exception);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace MTSC.ClientSide
|
||||
{
|
||||
/// <summary>
|
||||
/// Policy for connection issues.
|
||||
/// </summary>
|
||||
public enum ReconnectPolicy
|
||||
{
|
||||
/// <summary>
|
||||
/// Never attempt to reconnect automatically.
|
||||
/// </summary>
|
||||
Never,
|
||||
/// <summary>
|
||||
/// Attempt to reconnect automatically once.
|
||||
/// </summary>
|
||||
Once,
|
||||
/// <summary>
|
||||
/// Attempt to forever reconnect automatically unless stopped.
|
||||
/// </summary>
|
||||
Forever
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -5,12 +5,12 @@
|
||||
<TargetFrameworks>netcoreapp2.1;net48;netstandard2.0;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<Version>2.8</Version>
|
||||
<Version>2.9</Version>
|
||||
<Authors>Alexandru-Victor Macocian</Authors>
|
||||
<Product>MTSC</Product>
|
||||
<Description>Modular TCP Server and Client</Description>
|
||||
<AssemblyVersion>0.2.8</AssemblyVersion>
|
||||
<FileVersion>0.2.8</FileVersion>
|
||||
<AssemblyVersion>0.2.9</AssemblyVersion>
|
||||
<FileVersion>0.2.9</FileVersion>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<PackageProjectUrl>https://github.com/AlexMacocian/MTSC</PackageProjectUrl>
|
||||
|
||||
Reference in New Issue
Block a user