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> <PackageTags>EonaCat, Network, .NET Standard, EonaCatHelpers, Jeroen, Saey, Protocol, Quic, UDP, TCP, Web, Server</PackageTags>
<PackageReleaseNotes></PackageReleaseNotes> <PackageReleaseNotes></PackageReleaseNotes>
<Description>EonaCat Networking library with Quic, TCP, UDP and a Webserver</Description> <Description>EonaCat Networking library with Quic, TCP, UDP and a Webserver</Description>
<Version>1.0.1</Version> <Version>1.0.2</Version>
<AssemblyVersion>1.0.0.1</AssemblyVersion> <AssemblyVersion>1.0.0.2</AssemblyVersion>
<FileVersion>1.0.0.1</FileVersion> <FileVersion>1.0.0.2</FileVersion>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
</PropertyGroup> </PropertyGroup>

View File

@ -4,13 +4,11 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
namespace EonaCat.Sockets namespace EonaCat.Sockets
{ {
/// <summary>
///
/// </summary>
public class SocketServer : IDisposable public class SocketServer : IDisposable
{ {
/// <summary> /// <summary>
@ -38,6 +36,19 @@ namespace EonaCat.Sockets
/// </summary> /// </summary>
public int DisconnectionCheckInterval { get; set; } = 100; 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> /// <summary>
/// Socket backlog. The maximum length of the pending connections queue. /// Socket backlog. The maximum length of the pending connections queue.
/// </summary> /// </summary>
@ -48,12 +59,14 @@ namespace EonaCat.Sockets
public NetworkType ServerType { get; set; } public NetworkType ServerType { get; set; }
/// <summary> /// <summary>
/// Only for UDP. Require to use SocketClient class for this. /// Only for UDP.
/// Required to use SocketClient class for this.
/// </summary> /// </summary>
public bool UDPClientManage { get; set; } = true; public bool UDPClientManage { get; set; } = true;
/// <summary> /// <summary>
/// Only for UDP. Accept 1 byte data for check connected state. /// Only for UDP.
/// Accept 1 byte data for connected state check.
/// </summary> /// </summary>
private int _UDPDataInterval { get => (int)(UDPDataInterval * 1.5); } private int _UDPDataInterval { get => (int)(UDPDataInterval * 1.5); }
public int UDPDataInterval { get; set; } = 5000; public int UDPDataInterval { get; set; } = 5000;
@ -77,7 +90,7 @@ namespace EonaCat.Sockets
/// <summary> /// <summary>
/// Called when executing Start() function /// Called when executing Start() function
/// </summary> /// </summary>
public event EventHandler OnStart; public event EventHandler<string> OnStart;
/// <summary> /// <summary>
/// Called when executing Stop() function /// Called when executing Stop() function
/// </summary> /// </summary>
@ -85,7 +98,7 @@ namespace EonaCat.Sockets
private Socket Listener { get; set; } private Socket Listener { get; set; }
private System.Timers.Timer DisconnectTimer { 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; } private double TimeNow { get => (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; }
public SocketServer(NetworkType type, string address, int port) public SocketServer(NetworkType type, string address, int port)
@ -128,6 +141,14 @@ namespace EonaCat.Sockets
{ {
case NetworkType.Tcp: case NetworkType.Tcp:
Listener = new Socket(IPAddress.AddressFamily, SocketType.Stream, ProtocolType.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) => DisconnectTimer.Elapsed += (s, e) =>
{ {
lock (ConnectedClients) lock (ConnectedClients)
@ -146,44 +167,52 @@ namespace EonaCat.Sockets
}; };
break; break;
case NetworkType.Udp: case NetworkType.Udp:
{
if (UDPClientManage)
{ {
LastDataRecievedTime = new Dictionary<EndPoint, double>(); if (UDPClientManage)
DisconnectTimer.Elapsed += (s, e) =>
{ {
var now = TimeNow; LastDataReceivedTime = new Dictionary<EndPoint, double>();
var times = LastDataRecievedTime; DisconnectTimer.Elapsed += (s, e) =>
var removed = new List<EndPoint>();
foreach (var kp in times)
{ {
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()); lock (ConnectedClients)
if (!client.Any())
{ {
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 (LastDataReceivedTime)
lock (LastDataRecievedTime)
{
foreach (var r in removed)
{ {
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.ReceiveBufferSize = ClientConnection.BufferSize;
Listener.SendBufferSize = 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> /// <summary>
/// Stop server /// Stop server
/// </summary> /// </summary>
@ -209,18 +279,18 @@ namespace EonaCat.Sockets
try try
{ {
DisconnectTimer.Stop(); DisconnectTimer.Stop();
foreach (var c in ConnectedClients) foreach (var client in ConnectedClients)
{ {
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = c }); OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
c.Socket.Close(); client.Socket.Close();
} }
ConnectedClients.Clear(); ConnectedClients.Clear();
OnStop?.Invoke(this, EventArgs.Empty); OnStop?.Invoke(this, EventArgs.Empty);
Listener.Close(); 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 try
{ {
OnStart?.Invoke(this, EventArgs.Empty); OnStart?.Invoke(this, CustomTcpSocket != null || CustomUdpSocket != null ? "Custom socket was used" : string.Empty);
if (ServerType == NetworkType.Tcp) if (ServerType == NetworkType.Tcp)
{ {
Listener.BeginAccept(AcceptCallback, Listener); Listener.BeginAccept(AcceptCallback, Listener);
@ -317,15 +387,7 @@ namespace EonaCat.Sockets
var buffer = objects[0] as byte[]; var buffer = objects[0] as byte[];
var remoteIp = objects[1] as EndPoint; var remoteIp = objects[1] as EndPoint;
var listener = objects[2] as Socket; var listener = objects[2] as Socket;
var bytesRead = 0; var bytesRead = listener.EndReceiveFrom(ar, ref remoteIp);
try
{
bytesRead = listener.EndReceiveFrom(ar, ref remoteIp);
}
catch (SocketException se)
{
// ignore
}
var clients = ConnectedClients.Where(x => x.Guid == remoteIp.ToString()).ToList(); var clients = ConnectedClients.Where(x => x.Guid == remoteIp.ToString()).ToList();
ClientConnection client; ClientConnection client;
if (clients.Count == 0) if (clients.Count == 0)
@ -355,13 +417,13 @@ namespace EonaCat.Sockets
} }
else else
{ {
if (LastDataRecievedTime.ContainsKey(remoteIp)) if (LastDataReceivedTime.ContainsKey(remoteIp))
{ {
LastDataRecievedTime[remoteIp] = TimeNow; LastDataReceivedTime[remoteIp] = TimeNow;
} }
else else
{ {
LastDataRecievedTime.Add(remoteIp, TimeNow); LastDataReceivedTime.Add(remoteIp, TimeNow);
} }
} }
Listener.BeginReceiveFrom(buffer, 0, ClientConnection.BufferSize, 0, ref remoteIp, AcceptCallbackUDP, new object[] { buffer, remoteIp, Listener }); Listener.BeginReceiveFrom(buffer, 0, ClientConnection.BufferSize, 0, ref remoteIp, AcceptCallbackUDP, new object[] { buffer, remoteIp, Listener });