5 using System.Collections.Concurrent;
6 using System.Collections.Generic;
8 using System.Net.Security;
9 using System.Net.Sockets;
10 using System.Security.Cryptography.X509Certificates;
12 using System.Threading;
13 using System.Threading.Tasks;
24 X509Certificate2 certificate;
27 List<ClientData> toRemove =
new List<ClientData>();
28 List<ClientData> clients =
new List<ClientData>();
29 List<IHandler> handlers =
new List<IHandler>();
30 List<ILogger> loggers =
new List<ILogger>();
31 List<IExceptionHandler> exceptionHandlers =
new List<IExceptionHandler>();
32 ConcurrentQueue<Tuple<ClientData, byte[]>> messageQueue =
new ConcurrentQueue<Tuple<ClientData, byte[]>>();
36 public int Port {
get => port;
set => port = value; }
47 public List<ClientData>
Clients {
get => clients;
set => clients = value; }
58 public int TickRate {
get => 1000 / loopMillis;
set => loopMillis = 1000 / Math.Min(value, 1000); }
85 public Server(X509Certificate2 certificate,
int port)
87 this.certificate = certificate;
91 #region Public Methods 109 handlers.Add(handler);
129 exceptionHandlers.Add(handler);
139 messageQueue.Enqueue(
new Tuple<
ClientData,
byte[]>(target, message));
145 public void Log(
string log)
147 foreach (
ILogger logger
in loggers)
158 foreach (
ILogger logger
in loggers)
169 listener =
new TcpListener(IPAddress.Any, port);
172 Log(
"Server started on: " + listener.LocalEndpoint.ToString());
173 DateTime lastLoad = DateTime.Now;
174 DateTime startLoopTime;
177 startLoopTime = DateTime.Now;
186 if (!client.TcpClient.Connected || client.ToBeRemoved)
188 toRemove.Add(client);
193 foreach (
IHandler handler
in handlers)
197 LogDebug(
"Client removed: " + client.TcpClient.Client.RemoteEndPoint.ToString());
198 client.SslStream?.Dispose();
199 client.TcpClient?.Dispose();
200 clients.Remove(client);
206 LogDebug(
"Exception: " + e.Message);
207 LogDebug(
"Stacktrace: " + e.StackTrace);
222 if (listener.Pending())
224 lastLoad = DateTime.Now;
225 TcpClient tcpClient = listener.AcceptTcpClient();
227 if (certificate !=
null)
229 SslStream sslStream =
new SslStream(tcpClient.GetStream(),
true,
new RemoteCertificateValidationCallback((o, c, ch, po) => {
231 }),
null, EncryptionPolicy.RequireEncryption);
232 clientStruct.SslStream = sslStream;
233 sslStream.AuthenticateAsServer(certificate);
235 foreach (
IHandler handler
in handlers)
242 clients.Add(clientStruct);
243 Log(
"Accepted new connection: " + tcpClient.Client.RemoteEndPoint.ToString());
248 LogDebug(
"Exception: " + e.Message);
249 LogDebug(
"Stacktrace: " + e.StackTrace);
261 Parallel.ForEach(clients, (client) =>
269 if (!client.TcpClient.Connected)
271 client.ToBeRemoved = true;
273 else if (client.TcpClient.Available > 0)
275 lastLoad = DateTime.Now;
276 Message message = CommunicationPrimitives.GetMessage(client.TcpClient, client.SslStream);
277 LogDebug(
"Received message from " + client.TcpClient.Client.RemoteEndPoint.ToString() +
278 "\nMessage length: " + message.MessageLength);
279 foreach (IHandler handler in handlers)
281 if (handler.PreHandleReceivedMessage(this, client, ref message))
286 foreach (
IHandler handler in handlers)
288 if (handler.HandleReceivedMessage(
this, client, message))
297 LogDebug(
"Exception: " + e.Message);
298 LogDebug(
"Stacktrace: " + e.StackTrace);
311 Parallel.ForEach(handlers, (handler) =>
319 LogDebug(
"Exception: " + e.Message);
320 LogDebug(
"Stacktrace: " + e.StackTrace);
333 if(messageQueue.Count > 0)
335 lastLoad = DateTime.Now;
338 while (messageQueue.Count > 0)
340 Tuple<ClientData, byte[]> queuedOrder =
null;
341 if (messageQueue.TryDequeue(out queuedOrder))
343 Message sendMessage = CommunicationPrimitives.BuildMessage(queuedOrder.Item2);
344 for (
int i = handlers.Count - 1; i >= 0; i--)
350 CommunicationPrimitives.SendMessage(queuedOrder.Item1.TcpClient, sendMessage, queuedOrder.Item1.SslStream);
356 LogDebug(
"Exception: " + e.Message);
357 LogDebug(
"Stacktrace: " + e.StackTrace);
371 if(((DateTime.Now - lastLoad).TotalMilliseconds > 1000) &&
ScaleUsage ||
374 Thread.Sleep((
int)Math.Max(loopMillis - (DateTime.Now - startLoopTime).TotalMilliseconds, 0));
383 return new Task(Run);
396 #region Private Methods 404 public TcpClient TcpClient;
405 public DateTime LastMessageTime;
406 public bool ToBeRemoved;
407 public SslStream SslStream;
411 this.TcpClient = client;
412 this.LastMessageTime = DateTime.Now;
Interface for communication handlers.
Server(int port)
Creates an instance of server.
int TickRate
How many times does the server proc per second. Cannot be set higher than 1000. This tickrate will be...
bool Log(string message)
Logs the received message.
Task RunAsync()
Runs the server async.
bool HandleException(Exception e)
Handles exceptions.
Structure containing client information.
bool Running
Returns the state of the server.
bool ForceTickrate
If set to true, the server will respect the provided TickRate, regardless of current server load.
Server SetPort(int port)
Sets the port to the specified value.
Server()
Creates an instance of server with default values.
void QueueMessage(ClientData target, byte[] message)
Queues a message to be sent.
void ClientRemoved(Server server, ClientData client)
Handles the removal of a client from the server.
bool ScaleUsage
If set to true, the server will use an algorithm to lower or increase the CPU usage based on demands.
void Run()
Blocking method. Runs the server on the current thread.
Basic server class to handle TCP connections.
Server AddHandler(IHandler handler)
Adds a handler to the server.
List< ClientData > Clients
List of clients currently connected to the server.
Server AddLogger(ILogger logger)
Adds a logger to the server.
bool HandleClient(Server server, ClientData client)
Handles a new client.
void Stop()
Stop the server.
Server AddExceptionHandler(IExceptionHandler handler)
Adds an exception handler to the server.
void LogDebug(string debugMessage)
Adds a debug message to be logged by the associated loggers.
Server(X509Certificate2 certificate, int port)
Creates an instance of server.
bool HandleSendMessage(Server server, ClientData client, ref Message message)
Handles a message before sending.
bool LogDebug(string message)
Logs the received debug message.
void Log(string log)
Adds a message to be logged by the associated loggers.
Handler to be used for handling exception.