This commit is contained in:
EonaCat 2023-03-24 11:06:57 +01:00
parent a5e2cbaf57
commit ff98074f5b
6 changed files with 165 additions and 214 deletions

View File

@ -18,9 +18,9 @@
<PackageTags>EonaCat, Network, .NET Standard, EonaCatHelpers, Jeroen, Saey, Protocol, Quic, UDP, TCP, Web, Server</PackageTags>
<PackageReleaseNotes></PackageReleaseNotes>
<Description>EonaCat Networking library with Quic, TCP, UDP and a Webserver</Description>
<Version>1.0.2</Version>
<AssemblyVersion>1.0.0.2</AssemblyVersion>
<FileVersion>1.0.0.2</FileVersion>
<Version>1.0.3</Version>
<AssemblyVersion>1.0.0.3</AssemblyVersion>
<FileVersion>1.0.0.3</FileVersion>
<PackageIcon>icon.png</PackageIcon>
</PropertyGroup>
@ -50,6 +50,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="EonaCat.Controls" Version="1.0.2" />
<PackageReference Include="EonaCat.Json" Version="1.0.3" />
<PackageReference Include="EonaCat.LogSystem" Version="1.0.0" />
<PackageReference Include="EonaCat.Matchers" Version="1.0.0" />

View File

