Updated
This commit is contained in:
parent
6c6831396e
commit
a5e2cbaf57
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 });
|
||||
|
|
Loading…
Reference in New Issue