diff --git a/MTSC-TestClient/Program.cs b/MTSC-TestClient/Program.cs index 133745c..32eccf1 100644 --- a/MTSC-TestClient/Program.cs +++ b/MTSC-TestClient/Program.cs @@ -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()) diff --git a/MTSC-TestServer/Program.cs b/MTSC-TestServer/Program.cs index 7e29b21..61853ee 100644 --- a/MTSC-TestServer/Program.cs +++ b/MTSC-TestServer/Program.cs @@ -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(); } diff --git a/MTSC/ClientSide/Client.cs b/MTSC/ClientSide/Client.cs index e568cb8..70cbc8d 100644 --- a/MTSC/ClientSide/Client.cs +++ b/MTSC/ClientSide/Client.cs @@ -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 handlers = new List(); - List loggers = new List(); - List exceptionHandlers = new List(); - Queue messageQueue = new Queue(); - SslStream sslStream = null; - TimeoutSuppressedStream safeNetworkStream = null; - static Hashtable certificateErrors = new Hashtable(); + private TcpClient tcpClient; + private CancellationTokenSource cancelMonitorToken; + private readonly List handlers = new List(); + private readonly List loggers = new List(); + private readonly List exceptionHandlers = new List(); + private readonly Queue messageQueue = new Queue(); + 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 AddressResolution { get; set; } = (addresses) => addresses[0]; public Func SchemeSslFilter { get; set; } = RequiresSsl; + /// + /// Callback function used to determine if the remote certificate is valid. + /// + 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 /// + /// Sets the . + /// + /// + /// This client. + public Client WithReconnectPolicy(ReconnectPolicy reconnectPolicy) + { + this.ReconnectPolicy = reconnectPolicy; + return this; + } + /// + /// Sets the certificate validation callback for ssl connection. + /// + /// + /// This client object. + public Client WithRemoteCertificateValidationCallback(RemoteCertificateValidationCallback remoteCertificateValidationCallback) + { + this.CertificateValidationCallback = remoteCertificateValidationCallback; + return this; + } + /// /// Sets the property. /// /// Function that should return true if the provided scheme should use Ssl. @@ -160,10 +186,6 @@ namespace MTSC.Client return this; } /// - /// Callback function used to determine if the remote certificate is valid. - /// - public RemoteCertificateValidationCallback CertificateValidationCallback { get; set; } - /// /// Attemps to connect to the specified server. /// /// True if connection was successful. @@ -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 /// True if the connection was successful. public Task ConnectAsync() { - return new Task(Connect); + return Task.Run(Connect); } /// /// Disconnects from the server. /// 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 } diff --git a/MTSC/ClientSide/ReconnectPolicy.cs b/MTSC/ClientSide/ReconnectPolicy.cs new file mode 100644 index 0000000..d70d363 --- /dev/null +++ b/MTSC/ClientSide/ReconnectPolicy.cs @@ -0,0 +1,21 @@ +namespace MTSC.ClientSide +{ + /// + /// Policy for connection issues. + /// + public enum ReconnectPolicy + { + /// + /// Never attempt to reconnect automatically. + /// + Never, + /// + /// Attempt to reconnect automatically once. + /// + Once, + /// + /// Attempt to forever reconnect automatically unless stopped. + /// + Forever + } +} diff --git a/MTSC/MTSC.csproj b/MTSC/MTSC.csproj index b21a3ff..59551f3 100644 --- a/MTSC/MTSC.csproj +++ b/MTSC/MTSC.csproj @@ -5,12 +5,12 @@ netcoreapp2.1;net48;netstandard2.0;netcoreapp3.1;net5.0 - 2.8 + 2.9 Alexandru-Victor Macocian MTSC Modular TCP Server and Client - 0.2.8 - 0.2.8 + 0.2.9 + 0.2.9 true AnyCPU;x64 https://github.com/AlexMacocian/MTSC