From 7cf668e3c1cea1ecfec05dd1157cf4c5910c8087 Mon Sep 17 00:00:00 2001 From: Alex Macocian Date: Fri, 19 Jul 2019 09:48:42 +0300 Subject: [PATCH] Implemented a unified message sending method. Implemented broadcasting handlers to test encrypted communication. --- MTSC-TestClient/MTSC-TestClient.csproj | 1 - MTSC-TestClient/Program.cs | 20 ++- MTSC-TestServer/MTSC-TestServer.csproj | 4 +- MTSC-TestServer/Program.cs | 14 +- MTSC.sln | 10 ++ MTSC/Client/Client.cs | 148 +++++++++++++++++----- MTSC/Client/Handlers/BroadcastHandler.cs | 61 +++++++++ MTSC/Client/Handlers/EncryptionHandler.cs | 36 ++++-- MTSC/Client/Handlers/IHandler.cs | 3 +- MTSC/Exceptions/ExceptionConsoleLogger.cs | 18 +++ MTSC/Exceptions/ExceptionThrower.cs | 17 +++ MTSC/Server/Handlers/BroadcastHandler.cs | 55 ++++++++ MTSC/Server/Handlers/EncryptionHandler.cs | 30 ++++- MTSC/Server/Handlers/IHandler.cs | 11 +- MTSC/Server/Server.cs | 54 +++++++- 15 files changed, 414 insertions(+), 68 deletions(-) create mode 100644 MTSC/Client/Handlers/BroadcastHandler.cs create mode 100644 MTSC/Exceptions/ExceptionConsoleLogger.cs create mode 100644 MTSC/Exceptions/ExceptionThrower.cs create mode 100644 MTSC/Server/Handlers/BroadcastHandler.cs diff --git a/MTSC-TestClient/MTSC-TestClient.csproj b/MTSC-TestClient/MTSC-TestClient.csproj index b74689a..8665336 100644 --- a/MTSC-TestClient/MTSC-TestClient.csproj +++ b/MTSC-TestClient/MTSC-TestClient.csproj @@ -4,7 +4,6 @@ Exe netcoreapp2.1 MTSC_TestClient - MTSC_TestClient.Program diff --git a/MTSC-TestClient/Program.cs b/MTSC-TestClient/Program.cs index c7748e4..cdca8bb 100644 --- a/MTSC-TestClient/Program.cs +++ b/MTSC-TestClient/Program.cs @@ -1,4 +1,7 @@ -using System; +using MTSC.Client; +using MTSC.Client.Handlers; +using MTSC.Logging; +using System; namespace MTSC_TestClient { @@ -6,7 +9,20 @@ namespace MTSC_TestClient { static void Main(string[] args) { - Console.WriteLine("Hello World!"); + Client client = new Client(); + BroadcastHandler broadcastHandler = new BroadcastHandler(client); + client + .SetServerAddress("127.0.0.1") + .SetPort(555) + .AddHandler(new EncryptionHandler(client)) + .AddHandler(broadcastHandler) + .AddLogger(new ConsoleLogger()) + .Connect(); + while (true) + { + string line = Console.ReadLine(); + broadcastHandler.Broadcast(line); + } } } } diff --git a/MTSC-TestServer/MTSC-TestServer.csproj b/MTSC-TestServer/MTSC-TestServer.csproj index 9be204c..05543d7 100644 --- a/MTSC-TestServer/MTSC-TestServer.csproj +++ b/MTSC-TestServer/MTSC-TestServer.csproj @@ -1,4 +1,4 @@ - + Exe @@ -13,7 +13,7 @@ - true + false AnyCPU diff --git a/MTSC-TestServer/Program.cs b/MTSC-TestServer/Program.cs index 486c2ac..e52c681 100644 --- a/MTSC-TestServer/Program.cs +++ b/MTSC-TestServer/Program.cs @@ -1,4 +1,6 @@ -using MTSC.Server; +using MTSC.Exceptions; +using MTSC.Logging; +using MTSC.Server; using MTSC.Server.Handlers; using System; using System.Security.Cryptography; @@ -11,8 +13,14 @@ namespace MTSC_TestServer { Server server = new Server(555); RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024); - EncryptionHandler encryptionHandler = new EncryptionHandler(rsa); - server.AddHandler(encryptionHandler).Run(); + EncryptionHandler encryptionHandler = new EncryptionHandler(rsa, server); + BroadcastHandler broadcastHandler = new BroadcastHandler(server); + server + .AddHandler(encryptionHandler) + .AddLogger(new ConsoleLogger()) + .AddExceptionHandler(new ExceptionConsoleLogger()) + .AddHandler(broadcastHandler) + .Run(); } } } diff --git a/MTSC.sln b/MTSC.sln index 74711f2..88b361f 100644 --- a/MTSC.sln +++ b/MTSC.sln @@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MTSC", "MTSC\MTSC.csproj", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MTSC-TestServer", "MTSC-TestServer\MTSC-TestServer.csproj", "{08A53F8F-BA6B-4C1C-B24B-CFD2626DE6C3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MTSC-TestClient", "MTSC-TestClient\MTSC-TestClient.csproj", "{B6792D3B-E141-48AC-B1E1-BCC0096ECF51}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -31,6 +33,14 @@ Global {08A53F8F-BA6B-4C1C-B24B-CFD2626DE6C3}.Release|Any CPU.Build.0 = Release|Any CPU {08A53F8F-BA6B-4C1C-B24B-CFD2626DE6C3}.Release|x64.ActiveCfg = Release|x64 {08A53F8F-BA6B-4C1C-B24B-CFD2626DE6C3}.Release|x64.Build.0 = Release|x64 + {B6792D3B-E141-48AC-B1E1-BCC0096ECF51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6792D3B-E141-48AC-B1E1-BCC0096ECF51}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6792D3B-E141-48AC-B1E1-BCC0096ECF51}.Debug|x64.ActiveCfg = Debug|Any CPU + {B6792D3B-E141-48AC-B1E1-BCC0096ECF51}.Debug|x64.Build.0 = Debug|Any CPU + {B6792D3B-E141-48AC-B1E1-BCC0096ECF51}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6792D3B-E141-48AC-B1E1-BCC0096ECF51}.Release|Any CPU.Build.0 = Release|Any CPU + {B6792D3B-E141-48AC-B1E1-BCC0096ECF51}.Release|x64.ActiveCfg = Release|Any CPU + {B6792D3B-E141-48AC-B1E1-BCC0096ECF51}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/MTSC/Client/Client.cs b/MTSC/Client/Client.cs index edeba1d..b90fdc0 100644 --- a/MTSC/Client/Client.cs +++ b/MTSC/Client/Client.cs @@ -1,4 +1,6 @@ using MTSC.Client.Handlers; +using MTSC.Exceptions; +using MTSC.Logging; using System; using System.Collections.Generic; using System.Net.Sockets; @@ -19,6 +21,9 @@ namespace MTSC.Client TcpClient tcpClient; CancellationTokenSource cancelMonitorToken; List handlers = new List(); + List loggers = new List(); + List exceptionHandlers = new List(); + Queue messageQueue = new Queue(); #endregion #region Properties public bool Connected @@ -42,6 +47,25 @@ namespace MTSC.Client #endregion #region Public Methods /// + /// Add a message to the message queue. + /// + /// Message to be sent. + public void QueueMessage(byte[] message) + { + messageQueue.Enqueue(message); + } + /// + /// Logs the message onto the associated loggers. + /// + /// Message to be logged. + public void Log(string log) + { + foreach (ILogger logger in loggers) + { + logger.Log(log + "\n"); + } + } + /// /// Sets the server address. /// /// Address of the server. @@ -72,28 +96,63 @@ namespace MTSC.Client return this; } /// + /// Adds an exception handler to the client. + /// + /// Exception handler to be added. + /// This client object. + public Client AddExceptionHandler(IExceptionHandler handler) + { + exceptionHandlers.Add(handler); + return this; + } + /// + /// Adds a logger onto the client. + /// + /// Logger to be added. + /// This client object. + public Client AddLogger(ILogger logger) + { + loggers.Add(logger); + return this; + } + /// /// Attemps to connect to the specified server. /// /// True if connection was successful. public bool Connect() { - if(tcpClient != null) + try { - cancelMonitorToken?.Cancel(); - tcpClient.Dispose(); - } - tcpClient = new TcpClient(); - tcpClient.Connect(address, port); - foreach(IHandler handler in handlers) - { - if (!handler.InitializeConnection(tcpClient)) + if (tcpClient != null) { - return false; + cancelMonitorToken?.Cancel(); + tcpClient.Dispose(); } + tcpClient = new TcpClient(); + tcpClient.Connect(address, port); + foreach(ILogger logger in loggers) + { + logger.Log("Connected to: " + tcpClient.Client.RemoteEndPoint.ToString()); + } + foreach (IHandler handler in handlers) + { + if (!handler.InitializeConnection(tcpClient)) + { + return false; + } + } + cancelMonitorToken = new CancellationTokenSource(); + Task.Run(MonitorConnection, cancelMonitorToken.Token); + return true; + } + catch(Exception e) + { + foreach(IExceptionHandler exceptionHandler in exceptionHandlers) + { + exceptionHandler.HandleException(e); + } + return false; } - cancelMonitorToken = new CancellationTokenSource(); - Task.Run(MonitorConnection, cancelMonitorToken.Token); - return true; } /// /// Attempts to connect to the specified server. @@ -117,31 +176,56 @@ namespace MTSC.Client { while (true) { - if(tcpClient.Available > 0) + try { - /* - * When a message has been received, process it. - */ - Message message = CommunicationPrimitives.GetMessage(tcpClient); - /* - * Preprocess message. - */ - foreach(IHandler handler in handlers) + if(messageQueue.Count > 0) { - if(handler.PreHandleReceivedMessage(tcpClient, ref message)) + byte[] messagebytes = messageQueue.Dequeue(); + Message sendMessage = CommunicationPrimitives.BuildMessage(messagebytes); + foreach(IHandler handler in handlers) { - break; + handler.HandleSendMessage(tcpClient, ref sendMessage); + } + CommunicationPrimitives.SendMessage(tcpClient, sendMessage); + } + if (tcpClient.Available > 0) + { + /* + * When a message has been received, process it. + */ + Message message = CommunicationPrimitives.GetMessage(tcpClient); + Log("Received a message of size: " + message.MessageLength); + /* + * Preprocess message. + */ + foreach (IHandler handler in handlers) + { + if (handler.PreHandleReceivedMessage(tcpClient, ref message)) + { + break; + } + } + /* + * Process the final message structure. + */ + foreach (IHandler handler in handlers) + { + if (handler.HandleReceivedMessage(tcpClient, message)) + { + break; + } } } - /* - * Process the final message structure. - */ - foreach(IHandler handler in handlers) + foreach (IHandler handler in handlers) { - if(handler.HandleReceivedMessage(tcpClient, message)) - { - break; - } + handler.Tick(tcpClient); + } + } + catch(Exception e) + { + foreach(IExceptionHandler exceptionHandler in exceptionHandlers) + { + exceptionHandler.HandleException(e); } } } diff --git a/MTSC/Client/Handlers/BroadcastHandler.cs b/MTSC/Client/Handlers/BroadcastHandler.cs new file mode 100644 index 0000000..a11126a --- /dev/null +++ b/MTSC/Client/Handlers/BroadcastHandler.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Net.Sockets; +using System.Text; + +namespace MTSC.Client.Handlers +{ + public class BroadcastHandler : IHandler + { + private List buffer = new List(); + private Client managedClient; + /// + /// Creates a new instance of BroadcastHandler. + /// + /// Client managed by the handler. + public BroadcastHandler(Client client) + { + this.managedClient = client; + } + + /// + /// Broadcast a message to all other clients connected to the server. + /// + /// Message to be broadcasted. + public void Broadcast(string message) + { + managedClient.QueueMessage(ASCIIEncoding.ASCII.GetBytes(message)); + } + + public void Disconnected(TcpClient client) + { + + } + + public bool HandleReceivedMessage(TcpClient client, Message message) + { + managedClient.Log("Broadcast: " + ASCIIEncoding.ASCII.GetString(message.MessageBytes)); + return false; + } + + public bool HandleSendMessage(TcpClient client, ref Message message) + { + return false; + } + + public bool InitializeConnection(TcpClient client) + { + return true; + } + + public bool PreHandleReceivedMessage(TcpClient client, ref Message message) + { + return false; + } + + public void Tick(TcpClient tcpClient) + { + + } + } +} diff --git a/MTSC/Client/Handlers/EncryptionHandler.cs b/MTSC/Client/Handlers/EncryptionHandler.cs index 007fe3b..59daf04 100644 --- a/MTSC/Client/Handlers/EncryptionHandler.cs +++ b/MTSC/Client/Handlers/EncryptionHandler.cs @@ -17,10 +17,22 @@ namespace MTSC.Client.Handlers NegotiatingSymKey, Encrypted } - + private Client managedClient; + #region Fields private ConnectionState connectionState = ConnectionState.Initial; private byte[] aesKey; - + #endregion + #region Constructors + /// + /// Creates an instance of EncryptionHandler. + /// + /// Client object that this handler manages. + public EncryptionHandler(Client client) + { + this.managedClient = client; + } + #endregion + #region Public Methods /// /// Tries to initialize an encrypted connection. /// @@ -87,7 +99,7 @@ namespace MTSC.Client.Handlers if (ascii.Contains(CommunicationPrimitives.SendPublicKey)) { byte[] publicKeyBytes = new byte[message.MessageLength - CommunicationPrimitives.SendPublicKey.Length - 1]; - Array.Copy(message.MessageBytes, message.MessageLength + 1, publicKeyBytes, 0, publicKeyBytes.Length); + Array.Copy(message.MessageBytes, CommunicationPrimitives.SendPublicKey.Length + 1, publicKeyBytes, 0, publicKeyBytes.Length); string publicKey = ASCIIEncoding.ASCII.GetString(publicKeyBytes); RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024); RSAParameters parameters = new RSAParameters(); @@ -118,15 +130,14 @@ namespace MTSC.Client.Handlers byte[] messageBytes = new byte[encryptedSymKey.Length + messageHeader.Length]; Array.Copy(messageHeader, 0, messageBytes, 0, messageHeader.Length); Array.Copy(encryptedSymKey, 0, messageBytes, messageHeader.Length, encryptedSymKey.Length); - Message sendMessage = CommunicationPrimitives.BuildMessage(messageBytes); - CommunicationPrimitives.SendMessage(client, sendMessage); + managedClient.QueueMessage(messageBytes); connectionState = ConnectionState.NegotiatingSymKey; return true; } } else if(connectionState == ConnectionState.NegotiatingSymKey) { - string ascii = ASCIIEncoding.ASCII.GetString(message.MessageBytes); + string ascii = ASCIIEncoding.ASCII.GetString(DecryptBytes(message.MessageBytes)); if(ascii == CommunicationPrimitives.AcceptEncryptionKey) { connectionState = ConnectionState.Encrypted; @@ -144,18 +155,18 @@ namespace MTSC.Client.Handlers /// Called on every tick by the client object. /// Performs regular operations. /// - /// Client connection. - public void Tick(TcpClient client) + /// Client connection. + public void Tick(TcpClient tcpClient) { if(connectionState == ConnectionState.Initial) { - Message message = CommunicationPrimitives.BuildMessage(ASCIIEncoding.ASCII.GetBytes(CommunicationPrimitives.RequestPublicKey)); - CommunicationPrimitives.SendMessage(client, message); + + managedClient.QueueMessage(ASCIIEncoding.ASCII.GetBytes(CommunicationPrimitives.RequestPublicKey)); this.connectionState = ConnectionState.RequestingPublicKey; } } - - + #endregion + #region Private Methods private string GetUniqueKey(int size) { return Convert.ToBase64String(GetUniqueByteKey(size)); @@ -260,5 +271,6 @@ namespace MTSC.Client.Handlers return result; } + #endregion } } diff --git a/MTSC/Client/Handlers/IHandler.cs b/MTSC/Client/Handlers/IHandler.cs index 2172e75..91b7e7f 100644 --- a/MTSC/Client/Handlers/IHandler.cs +++ b/MTSC/Client/Handlers/IHandler.cs @@ -43,7 +43,8 @@ namespace MTSC.Client.Handlers /// Called every cycle. This method should perform regular actions on the connection. /// /// Client object. - void Tick(TcpClient client); + /// TcpClient connection. + void Tick(TcpClient tcpClient); /// /// Called when the client is disconnected. /// diff --git a/MTSC/Exceptions/ExceptionConsoleLogger.cs b/MTSC/Exceptions/ExceptionConsoleLogger.cs new file mode 100644 index 0000000..a563654 --- /dev/null +++ b/MTSC/Exceptions/ExceptionConsoleLogger.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MTSC.Exceptions +{ + /// + /// Logs the received exceptions to the console. + /// + public class ExceptionConsoleLogger : IExceptionHandler + { + public bool HandleException(Exception e) + { + Console.WriteLine(e.Message + "\nStackTrace:\n" + e.StackTrace); + return false; + } + } +} diff --git a/MTSC/Exceptions/ExceptionThrower.cs b/MTSC/Exceptions/ExceptionThrower.cs new file mode 100644 index 0000000..c036321 --- /dev/null +++ b/MTSC/Exceptions/ExceptionThrower.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MTSC.Exceptions +{ + /// + /// Throws all handled exceptions. + /// + public class ExceptionThrower : IExceptionHandler + { + public bool HandleException(Exception e) + { + throw e; + } + } +} diff --git a/MTSC/Server/Handlers/BroadcastHandler.cs b/MTSC/Server/Handlers/BroadcastHandler.cs new file mode 100644 index 0000000..732b195 --- /dev/null +++ b/MTSC/Server/Handlers/BroadcastHandler.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MTSC.Server.Handlers +{ + /// + /// Broadcast handler. + /// + public class BroadcastHandler : IHandler + { + private Server managedServer; + + public BroadcastHandler(Server server) + { + this.managedServer = server; + } + + public void ClientRemoved(ClientStruct client) + { + + } + + public bool HandleClient(ClientStruct client) + { + return false; + } + + public bool HandleReceivedMessage(ClientStruct client, Message message) + { + managedServer.Log("Broadcast: " + ASCIIEncoding.ASCII.GetString(message.MessageBytes)); + managedServer.Log("From: " + client.TcpClient.Client.RemoteEndPoint.ToString()); + foreach(ClientStruct clientStruct in managedServer.Clients) + { + managedServer.QueueMessage(clientStruct, message.MessageBytes); + } + return false; + } + + public bool HandleSendMessage(ClientStruct client, ref Message message) + { + return false; + } + + public bool PreHandleReceivedMessage(ClientStruct client, ref Message message) + { + return false; + } + + public void Tick() + { + + } + } +} diff --git a/MTSC/Server/Handlers/EncryptionHandler.cs b/MTSC/Server/Handlers/EncryptionHandler.cs index 5ae712a..ac45f39 100644 --- a/MTSC/Server/Handlers/EncryptionHandler.cs +++ b/MTSC/Server/Handlers/EncryptionHandler.cs @@ -27,6 +27,7 @@ namespace MTSC.Server.Handlers private Dictionary additionalData; private RSACryptoServiceProvider rsa; private string privateKey, publicKey; + private Server managedServer; #endregion #region Properties #endregion @@ -35,9 +36,12 @@ namespace MTSC.Server.Handlers /// Creates an instance of EncryptionHandler. /// /// Symmetrical algorithm to be used for end-to-end encryption. - public EncryptionHandler(RSACryptoServiceProvider rsa) + /// Server object managed by the handler. + public EncryptionHandler(RSACryptoServiceProvider rsa, Server server) { + managedServer = server; additionalData = new Dictionary(); + this.rsa = rsa; privateKey = HelperFunctions.ToXmlString(rsa, true); publicKey = HelperFunctions.ToXmlString(rsa, false); } @@ -67,15 +71,14 @@ namespace MTSC.Server.Handlers /// Client connection. /// Received message. /// True if no other handler should handle current message. - public bool HandleMessage(ClientStruct client, Message message) + public bool HandleReceivedMessage(ClientStruct client, Message message) { if (additionalData[client].ClientState == ClientState.Initial || additionalData[client].ClientState == ClientState.Negotiating) { string asciiMessage = ASCIIEncoding.ASCII.GetString(message.MessageBytes); if (asciiMessage == CommunicationPrimitives.RequestPublicKey) { - Message sendMessage = CommunicationPrimitives.BuildMessage(ASCIIEncoding.ASCII.GetBytes(CommunicationPrimitives.SendPublicKey + ":" + publicKey)); - CommunicationPrimitives.SendMessage(client.TcpClient, sendMessage); + managedServer.QueueMessage(client, ASCIIEncoding.ASCII.GetBytes(CommunicationPrimitives.SendPublicKey + ":" + publicKey)); additionalData[client].ClientState = ClientState.Negotiating; return true; } @@ -85,8 +88,7 @@ namespace MTSC.Server.Handlers Array.Copy(message.MessageBytes, CommunicationPrimitives.SendEncryptionKey.Length + 1, encryptedKey, 0, encryptedKey.Length); byte[] decryptedKey = rsa.Decrypt(encryptedKey, false); additionalData[client].Key = decryptedKey; - Message sendMessage = CommunicationPrimitives.BuildMessage(ASCIIEncoding.ASCII.GetBytes(CommunicationPrimitives.AcceptEncryptionKey)); - CommunicationPrimitives.SendMessage(client.TcpClient, sendMessage); + managedServer.QueueMessage(client, ASCIIEncoding.ASCII.GetBytes(CommunicationPrimitives.AcceptEncryptionKey)); additionalData[client].ClientState = ClientState.Encrypted; return true; } @@ -106,7 +108,7 @@ namespace MTSC.Server.Handlers /// Client connection. /// Message to be processed. /// True if no other handlers should perform operations on current message. - public bool PreHandleMessage(ClientStruct client, ref Message message) + public bool PreHandleReceivedMessage(ClientStruct client, ref Message message) { if(additionalData[client].ClientState == ClientState.Encrypted) { @@ -132,6 +134,20 @@ namespace MTSC.Server.Handlers public void Tick() { + } + /// + /// Encrypts the message before sending. + /// + /// Client object. + /// Message to be processed. + /// True if no other handler should process this message. + public bool HandleSendMessage(ClientStruct client, ref Message message) + { + if(additionalData[client].ClientState == ClientState.Encrypted) + { + message = CommunicationPrimitives.BuildMessage(EncryptBytes(additionalData[client].Key, message.MessageBytes)); + } + return false; } #endregion #region Private Methods diff --git a/MTSC/Server/Handlers/IHandler.cs b/MTSC/Server/Handlers/IHandler.cs index 775fbe4..c69c2c4 100644 --- a/MTSC/Server/Handlers/IHandler.cs +++ b/MTSC/Server/Handlers/IHandler.cs @@ -17,19 +17,26 @@ namespace MTSC.Server.Handlers /// True if the handler processed the client. bool HandleClient(ClientStruct client); /// + /// Handles a message before sending. + /// + /// Client object. + /// Message to be processed. + /// True if no other handler should handle this message. + bool HandleSendMessage(ClientStruct client, ref Message message); + /// /// Called before the message handling. /// Perform here any processing of the message. /// /// Client structure. /// Message to be preprocessed. /// True if the message has been preprocessed and no other handler should handle it anymore. - bool PreHandleMessage(ClientStruct client, ref Message message); + bool PreHandleReceivedMessage(ClientStruct client, ref Message message); /// /// Handles the received message. /// /// Message to be handled. /// True if the message has been handled, false if the message has not been handled. - bool HandleMessage(ClientStruct client, Message message); + bool HandleReceivedMessage(ClientStruct client, Message message); /// /// Handles the removal of a client from the server. /// diff --git a/MTSC/Server/Server.cs b/MTSC/Server/Server.cs index 189efd2..cea1864 100644 --- a/MTSC/Server/Server.cs +++ b/MTSC/Server/Server.cs @@ -24,13 +24,21 @@ namespace MTSC.Server List handlers = new List(); List loggers = new List(); List exceptionHandlers = new List(); + Queue> messageQueue = new Queue>(); #endregion #region Properties + /// + /// Server port. + /// public int Port { get => port; set => port = value; } /// /// Returns the state of the server. /// public bool Running { get => running; } + /// + /// List of clients currently connected to the server. + /// + public List Clients { get => clients; set => clients = value; } #endregion #region Constructors /// @@ -91,6 +99,26 @@ namespace MTSC.Server return this; } /// + /// Queues a message to be sent. + /// + /// Target client. + /// Message to be sent. + public void QueueMessage(ClientStruct target, byte[] message) + { + messageQueue.Enqueue(new Tuple(target, message)); + } + /// + /// Adds a message to be logged by the associated loggers. + /// + /// Message to be logged + public void Log(string log) + { + foreach (ILogger logger in loggers) + { + logger.Log(log + "\n"); + } + } + /// /// Blocking method. Runs the server on the current thread. /// public void Run() @@ -98,8 +126,24 @@ namespace MTSC.Server listener?.Stop(); listener = new TcpListener(IPAddress.Any, port); listener.Start(); + running = true; + Log("Server started on: " + listener.LocalEndpoint.ToString()); while (running) { + /* + * Check if there are messages queued to be sent. + */ + if(messageQueue.Count > 0) + { + Tuple queuedOrder = messageQueue.Dequeue(); + Message sendMessage = CommunicationPrimitives.BuildMessage(queuedOrder.Item2); + foreach(IHandler handler in handlers) + { + handler.HandleSendMessage(queuedOrder.Item1, ref sendMessage); + } + CommunicationPrimitives.SendMessage(queuedOrder.Item1.TcpClient, sendMessage); + } + /* * Check if the server has any pending connections. * If it has a new connection, process it. @@ -116,6 +160,7 @@ namespace MTSC.Server } } clients.Add(clientStruct); + Log("Accepted new connection: " + tcpClient.Client.RemoteEndPoint.ToString()); } /* * Process in parallel all clients. @@ -127,21 +172,18 @@ namespace MTSC.Server if (client.TcpClient.Available > 0) { Message message = CommunicationPrimitives.GetMessage(client.TcpClient); - foreach (ILogger logger in loggers) - { - logger.Log("Received message from " + client.TcpClient.Client.RemoteEndPoint.ToString() + + Log("Received message from " + client.TcpClient.Client.RemoteEndPoint.ToString() + " Message length: " + message.MessageLength); - } foreach (IHandler handler in handlers) { - if (handler.PreHandleMessage(client, ref message)) + if (handler.PreHandleReceivedMessage(client, ref message)) { break; } } foreach (IHandler handler in handlers) { - if (handler.HandleMessage(client, message)) + if (handler.HandleReceivedMessage(client, message)) { break; }