@ -6,7 +6,7 @@ namespace EonaCat.Sockets
{
public delegate void SendBytesTo(byte[] data, EndPoint point);
public class ClientConnection : IDisposable
public class Client : IDisposable
{
/// <summary>
/// Unique client id.
@ -43,10 +43,7 @@ namespace EonaCat.Sockets
public event SendBytesTo SendData;
/// <summary>
/// ClientConnection class constructor.
/// </summary>
public ClientConnection()
public Client()
{
Buffer = new byte[BufferSize];
}

View File

@ -5,7 +5,7 @@ using System.Net.Sockets;
namespace EonaCat.Sockets
{
public class SocketClient : ClientConnection, IDisposable
public class SocketClient : Client, IDisposable
{
/// <summary>
/// Client type

View File

@ -5,7 +5,8 @@ using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using EonaCat.Helpers.Controls;
namespace EonaCat.Sockets
{
@ -29,7 +30,7 @@ namespace EonaCat.Sockets
/// <summary>
/// List of connected clients.
/// </summary>
public List<ClientConnection> ConnectedClients { get; set; } = new List<ClientConnection>();
public List<Client> ConnectedClients { get; set; } = new List<Client>();
/// <summary>
/// Interval to check for disconnected clients
@ -44,9 +45,8 @@ namespace EonaCat.Sockets
/// <summary>
/// Determines if the socket is in blocking mode
/// (default: true)
/// </summary>
public bool IsSocketInBlockingMode { get; set; } = true;
public bool IsSocketBlockingDisabled { get; set; }
/// <summary>
@ -60,9 +60,9 @@ namespace EonaCat.Sockets
/// <summary>
/// Only for UDP.
/// Required to use SocketClient class for this.
/// (default: true)
/// </summary>
public bool UDPClientManage { get; set; } = true;
public bool UseClientManager { get; set; } = true;
/// <summary>
/// Only for UDP.
@ -96,18 +96,20 @@ namespace EonaCat.Sockets
/// </summary>
public event EventHandler OnStop;
private Socket Listener { get; set; }
private System.Timers.Timer DisconnectTimer { get; set; }
private Socket Server { get; set; }
private EonaCatTimer DisconnectTimerUdp { get; set; }
private EonaCatTimer DisconnectTimerTcp { get; set; }
private Dictionary<EndPoint, double> LastDataReceivedTime { get; set; }
private double TimeNow { get => (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; }
public SocketServer(NetworkType type, string address, int port)
{
if (IPAddress.TryParse(address, out var ipAddress))
if (!IPAddress.TryParse(address, out var ipAddress))
{
new SocketServer(type, ipAddress, port);
throw new Exception("Invalid IP address specified");
}
throw new Exception("Invalid IP address specified");
SetupSocketServer(type, ipAddress, port);
}
/// <summary>
@ -117,12 +119,63 @@ namespace EonaCat.Sockets
/// <param name="address">Server listening ip</param>
/// <param name="port">Server listening port</param>
public SocketServer(NetworkType type, IPAddress address, int port)
{
SetupSocketServer(type, address, port);
}
private void SetupSocketServer(NetworkType type, IPAddress address, int port)
{
Guid = System.Guid.NewGuid().ToString();
ServerType = type;
IPAddress = address;
Port = port;
DisconnectTimer = new System.Timers.Timer(DisconnectionCheckInterval);
}
private readonly List<EndPoint> _removedEndpoints = new List<EndPoint>();
private void UdpCheckCallback()
{
_removedEndpoints.Clear();
var now = TimeNow;
foreach (var kp in LastDataReceivedTime.Where(kp => now - kp.Value > _UDPDataInterval / 1000))
{
lock (ConnectedClients)
{
var client = ConnectedClients.FirstOrDefault(x => x.Guid == kp.Key.ToString());
if (client == null)
{
continue;
}
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
ConnectedClients.Remove(client);
_removedEndpoints.Add(kp.Key);
}
}
lock (LastDataReceivedTime)
{
foreach (var r in _removedEndpoints)
{
LastDataReceivedTime.Remove(r);
}
}
}
private void TcpCheckCallback()
{
lock (ConnectedClients)
{
ConnectedClients.RemoveAll(x =>
{
if (x.IsConnected)
{
return false;
}
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = x });
return true;
});
}
}
public void Dispose()
@ -133,96 +186,52 @@ namespace EonaCat.Sockets
/// <summary>
/// Start server
/// </summary>
public void Start()
public async Task StartAsync()
{
try
{
switch (ServerType)
{
case NetworkType.Tcp:
Listener = new Socket(IPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
Server = new Socket(IPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
ConfigureSocketOptions();
// Check if we need to override the socket with a custom socket
if (CustomTcpSocket != null)
{
Listener = CustomTcpSocket;
Server = CustomTcpSocket;
}
DisconnectTimer.Elapsed += (s, e) =>
{
lock (ConnectedClients)
{
ConnectedClients.RemoveAll(x =>
{
if (x.IsConnected)
{
return false;
}
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = x });
return true;
});
}
};
DisconnectTimerTcp = new EonaCatTimer(TimeSpan.FromMilliseconds(DisconnectionCheckInterval), TcpCheckCallback);
DisconnectTimerTcp.Start();
break;
case NetworkType.Udp:
{
if (UDPClientManage)
if (UseClientManager)
{
LastDataReceivedTime = new Dictionary<EndPoint, double>();
DisconnectTimer.Elapsed += (s, e) =>
{
var now = TimeNow;
var times = LastDataReceivedTime;
var removed = new List<EndPoint>();
foreach (var kp in times)
{
if (now - kp.Value > _UDPDataInterval / 1000)
{
lock (ConnectedClients)
{
var client = ConnectedClients.Where(x => x.Guid == kp.Key.ToString());
if (!client.Any())
{
continue;
}
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client.First() });
ConnectedClients.Remove(client.First());
removed.Add(kp.Key);
}
}
}
lock (LastDataReceivedTime)
{
foreach (var r in removed)
{
LastDataReceivedTime.Remove(r);
}
}
};
DisconnectTimerUdp = new EonaCatTimer(TimeSpan.FromMilliseconds(DisconnectionCheckInterval), UdpCheckCallback);
DisconnectTimerUdp.Start();
}
Listener = new Socket(IPAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
Server = new Socket(IPAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
ConfigureSocketOptions();
// Check if we need to override the socket with a custom socket
if (CustomUdpSocket != null)
{
Listener = CustomUdpSocket;
Server = CustomUdpSocket;
}
break;
}
}
Listener.ReceiveBufferSize = ClientConnection.BufferSize;
Listener.SendBufferSize = ClientConnection.BufferSize;
Listener.Bind(new IPEndPoint(IPAddress, Port));
Server.ReceiveBufferSize = Client.BufferSize;
Server.SendBufferSize = Client.BufferSize;
Server.Bind(new IPEndPoint(IPAddress, Port));
if (ServerType == NetworkType.Tcp)
{
Listener.Listen(Backlog);
Server.Listen(Backlog);
}
DisconnectTimer.Start();
ListenerLoop();
await Task.Run(ListenLoop);
}
catch (SocketException se)
{
@ -261,14 +270,14 @@ namespace EonaCat.Sockets
private void ConfigureSocketOptions()
{
// Disable ICMP packet shutdown
SetConnectionReset(Listener);
SetConnectionReset(Server);
if (ReuseAddress)
{
Listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
}
Listener.Blocking = IsSocketInBlockingMode;
Server.Blocking = IsSocketBlockingDisabled;
}
/// <summary>
@ -278,7 +287,8 @@ namespace EonaCat.Sockets
{
try
{
DisconnectTimer.Stop();
DisconnectTimerUdp?.Stop();
DisconnectTimerTcp?.Stop();
foreach (var client in ConnectedClients)
{
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
@ -286,7 +296,7 @@ namespace EonaCat.Sockets
}
ConnectedClients.Clear();
OnStop?.Invoke(this, EventArgs.Empty);
Listener.Close();
Server.Close();
}
catch (SocketException socketException)
{
@ -294,35 +304,29 @@ namespace EonaCat.Sockets
}
}
private void ListenerLoop()
private void ListenLoop()
{
try
{
OnStart?.Invoke(this, CustomTcpSocket != null || CustomUdpSocket != null ? "Custom socket was used" : string.Empty);
if (ServerType == NetworkType.Tcp)
{
Listener.BeginAccept(AcceptCallback, Listener);
}
if (ServerType != NetworkType.Udp)
switch (ServerType)
{
return;
}
var loopbackAddress = Listener.AddressFamily == AddressFamily.InterNetworkV6
? IPAddress.IPv6Loopback
: IPAddress.Loopback;
case NetworkType.Tcp:
Server.BeginAccept(AcceptCallback, Server);
break;
if (UDPClientManage)
{
var buffer = new byte[ClientConnection.BufferSize];
EndPoint remoteIp = new IPEndPoint(loopbackAddress, 0);
Listener.BeginReceiveFrom(buffer, 0, ClientConnection.BufferSize, 0, ref remoteIp, AcceptCallbackUDP, new object[] { buffer, remoteIp, Listener });
}
else
{
var buffer = new byte[ClientConnection.BufferSize];
EndPoint remoteEp = new IPEndPoint(loopbackAddress, 0);
Listener.BeginReceiveFrom(buffer, 0, ClientConnection.BufferSize, 0, ref remoteEp, ReadCallbackUDP, new object[] { buffer, remoteEp, Listener });
case NetworkType.Udp:
var loopbackAddress = Server.AddressFamily == AddressFamily.InterNetworkV6
? IPAddress.IPv6Loopback
: IPAddress.Loopback;
var buffer = new byte[Client.BufferSize];
EndPoint remoteIp = new IPEndPoint(loopbackAddress, 0);
Server.BeginReceiveFrom(buffer, 0, Client.BufferSize, 0, ref remoteIp, UseClientManager ? AcceptClientsCallbackUDP : ReadClientCallbackUdp, new object[] { buffer, remoteIp, Server });
break;
default:
throw new NotSupportedException("Protocol type not supported");
}
}
catch (SocketException se)
@ -337,7 +341,7 @@ namespace EonaCat.Sockets
{
var listener = (Socket)ar.AsyncState;
var handler = listener.EndAccept(ar);
var client = new ClientConnection()
var client = new Client()
{
Guid = System.Guid.NewGuid().ToString(),
Socket = handler
@ -347,8 +351,8 @@ namespace EonaCat.Sockets
ConnectedClients.Add(client);
}
OnConnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
client.Socket.BeginReceive(client.Buffer, 0, ClientConnection.BufferSize, 0, ReadCallback, client);
listener.BeginAccept(AcceptCallback, Listener);
client.Socket.BeginReceive(client.Buffer, 0, Client.BufferSize, 0, ReadCallback, client);
listener.BeginAccept(AcceptCallback, Server);
}
catch (SocketException se)
{
@ -361,7 +365,7 @@ namespace EonaCat.Sockets
{
try
{
var client = (ClientConnection)ar.AsyncState;
var client = (Client)ar.AsyncState;
var bytesRead = client.Socket.EndReceive(ar);
if (bytesRead > 0)
{
@ -369,8 +373,7 @@ namespace EonaCat.Sockets
Array.Copy(client.Buffer, 0, data, 0, bytesRead);
OnReceived?.Invoke(this, new SocketServerDataEventArgs() { Client = client, Data = data });
}
Thread.Sleep(DisconnectionCheckInterval);
client.Socket.BeginReceive(client.Buffer, 0, ClientConnection.BufferSize, 0, ReadCallback, client);
client.Socket.BeginReceive(client.Buffer, 0, Client.BufferSize, 0, ReadCallback, client);
}
catch (SocketException se)
{
@ -379,7 +382,7 @@ namespace EonaCat.Sockets
catch (ObjectDisposedException) { }
}
private void AcceptCallbackUDP(IAsyncResult ar)
private void AcceptClientsCallbackUDP(IAsyncResult ar)
{
try
{
@ -388,27 +391,28 @@ namespace EonaCat.Sockets
var remoteIp = objects[1] as EndPoint;
var listener = objects[2] as Socket;
var bytesRead = listener.EndReceiveFrom(ar, ref remoteIp);
var clients = ConnectedClients.Where(x => x.Guid == remoteIp.ToString()).ToList();
ClientConnection client;
if (clients.Count == 0)
Client client;
lock (ConnectedClients)
{
client = new ClientConnection()
client = ConnectedClients.FirstOrDefault(x => x.Guid == remoteIp.ToString());
if (client == null)
{
Guid = remoteIp.ToString(),
Socket = new Socket(remoteIp.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
};
client.SendData += SendData;
client.Socket.Connect(remoteIp);
if (client.IsConnected)
{
ConnectedClients.Add(client);
OnConnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
client = new Client()
{
Guid = remoteIp.ToString(),
Socket = new Socket(remoteIp.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
};
client.SendData += SendData;
client.Socket.Connect(remoteIp);
if (client.IsConnected)
{
ConnectedClients.Add(client);
OnConnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
}
}
}
else
{
client = clients.First();
}
if (bytesRead > 1)
{
var data = new byte[bytesRead];
@ -417,16 +421,20 @@ namespace EonaCat.Sockets
}
else
{
if (LastDataReceivedTime.ContainsKey(remoteIp))
lock (LastDataReceivedTime)
{
LastDataReceivedTime[remoteIp] = TimeNow;
}
else
{
LastDataReceivedTime.Add(remoteIp, TimeNow);
if (LastDataReceivedTime.ContainsKey(remoteIp))
{
LastDataReceivedTime[remoteIp] = TimeNow;
}
else
{
LastDataReceivedTime.Add(remoteIp, TimeNow);
}
}
}
Listener.BeginReceiveFrom(buffer, 0, ClientConnection.BufferSize, 0, ref remoteIp, AcceptCallbackUDP, new object[] { buffer, remoteIp, Listener });
Server.BeginReceiveFrom(buffer, 0, Client.BufferSize, 0, ref remoteIp, AcceptClientsCallbackUDP, new object[] { buffer, remoteIp, listener });
}
catch (SocketException se)
{
@ -435,22 +443,30 @@ namespace EonaCat.Sockets
catch (ObjectDisposedException) { }
}
private void ReadCallbackUDP(IAsyncResult ar)
private void ReadClientCallbackUdp(IAsyncResult ar)
{
try
{
var objects = (object[])ar.AsyncState;
var buffer = objects[0] as byte[];
var remote_ip = objects[1] as EndPoint;
var remoteIp = objects[1] as EndPoint;
var listener = objects[2] as Socket;
var bytesRead = listener.EndReceiveFrom(ar, ref remote_ip);
var bytesRead = listener.EndReceiveFrom(ar, ref remoteIp);
if (bytesRead > 0)
{
var data = new byte[bytesRead];
Array.Copy(buffer, 0, data, 0, bytesRead);
OnReceived?.Invoke(this, new SocketServerDataEventArgs() { Data = data });
OnReceived?.Invoke(this, new SocketServerDataEventArgs()
{
Data = data,
Client = new Client()
{
Guid = remoteIp.ToString(),
Socket = new Socket(remoteIp.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
}
});
}
Listener.BeginReceiveFrom(buffer, 0, ClientConnection.BufferSize, 0, ref remote_ip, ReadCallbackUDP, new object[] { buffer, remote_ip, Listener });
Server.BeginReceiveFrom(buffer, 0, Client.BufferSize, 0, ref remoteIp, ReadClientCallbackUdp, new object[] { buffer, remoteIp, Server });
}
catch (SocketException se)
{
@ -463,7 +479,7 @@ namespace EonaCat.Sockets
{
try
{
var client = (ClientConnection)ar.AsyncState;
var client = (Client)ar.AsyncState;
client.Socket.EndDisconnect(ar);
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
lock (ConnectedClients)
@ -481,7 +497,7 @@ namespace EonaCat.Sockets
{
try
{
Listener.BeginSendTo(data, 0, data.Length, 0, point, EndSendTo, null);
Server.BeginSendTo(data, 0, data.Length, 0, point, EndSendTo, null);
}
catch (SocketException se)
{
@ -493,7 +509,7 @@ namespace EonaCat.Sockets
{
try
{
Listener.EndSendTo(ar);
Server.EndSendTo(ar);
}
catch (SocketException se)
{

View File

@ -5,11 +5,12 @@ namespace EonaCat.Sockets
public class SocketServerDataEventArgs : EventArgs
{
/// <summary>
/// Client connection.
/// Client.
/// </summary>
public ClientConnection Client { get; set; }
public Client Client { get; set; }
/// <summary>
/// Recieved data.
/// Received data.
/// </summary>
public byte[] Data { get; set; }
}
@ -19,6 +20,6 @@ namespace EonaCat.Sockets
/// <summary>
/// Client connection.
/// </summary>
public ClientConnection Client { get; set; }
public Client Client { get; set; }
}
}

View File

@ -1,64 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
namespace EonaCat.Sockets
{
public class SocketServerPool : IDisposable
{
public List<SocketServer> Servers { get; set; } = new List<SocketServer>();
public SocketServer this[int index]
{
get
{
if (Servers.Count < index)
return Servers[index];
return null;
}
}
public SocketServer this[string name]
{
get
{
var server = Servers.Where(x => x.Guid == name);
if (server.Count() > 0)
return server.First();
return null;
}
}
public void Dispose()
{
Servers.ForEach(x => x.Stop());
Servers.Clear();
}
public SocketServer Add(NetworkType type, IPAddress ip, int port)
{
SocketServer server = new SocketServer(type, ip, port);
Servers.Add(server);
return server;
}
public bool Remove(string name)
{
int count = Servers.RemoveAll(x => x.Guid == name);
return count > 0 ? true : false;
}
public bool Remove(int index)
{
try
{
Servers.RemoveAt(index);
}
catch
{
return false;
}
return true;
}
}
}