This commit is contained in:
EonaCat 2023-03-24 08:31:16 +01:00
parent 6c6831396e
commit a5e2cbaf57
2 changed files with 116 additions and 54 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.1</Version>
<AssemblyVersion>1.0.0.1</AssemblyVersion>
<FileVersion>1.0.0.1</FileVersion>
<Version>1.0.2</Version>
<AssemblyVersion>1.0.0.2</AssemblyVersion>
<FileVersion>1.0.0.2</FileVersion>
<PackageIcon>icon.png</PackageIcon>
</PropertyGroup>

View File

@ -4,13 +4,11 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;
namespace EonaCat.Sockets
{
/// <summary>
///
/// </summary>
public class SocketServer : IDisposable
{
/// <summary>
@ -38,6 +36,19 @@ namespace EonaCat.Sockets
/// </summary>
public int DisconnectionCheckInterval { get; set; } = 100;
/// <summary>
/// Determines if we need to reuse the socket address
/// (default: false)
/// </summary>
public bool ReuseAddress { get; set; }
/// <summary>
/// Determines if the socket is in blocking mode
/// (default: true)
/// </summary>
public bool IsSocketInBlockingMode { get; set; } = true;
/// <summary>
/// Socket backlog. The maximum length of the pending connections queue.
/// </summary>
@ -48,12 +59,14 @@ namespace EonaCat.Sockets
public NetworkType ServerType { get; set; }
/// <summary>
/// Only for UDP. Require to use SocketClient class for this.
/// Only for UDP.
/// Required to use SocketClient class for this.
/// </summary>
public bool UDPClientManage { get; set; } = true;
/// <summary>
/// Only for UDP. Accept 1 byte data for check connected state.
/// Only for UDP.
/// Accept 1 byte data for connected state check.
/// </summary>
private int _UDPDataInterval { get => (int)(UDPDataInterval * 1.5); }
public int UDPDataInterval { get; set; } = 5000;
@ -77,7 +90,7 @@ namespace EonaCat.Sockets
/// <summary>
/// Called when executing Start() function
/// </summary>
public event EventHandler OnStart;
public event EventHandler<string> OnStart;
/// <summary>
/// Called when executing Stop() function
/// </summary>
@ -85,7 +98,7 @@ namespace EonaCat.Sockets
private Socket Listener { get; set; }
private System.Timers.Timer DisconnectTimer { get; set; }
private Dictionary<EndPoint, double> LastDataRecievedTime { 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)
@ -128,6 +141,14 @@ namespace EonaCat.Sockets
{
case NetworkType.Tcp:
Listener = 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;
}
DisconnectTimer.Elapsed += (s, e) =>
{
lock (ConnectedClients)
@ -146,44 +167,52 @@ namespace EonaCat.Sockets
};
break;
case NetworkType.Udp:
{
if (UDPClientManage)
{
LastDataRecievedTime = new Dictionary<EndPoint, double>();
DisconnectTimer.Elapsed += (s, e) =>
if (UDPClientManage)
{
var now = TimeNow;
var times = LastDataRecievedTime;
var removed = new List<EndPoint>();
foreach (var kp in times)
LastDataReceivedTime = new Dictionary<EndPoint, double>();
DisconnectTimer.Elapsed += (s, e) =>
{
if (now - kp.Value > _UDPDataInterval / 1000)
var now = TimeNow;
var times = LastDataReceivedTime;
var removed = new List<EndPoint>();
foreach (var kp in times)
{
lock (ConnectedClients)
if (now - kp.Value > _UDPDataInterval / 1000)
{
var client = ConnectedClients.Where(x => x.Guid == kp.Key.ToString());
if (!client.Any())
lock (ConnectedClients)
{
continue;
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);
}
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client.First() });
ConnectedClients.Remove(client.First());
removed.Add(kp.Key);
}
}
}
lock (LastDataRecievedTime)
{
foreach (var r in removed)
lock (LastDataReceivedTime)
{
LastDataRecievedTime.Remove(r);
foreach (var r in removed)
{
LastDataReceivedTime.Remove(r);
}
}
}
};
};
}
Listener = 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;
}
break;
}
Listener = new Socket(IPAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
break;
}
}
Listener.ReceiveBufferSize = ClientConnection.BufferSize;
Listener.SendBufferSize = ClientConnection.BufferSize;
@ -201,6 +230,47 @@ namespace EonaCat.Sockets
}
}
/// <summary>
/// Setup this udp socket to override the default one
/// (default: null => using default)
/// </summary>
public Socket CustomUdpSocket { get; set; }
/// <summary>
/// Setup this tcp socket to override the default one
/// (default: null => using default)
/// </summary>
public Socket CustomTcpSocket { get; set; }
static void SetConnectionReset(Socket socket)
{
try
{
if (socket.ProtocolType == ProtocolType.Udp && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// Disable ICMP packet shutdown (forcibly closed)
const uint IOC_IN = 0x80000000;
const uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
socket.IOControl((int)SIO_UDP_CONNRESET, new[] { Convert.ToByte(false) }, null);
};
}
catch { }
}
private void ConfigureSocketOptions()
{
// Disable ICMP packet shutdown
SetConnectionReset(Listener);
if (ReuseAddress)
{
Listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
}
Listener.Blocking = IsSocketInBlockingMode;
}
/// <summary>
/// Stop server
/// </summary>
@ -209,18 +279,18 @@ namespace EonaCat.Sockets
try
{
DisconnectTimer.Stop();
foreach (var c in ConnectedClients)
foreach (var client in ConnectedClients)
{
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = c });
c.Socket.Close();
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
client.Socket.Close();
}
ConnectedClients.Clear();
OnStop?.Invoke(this, EventArgs.Empty);
Listener.Close();
}
catch (SocketException se)
catch (SocketException socketException)
{
OnError?.Invoke(this, new ErrorEventArgs(se));
OnError?.Invoke(this, new ErrorEventArgs(socketException));
}
}
@ -228,7 +298,7 @@ namespace EonaCat.Sockets
{
try
{
OnStart?.Invoke(this, EventArgs.Empty);
OnStart?.Invoke(this, CustomTcpSocket != null || CustomUdpSocket != null ? "Custom socket was used" : string.Empty);
if (ServerType == NetworkType.Tcp)
{
Listener.BeginAccept(AcceptCallback, Listener);
@ -317,15 +387,7 @@ namespace EonaCat.Sockets
var buffer = objects[0] as byte[];
var remoteIp = objects[1] as EndPoint;
var listener = objects[2] as Socket;
var bytesRead = 0;
try
{
bytesRead = listener.EndReceiveFrom(ar, ref remoteIp);
}
catch (SocketException se)
{
// ignore
}
var bytesRead = listener.EndReceiveFrom(ar, ref remoteIp);
var clients = ConnectedClients.Where(x => x.Guid == remoteIp.ToString()).ToList();
ClientConnection client;
if (clients.Count == 0)
@ -355,13 +417,13 @@ namespace EonaCat.Sockets
}
else
{
if (LastDataRecievedTime.ContainsKey(remoteIp))
if (LastDataReceivedTime.ContainsKey(remoteIp))
{
LastDataRecievedTime[remoteIp] = TimeNow;
LastDataReceivedTime[remoteIp] = TimeNow;
}
else
{
LastDataRecievedTime.Add(remoteIp, TimeNow);
LastDataReceivedTime.Add(remoteIp, TimeNow);
}
}
Listener.BeginReceiveFrom(buffer, 0, ClientConnection.BufferSize, 0, ref remoteIp, AcceptCallbackUDP, new object[] { buffer, remoteIp, Listener });