diff --git a/EonaCat.Network/EonaCat.Network.csproj b/EonaCat.Network/EonaCat.Network.csproj index 60c089c..9c7f28c 100644 --- a/EonaCat.Network/EonaCat.Network.csproj +++ b/EonaCat.Network/EonaCat.Network.csproj @@ -18,9 +18,9 @@ EonaCat, Network, .NET Standard, EonaCatHelpers, Jeroen, Saey, Protocol, Quic, UDP, TCP, Web, Server EonaCat Networking library with Quic, TCP, UDP and a Webserver - 1.0.2 - 1.0.0.2 - 1.0.0.2 + 1.0.3 + 1.0.0.3 + 1.0.0.3 icon.png @@ -50,6 +50,7 @@ + diff --git a/EonaCat.Network/System/Sockets/ClientConnection.cs b/EonaCat.Network/System/Sockets/Client.cs similarity index 92% rename from EonaCat.Network/System/Sockets/ClientConnection.cs rename to EonaCat.Network/System/Sockets/Client.cs index 7293580..dda9ab9 100644 --- a/EonaCat.Network/System/Sockets/ClientConnection.cs +++ b/EonaCat.Network/System/Sockets/Client.cs @@ -6,7 +6,7 @@ namespace EonaCat.Sockets { public delegate void SendBytesTo(byte[] data, EndPoint point); - public class ClientConnection : IDisposable + public class Client : IDisposable { /// /// Unique client id. @@ -43,10 +43,7 @@ namespace EonaCat.Sockets public event SendBytesTo SendData; - /// - /// ClientConnection class constructor. - /// - public ClientConnection() + public Client() { Buffer = new byte[BufferSize]; } diff --git a/EonaCat.Network/System/Sockets/SocketClient.cs b/EonaCat.Network/System/Sockets/SocketClient.cs index 00dceb3..8e05950 100644 --- a/EonaCat.Network/System/Sockets/SocketClient.cs +++ b/EonaCat.Network/System/Sockets/SocketClient.cs @@ -5,7 +5,7 @@ using System.Net.Sockets; namespace EonaCat.Sockets { - public class SocketClient : ClientConnection, IDisposable + public class SocketClient : Client, IDisposable { /// /// Client type diff --git a/EonaCat.Network/System/Sockets/SocketServer.cs b/EonaCat.Network/System/Sockets/SocketServer.cs index 796d5dd..b5693d4 100644 --- a/EonaCat.Network/System/Sockets/SocketServer.cs +++ b/EonaCat.Network/System/Sockets/SocketServer.cs @@ -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 /// /// List of connected clients. /// - public List ConnectedClients { get; set; } = new List(); + public List ConnectedClients { get; set; } = new List(); /// /// Interval to check for disconnected clients @@ -44,9 +45,8 @@ namespace EonaCat.Sockets /// /// Determines if the socket is in blocking mode - /// (default: true) /// - public bool IsSocketInBlockingMode { get; set; } = true; + public bool IsSocketBlockingDisabled { get; set; } /// @@ -60,9 +60,9 @@ namespace EonaCat.Sockets /// /// Only for UDP. - /// Required to use SocketClient class for this. + /// (default: true) /// - public bool UDPClientManage { get; set; } = true; + public bool UseClientManager { get; set; } = true; /// /// Only for UDP. @@ -96,18 +96,20 @@ namespace EonaCat.Sockets /// 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 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); } /// @@ -117,12 +119,63 @@ namespace EonaCat.Sockets /// Server listening ip /// Server listening port 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 _removedEndpoints = new List(); + + 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 /// /// Start server /// - 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(); - DisconnectTimer.Elapsed += (s, e) => - { - var now = TimeNow; - var times = LastDataReceivedTime; - var removed = new List(); - 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; } /// @@ -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) { diff --git a/EonaCat.Network/System/Sockets/SocketServerEventArgs.cs b/EonaCat.Network/System/Sockets/SocketServerEventArgs.cs index c874af9..ac64e80 100644 --- a/EonaCat.Network/System/Sockets/SocketServerEventArgs.cs +++ b/EonaCat.Network/System/Sockets/SocketServerEventArgs.cs @@ -5,11 +5,12 @@ namespace EonaCat.Sockets public class SocketServerDataEventArgs : EventArgs { /// - /// Client connection. + /// Client. /// - public ClientConnection Client { get; set; } + public Client Client { get; set; } + /// - /// Recieved data. + /// Received data. /// public byte[] Data { get; set; } } @@ -19,6 +20,6 @@ namespace EonaCat.Sockets /// /// Client connection. /// - public ClientConnection Client { get; set; } + public Client Client { get; set; } } } diff --git a/EonaCat.Network/System/Sockets/SocketServerPool.cs b/EonaCat.Network/System/Sockets/SocketServerPool.cs deleted file mode 100644 index a149c5a..0000000 --- a/EonaCat.Network/System/Sockets/SocketServerPool.cs +++ /dev/null @@ -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 Servers { get; set; } = new List(); - - 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; - } - } -}