MTSC  1.0.5
Build TCP servers out of modules with handlers for communication, exception and logging.
Client.cs
1 using MTSC.Client.Handlers;
2 using MTSC.Exceptions;
3 using MTSC.Logging;
4 using System;
5 using System.Collections;
6 using System.Collections.Generic;
7 using System.Net.Security;
8 using System.Net.Sockets;
9 using System.Security.Cryptography.X509Certificates;
10 using System.Text;
11 using System.Threading;
12 using System.Threading.Tasks;
13 
14 namespace MTSC.Client
15 {
19  public class Client
20  {
21  #region Fields
22  string address;
23  int port;
24  TcpClient tcpClient;
25  CancellationTokenSource cancelMonitorToken;
26  List<IHandler> handlers = new List<IHandler>();
27  List<ILogger> loggers = new List<ILogger>();
28  List<IExceptionHandler> exceptionHandlers = new List<IExceptionHandler>();
29  Queue<byte[]> messageQueue = new Queue<byte[]>();
30  SslStream sslStream = null;
31  static Hashtable certificateErrors = new Hashtable();
32  bool useSsl = false;
33  #endregion
34  #region Properties
35  public bool Connected
36  {
37  get
38  {
39  if (tcpClient != null)
40  return tcpClient.Connected;
41  else
42  return false;
43  }
44  }
45  public string Address { get => address; }
46  public int Port { get => port; }
47  #endregion
48  #region Constructors
49  public Client(bool useSsl = false)
50  {
51  this.useSsl = useSsl;
52  }
53  #endregion
54  #region Public Methods
55  public void QueueMessage(byte[] message)
60  {
61  messageQueue.Enqueue(message);
62  }
67  public void Log(string log)
68  {
69  foreach (ILogger logger in loggers)
70  {
71  logger.Log(log);
72  }
73  }
78  public void LogDebug(string debugMessage)
79  {
80  foreach (ILogger logger in loggers)
81  {
82  logger.LogDebug(debugMessage);
83  }
84  }
90  public Client SetServerAddress(string address)
91  {
92  this.address = address;
93  return this;
94  }
100  public Client SetPort(int port)
101  {
102  this.port = port;
103  return this;
104  }
110  public Client AddHandler(IHandler handler)
111  {
112  handlers.Add(handler);
113  return this;
114  }
121  {
122  exceptionHandlers.Add(handler);
123  return this;
124  }
130  public Client AddLogger(ILogger logger)
131  {
132  loggers.Add(logger);
133  return this;
134  }
138  public RemoteCertificateValidationCallback CertificateValidationCallback { get; set; }
143  public bool Connect()
144  {
145  try
146  {
147  if (tcpClient != null)
148  {
149  cancelMonitorToken?.Cancel();
150  tcpClient.Dispose();
151  }
152  tcpClient = new TcpClient();
153  tcpClient.Connect(address, port);
154  if (useSsl)
155  {
156  if(this.CertificateValidationCallback == null)
157  {
158  this.CertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
159  }
160  sslStream = new SslStream(tcpClient.GetStream(), true, this.CertificateValidationCallback, null);
161  sslStream.AuthenticateAsClient(address);
162  }
163  foreach(ILogger logger in loggers)
164  {
165  logger.Log("Connected to: " + tcpClient.Client.RemoteEndPoint.ToString());
166  }
167  foreach (IHandler handler in handlers)
168  {
169  if (!handler.InitializeConnection(this))
170  {
171  return false;
172  }
173  }
174  cancelMonitorToken = new CancellationTokenSource();
175  Task.Run(new Action(MonitorConnection), cancelMonitorToken.Token);
176  return true;
177  }
178  catch(Exception e)
179  {
180  LogDebug("Exception: " + e.Message);
181  LogDebug("Stacktrace: " + e.StackTrace);
182  foreach (IExceptionHandler exceptionHandler in exceptionHandlers)
183  {
184  exceptionHandler.HandleException(e);
185  }
186  return false;
187  }
188  }
193  public Task<bool> ConnectAsync()
194  {
195  return new Task<bool>(Connect);
196  }
200  public void Disconnect()
201  {
202  cancelMonitorToken?.Cancel();
203  tcpClient.Dispose();
204  }
205  #endregion
206  #region Private Methods
207  private static bool ValidateServerCertificate( object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
216  {
217  if (sslPolicyErrors == SslPolicyErrors.None)
218  return true;
219 
220  // Do not allow this client to communicate with unauthenticated servers.
221  return true;
222  }
223 
224  private void MonitorConnection()
225  {
226  while (true)
227  {
228  try
229  {
230  if(messageQueue.Count > 0)
231  {
232  byte[] messagebytes = messageQueue.Dequeue();
233  Message sendMessage = CommunicationPrimitives.BuildMessage(messagebytes);
234  for(int i = handlers.Count - 1; i >= 0; i--)
235  {
236  IHandler handler = handlers[i];
237  handler.HandleSendMessage(this, ref sendMessage);
238  }
239  CommunicationPrimitives.SendMessage(tcpClient, sendMessage, sslStream);
240  }
241  if (tcpClient.Available > 0)
242  {
243  /*
244  * When a message has been received, process it.
245  */
246  Message message = CommunicationPrimitives.GetMessage(tcpClient, sslStream);
247  LogDebug("Received a message of size: " + message.MessageLength);
248  /*
249  * Preprocess message.
250  */
251  foreach (IHandler handler in handlers)
252  {
253  if (handler.PreHandleReceivedMessage(this, ref message))
254  {
255  break;
256  }
257  }
258  /*
259  * Process the final message structure.
260  */
261  foreach (IHandler handler in handlers)
262  {
263  if (handler.HandleReceivedMessage(this, message))
264  {
265  break;
266  }
267  }
268  }
269  foreach (IHandler handler in handlers)
270  {
271  handler.Tick(this);
272  }
273  }
274  catch(Exception e)
275  {
276  LogDebug("Exception: " + e.Message);
277  LogDebug("Stacktrace: " + e.StackTrace);
278  foreach (IExceptionHandler exceptionHandler in exceptionHandlers)
279  {
280  exceptionHandler.HandleException(e);
281  }
282  }
283  Thread.Sleep(33);
284  }
285  }
286  #endregion
287  }
288 }
void Log(string log)
Logs the message onto the associated loggers.
Definition: Client.cs:67
Task< bool > ConnectAsync()
Attempts to connect to the specified server.
Definition: Client.cs:193
Client AddExceptionHandler(IExceptionHandler handler)
Adds an exception handler to the client.
Definition: Client.cs:120
bool Log(string message)
Logs the received message.
Client SetServerAddress(string address)
Sets the server address.
Definition: Client.cs:90
Client AddHandler(IHandler handler)
Adds a handler onto the client.
Definition: Client.cs:110
bool HandleException(Exception e)
Handles exceptions.
Client SetPort(int port)
Sets the server port.
Definition: Client.cs:100
bool HandleSendMessage(Client client, ref Message message)
Called when a message is being sent to the server.
bool PreHandleReceivedMessage(Client client, ref Message message)
Called before the message is handled. Use this method to modify the message if necesarry.
RemoteCertificateValidationCallback CertificateValidationCallback
Callback function used to determine if the remote certificate is valid.
Definition: Client.cs:138
bool Connect()
Attemps to connect to the specified server.
Definition: Client.cs:143
bool HandleReceivedMessage(Client client, Message message)
Called when a message has been received.
Interface for loggers.
Definition: ILogger.cs:10
void Tick(Client client)
Called every cycle. This method should perform regular actions on the connection.
bool InitializeConnection(Client client)
Called when the connection is being initialized.
void Disconnect()
Disconnects from the server.
Definition: Client.cs:200
Client AddLogger(ILogger logger)
Adds a logger onto the client.
Definition: Client.cs:130
void QueueMessage(byte[] message)
Add a message to the message queue.
Definition: Client.cs:59
Handler interface for client communication.
Definition: IHandler.cs:11
Definition: Client.cs:14
void LogDebug(string debugMessage)
Logs the debug message onto the associated loggers.
Definition: Client.cs:78
bool LogDebug(string message)
Logs the received debug message.
Base class for TCP Client.
Definition: Client.cs:19
Handler to be used for handling exception.