Updated
This commit is contained in:
parent
fb824136d9
commit
4767d7604c
|
@ -1,86 +0,0 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace EonaCat.Sockets
|
||||
{
|
||||
public delegate void SendBytesTo(byte[] data, EndPoint point);
|
||||
|
||||
public class Client : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique client id.
|
||||
/// </summary>
|
||||
public string Guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Client socket.
|
||||
/// </summary>
|
||||
public Socket Socket { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Receive buffer.
|
||||
/// </summary>
|
||||
public byte[] Buffer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Buffer size
|
||||
/// </summary>
|
||||
public static int BufferSize { get; set; } = 1024 * 1024; // 1024KB
|
||||
|
||||
/// <summary>
|
||||
/// Socket connection state.
|
||||
/// </summary>
|
||||
public bool IsConnected => !((Socket.Poll(1000, SelectMode.SelectRead) && (Socket.Available == 0)) || !Socket.Connected);
|
||||
/// <summary>
|
||||
/// Client ip.
|
||||
/// </summary>
|
||||
public IPAddress RemoteIP => (Socket.RemoteEndPoint as IPEndPoint)?.Address;
|
||||
/// <summary>
|
||||
/// Client port.
|
||||
/// </summary>
|
||||
public int RemotePort => (Socket.RemoteEndPoint as IPEndPoint).Port;
|
||||
|
||||
public event SendBytesTo SendData;
|
||||
|
||||
public Client()
|
||||
{
|
||||
Buffer = new byte[BufferSize];
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Socket.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send data to the client.
|
||||
/// </summary>
|
||||
/// <param name="data">Data for sending</param>
|
||||
public void Send(byte[] data)
|
||||
{
|
||||
switch (Socket.SocketType)
|
||||
{
|
||||
case SocketType.Stream:
|
||||
Socket.BeginSend(data, 0, data.Length, 0, EndSend, null);
|
||||
break;
|
||||
case SocketType.Dgram when SendData != null:
|
||||
SendData?.Invoke(data, Socket.RemoteEndPoint);
|
||||
break;
|
||||
case SocketType.Dgram:
|
||||
Socket.BeginSendTo(data, 0, data.Length, 0, Socket.RemoteEndPoint, EndSendTo, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void EndSend(IAsyncResult ar)
|
||||
{
|
||||
Socket.EndSend(ar);
|
||||
}
|
||||
|
||||
private void EndSendTo(IAsyncResult ar)
|
||||
{
|
||||
Socket.EndSendTo(ar);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace EonaCat.Sockets
|
||||
{
|
||||
[Flags]
|
||||
public enum NetworkType
|
||||
{
|
||||
Tcp,
|
||||
Udp
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace EonaCat.Dns.Core.Sockets
|
||||
{
|
||||
public class RemoteInfo
|
||||
{
|
||||
public bool IsTcp { get; set; }
|
||||
public bool HasEndpoint => EndPoint != null;
|
||||
public EndPoint EndPoint { get; set; }
|
||||
public byte[] Data { get; set; }
|
||||
public IPAddress Address => HasEndpoint ? ((IPEndPoint)EndPoint).Address : null;
|
||||
public int Port => HasEndpoint ? ((IPEndPoint)EndPoint).Port : 0;
|
||||
public Socket Socket { get; set; }
|
||||
public bool IsIpv6 { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,189 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace EonaCat.Sockets
|
||||
{
|
||||
public class SocketClient : Client, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Client type
|
||||
/// </summary>
|
||||
public NetworkType ClientType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timer interval
|
||||
/// </summary>
|
||||
public int LoopInterval { get; set; } = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Only for UDP. Sending 1 byte data for check connected state.
|
||||
/// </summary>
|
||||
public int UDPDataInterval { get; set; } = 5000;
|
||||
|
||||
/// <summary>
|
||||
/// Called when client connected
|
||||
/// </summary>
|
||||
public event EventHandler OnConnected;
|
||||
/// <summary>
|
||||
/// Called when client disconnected
|
||||
/// </summary>
|
||||
public event EventHandler OnDisconnected;
|
||||
|
||||
/// <summary>
|
||||
/// Called when received data from server
|
||||
/// </summary>
|
||||
public event EventHandler<SocketClientEventArgs> OnReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Called when client has an error
|
||||
/// </summary>
|
||||
public event EventHandler<ErrorEventArgs> OnError;
|
||||
|
||||
private System.Timers.Timer UDPTimer { get; set; }
|
||||
private System.Timers.Timer DisconnectTimer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SocketClient class constructor.
|
||||
/// </summary>
|
||||
/// <param name="type">Client type</param>
|
||||
public SocketClient(NetworkType type)
|
||||
{
|
||||
ClientType = type;
|
||||
UDPTimer = new System.Timers.Timer(UDPDataInterval);
|
||||
UDPTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
if (IsConnected)
|
||||
Socket.Send(new byte[] { 0 });
|
||||
};
|
||||
DisconnectTimer = new System.Timers.Timer(LoopInterval);
|
||||
DisconnectTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
if (!IsConnected)
|
||||
{
|
||||
Disconnect();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public new void Dispose()
|
||||
{
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect to remote host
|
||||
/// </summary>
|
||||
/// <param name="address">Remote ip address</param>
|
||||
/// <param name="port">Remote port</param>
|
||||
public void Connect(IPAddress address, int port) => Connect(new IPEndPoint(address, port));
|
||||
|
||||
/// <summary>
|
||||
/// Connect to remote host
|
||||
/// </summary>
|
||||
/// <param name="point">Remote end point</param>
|
||||
public void Connect(IPEndPoint point)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ClientType == NetworkType.Tcp)
|
||||
{
|
||||
Socket = new Socket(point.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
Socket.BeginConnect(point, ConnectCallback, null);
|
||||
}
|
||||
else if (ClientType == NetworkType.Udp)
|
||||
{
|
||||
Socket = new Socket(point.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
|
||||
Socket.Connect(point);
|
||||
Socket.Send(new byte[] { 0 });
|
||||
OnConnected?.Invoke(this, EventArgs.Empty);
|
||||
UDPTimer.Start();
|
||||
DisconnectTimer.Start();
|
||||
Socket.BeginReceive(Buffer, 0, BufferSize, 0, ReadCallbackUDP, null);
|
||||
}
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(se));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from remote host
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Socket == null)
|
||||
return;
|
||||
UDPTimer.Stop();
|
||||
DisconnectTimer.Stop();
|
||||
Socket.Close();
|
||||
OnDisconnected?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(se));
|
||||
}
|
||||
}
|
||||
|
||||
private void ConnectCallback(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
Socket.EndConnect(ar);
|
||||
OnConnected?.Invoke(this, EventArgs.Empty);
|
||||
DisconnectTimer.Start();
|
||||
Socket.BeginReceive(Buffer, 0, BufferSize, 0, ReadCallback, null);
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(se));
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadCallback(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Socket.Connected)
|
||||
return;
|
||||
int readBytes = Socket.EndReceive(ar);
|
||||
if (readBytes > 0)
|
||||
{
|
||||
byte[] data = new byte[readBytes];
|
||||
Array.Copy(Buffer, data, readBytes);
|
||||
OnReceived?.Invoke(this, new SocketClientEventArgs() { Data = data });
|
||||
}
|
||||
Socket.BeginReceive(Buffer, 0, BufferSize, 0, ReadCallback, null);
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(se));
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadCallbackUDP(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Socket.Connected)
|
||||
return;
|
||||
int readBytes = Socket.EndReceive(ar);
|
||||
if (readBytes > 0)
|
||||
{
|
||||
byte[] data = new byte[readBytes];
|
||||
Array.Copy(Buffer, data, readBytes);
|
||||
OnReceived?.Invoke(this, new SocketClientEventArgs() { Data = data });
|
||||
}
|
||||
Socket.BeginReceive(Buffer, 0, BufferSize, 0, ReadCallbackUDP, null);
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(se));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace EonaCat.Sockets
|
||||
{
|
||||
public class SocketClientEventArgs : EventArgs
|
||||
{
|
||||
public byte[] Data { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,542 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
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
|
||||
{
|
||||
public class SocketServer : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique server id.
|
||||
/// </summary>
|
||||
public string Guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Listening ip address.
|
||||
/// </summary>
|
||||
public IPAddress IPAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Listening port.
|
||||
/// </summary>
|
||||
public int Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of connected clients.
|
||||
/// </summary>
|
||||
public List<Client> ConnectedClients { get; set; } = new List<Client>();
|
||||
|
||||
/// <summary>
|
||||
/// Interval to check for disconnected clients
|
||||
/// </summary>
|
||||
public int DisconnectionCheckInterval { get; set; } = 50;
|
||||
|
||||
/// <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
|
||||
/// </summary>
|
||||
public bool IsSocketBlockingDisabled { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Socket backlog. The maximum length of the pending connections queue.
|
||||
/// </summary>
|
||||
public int Backlog { get; set; } = 0;
|
||||
/// <summary>
|
||||
/// Server type.
|
||||
/// </summary>
|
||||
public NetworkType ServerType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Only for UDP.
|
||||
/// (default: true)
|
||||
/// </summary>
|
||||
public bool UseClientManager { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
|
||||
/// <summary>
|
||||
/// Called when new client connected
|
||||
/// </summary>
|
||||
public event EventHandler<SocketServerClientEventArgs> OnConnected;
|
||||
/// <summary>
|
||||
/// Called when client disconnected
|
||||
/// </summary>
|
||||
public event EventHandler<SocketServerClientEventArgs> OnDisconnected;
|
||||
/// <summary>
|
||||
/// Called when received data from client
|
||||
/// </summary>
|
||||
public event EventHandler<SocketServerDataEventArgs> OnReceived;
|
||||
/// <summary>
|
||||
/// Called when server catch error
|
||||
/// </summary>
|
||||
public event EventHandler<ErrorEventArgs> OnError;
|
||||
/// <summary>
|
||||
/// Called when executing Start() function
|
||||
/// </summary>
|
||||
public event EventHandler<string> OnStart;
|
||||
/// <summary>
|
||||
/// Called when executing Stop() function
|
||||
/// </summary>
|
||||
public event EventHandler OnStop;
|
||||
|
||||
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))
|
||||
{
|
||||
throw new Exception("Invalid IP address specified");
|
||||
}
|
||||
|
||||
SetupSocketServer(type, ipAddress, port);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="type">Server type (TCP, UDP)</param>
|
||||
/// <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;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start server
|
||||
/// </summary>
|
||||
public async Task StartAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (ServerType)
|
||||
{
|
||||
case NetworkType.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)
|
||||
{
|
||||
Server = CustomTcpSocket;
|
||||
}
|
||||
|
||||
DisconnectTimerTcp = new EonaCatTimer(TimeSpan.FromMilliseconds(DisconnectionCheckInterval), TcpCheckCallback);
|
||||
DisconnectTimerTcp.Start();
|
||||
break;
|
||||
case NetworkType.Udp:
|
||||
{
|
||||
if (UseClientManager)
|
||||
{
|
||||
LastDataReceivedTime = new Dictionary<EndPoint, double>();
|
||||
DisconnectTimerUdp = new EonaCatTimer(TimeSpan.FromMilliseconds(DisconnectionCheckInterval), UdpCheckCallback);
|
||||
DisconnectTimerUdp.Start();
|
||||
}
|
||||
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)
|
||||
{
|
||||
Server = CustomUdpSocket;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
Server.ReceiveBufferSize = Client.BufferSize;
|
||||
Server.SendBufferSize = Client.BufferSize;
|
||||
Server.Bind(new IPEndPoint(IPAddress, Port));
|
||||
if (ServerType == NetworkType.Tcp)
|
||||
{
|
||||
Server.Listen(Backlog);
|
||||
}
|
||||
await Task.Run(ListenLoop);
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(se));
|
||||
}
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
|
||||
private static void SetConnectionReset(Socket socket)
|
||||
{
|
||||
if (socket.ProtocolType != ProtocolType.Udp || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
private void ConfigureSocketOptions()
|
||||
{
|
||||
// Disable ICMP packet shutdown
|
||||
SetConnectionReset(Server);
|
||||
|
||||
if (ReuseAddress)
|
||||
{
|
||||
Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
}
|
||||
|
||||
Server.Blocking = IsSocketBlockingDisabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop server
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
DisconnectTimerUdp?.Stop();
|
||||
DisconnectTimerTcp?.Stop();
|
||||
foreach (var client in ConnectedClients)
|
||||
{
|
||||
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
|
||||
client.Socket.Close();
|
||||
}
|
||||
ConnectedClients.Clear();
|
||||
OnStop?.Invoke(this, EventArgs.Empty);
|
||||
Server.Close();
|
||||
}
|
||||
catch (SocketException socketException)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(socketException));
|
||||
}
|
||||
}
|
||||
|
||||
private void ListenLoop()
|
||||
{
|
||||
try
|
||||
{
|
||||
OnStart?.Invoke(this, CustomTcpSocket != null || CustomUdpSocket != null ? "Custom socket was used" : string.Empty);
|
||||
|
||||
switch (ServerType)
|
||||
{
|
||||
case NetworkType.Tcp:
|
||||
BeginTcpAccept();
|
||||
break;
|
||||
|
||||
case NetworkType.Udp:
|
||||
BeginUdpReceive();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Protocol type not supported");
|
||||
}
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(se));
|
||||
}
|
||||
}
|
||||
|
||||
private void BeginTcpAccept()
|
||||
{
|
||||
Server.BeginAccept(AcceptCallback, Server);
|
||||
}
|
||||
|
||||
private void BeginUdpReceive()
|
||||
{
|
||||
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 });
|
||||
}
|
||||
|
||||
private void ReadCallback(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = (Client)ar.AsyncState;
|
||||
var bytesRead = client.Socket.EndReceive(ar);
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
var data = new byte[bytesRead];
|
||||
Array.Copy(client.Buffer, 0, data, 0, bytesRead);
|
||||
OnReceived?.Invoke(this, new SocketServerDataEventArgs() { Client = client, Data = data });
|
||||
|
||||
// read any remaining data
|
||||
while (client.Socket.Available > 0)
|
||||
{
|
||||
bytesRead = client.Socket.Receive(client.Buffer, 0, Client.BufferSize, SocketFlags.None);
|
||||
data = new byte[bytesRead];
|
||||
Array.Copy(client.Buffer, 0, data, 0, bytesRead);
|
||||
OnReceived?.Invoke(this, new SocketServerDataEventArgs() { Client = client, Data = data });
|
||||
}
|
||||
|
||||
client.Socket.BeginReceive(client.Buffer, 0, Client.BufferSize, 0, ReadCallback, client); // recursive call
|
||||
}
|
||||
else
|
||||
{
|
||||
OnDisconnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
|
||||
lock (ConnectedClients)
|
||||
{
|
||||
ConnectedClients.Remove(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(se));
|
||||
}
|
||||
catch (ObjectDisposedException) { }
|
||||
}
|
||||
|
||||
private void AcceptCallback(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
var listener = (Socket)ar.AsyncState;
|
||||
var handler = listener.EndAccept(ar);
|
||||
var client = new Client()
|
||||
{
|
||||
Guid = System.Guid.NewGuid().ToString(),
|
||||
Socket = handler
|
||||
};
|
||||
lock (ConnectedClients)
|
||||
{
|
||||
ConnectedClients.Add(client);
|
||||
}
|
||||
|
||||
OnConnected?.Invoke(this, new SocketServerClientEventArgs() { Client = client });
|
||||
client.Socket.BeginReceive(client.Buffer, 0, Client.BufferSize, 0, ReadCallback, client); // recursive call
|
||||
|
||||
Thread.Sleep(DisconnectionCheckInterval);
|
||||
|
||||
if (listener.IsBound) // check if the listener is still bound
|
||||
{
|
||||
listener.BeginAccept(AcceptCallback, Server); // recursive call
|
||||
}
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(se));
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void AcceptClientsCallbackUdp(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
var objects = (object[])ar.AsyncState;
|
||||
var buffer = objects[0] as byte[];
|
||||
var remoteIp = objects[1] as EndPoint;
|
||||
var listener = objects[2] as Socket;
|
||||
var bytesRead = listener.EndReceiveFrom(ar, ref remoteIp);
|
||||
|
||||
Client client;
|
||||
lock (ConnectedClients)
|
||||
{
|
||||
client = ConnectedClients.FirstOrDefault(x => x.Guid == remoteIp.ToString());
|
||||
if (client == null)
|
||||
{
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
var data = new byte[bytesRead];
|
||||
Array.Copy(buffer, 0, data, 0, bytesRead);
|
||||
OnReceived?.Invoke(this, new SocketServerDataEventArgs() { Client = client, Data = data, });
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (LastDataReceivedTime)
|
||||
{
|
||||
if (LastDataReceivedTime.ContainsKey(remoteIp))
|
||||
{
|
||||
LastDataReceivedTime[remoteIp] = TimeNow;
|
||||
}
|
||||
else
|
||||
{
|
||||
LastDataReceivedTime.Add(remoteIp, TimeNow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Server.BeginReceiveFrom(buffer, 0, Client.BufferSize, 0, ref remoteIp, AcceptClientsCallbackUdp, new object[] { buffer, remoteIp, listener });
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(se));
|
||||
}
|
||||
catch (ObjectDisposedException) { }
|
||||
}
|
||||
|
||||
private void ReadClientCallbackUdp(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
var objects = (object[])ar.AsyncState;
|
||||
var buffer = objects[0] as byte[];
|
||||
var remoteIp = objects[1] as EndPoint;
|
||||
var listener = objects[2] as Socket;
|
||||
var bytesRead = listener.EndReceiveFrom(ar, ref remoteIp);
|
||||
while (bytesRead > 0)
|
||||
{
|
||||
var data = new byte[bytesRead];
|
||||
Array.Copy(buffer, 0, data, 0, bytesRead);
|
||||
OnReceived?.Invoke(this, new SocketServerDataEventArgs()
|
||||
{
|
||||
Data = data,
|
||||
Client = new Client()
|
||||
{
|
||||
Guid = remoteIp.ToString(),
|
||||
Socket = new Socket(remoteIp.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
|
||||
}
|
||||
});
|
||||
|
||||
// read any remaining data
|
||||
bytesRead = listener.ReceiveFrom(buffer, 0, Client.BufferSize, SocketFlags.None, ref remoteIp);
|
||||
}
|
||||
listener.BeginReceiveFrom(buffer, 0, Client.BufferSize, 0, ref remoteIp, ReadClientCallbackUdp, new object[] { buffer, remoteIp, listener });
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(se));
|
||||
}
|
||||
catch (ObjectDisposedException) { }
|
||||
}
|
||||
|
||||
public void SendData(byte[] data, EndPoint point)
|
||||
{
|
||||
try
|
||||
{
|
||||
Server.BeginSendTo(data, 0, data.Length, 0, point, EndSendTo, null);
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(se));
|
||||
}
|
||||
}
|
||||
|
||||
private void EndSendTo(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
Server.EndSendTo(ar);
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
OnError?.Invoke(this, new ErrorEventArgs(se));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace EonaCat.Sockets
|
||||
{
|
||||
public class SocketServerDataEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Client.
|
||||
/// </summary>
|
||||
public Client Client { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Received data.
|
||||
/// </summary>
|
||||
public byte[] Data { get; set; }
|
||||
}
|
||||
|
||||
public class SocketServerClientEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Client connection.
|
||||
/// </summary>
|
||||
public Client Client { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EonaCat.Dns.Core.Sockets.Tcp
|
||||
{
|
||||
public class SocketTcpClient
|
||||
{
|
||||
public event Action<RemoteInfo> OnConnect;
|
||||
public event Action<RemoteInfo> OnReceive;
|
||||
public event Action<RemoteInfo> OnDisconnect;
|
||||
public event Action<RemoteInfo> OnSend;
|
||||
|
||||
private Socket socket;
|
||||
|
||||
public Task ConnectAsync(string ipAddress, int port)
|
||||
{
|
||||
if (!IPAddress.TryParse(ipAddress, out IPAddress ip))
|
||||
{
|
||||
throw new Exception("Invalid ipAddress given");
|
||||
}
|
||||
|
||||
return CreateSocketTcpClientAsync(ip, port);
|
||||
}
|
||||
|
||||
private async Task CreateSocketTcpClientAsync(IPAddress ipAddress, int port)
|
||||
{
|
||||
IsIP6 = ipAddress.AddressFamily == AddressFamily.InterNetworkV6;
|
||||
socket = new Socket(IsIP6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
try
|
||||
{
|
||||
await socket.ConnectAsync(ipAddress, port).ConfigureAwait(false);
|
||||
OnConnect?.Invoke(new RemoteInfo { IsTcp = true, IsIpv6 = IsIP6 });
|
||||
_ = StartReceivingAsync();
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
Console.WriteLine($"SocketException: {ex.Message}");
|
||||
Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsIP6 { get; private set; }
|
||||
|
||||
private Task StartReceivingAsync()
|
||||
{
|
||||
byte[] buffer = new byte[1024];
|
||||
return Task.Factory.StartNew(async () =>
|
||||
{
|
||||
while (socket.Connected)
|
||||
{
|
||||
try
|
||||
{
|
||||
int received = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None).ConfigureAwait(false);
|
||||
if (received > 0)
|
||||
{
|
||||
byte[] data = new byte[received];
|
||||
Array.Copy(buffer, data, received);
|
||||
OnReceive?.Invoke(new RemoteInfo
|
||||
{ IsTcp = true, Data = data, EndPoint = socket.RemoteEndPoint, IsIpv6 = IsIP6 });
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
Console.WriteLine($"SocketException: {ex.Message}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
OnDisconnect?.Invoke(new RemoteInfo { IsTcp = true, EndPoint = socket.RemoteEndPoint, IsIpv6 = IsIP6 });
|
||||
});
|
||||
}
|
||||
|
||||
public async Task SendAsync(byte[] data)
|
||||
{
|
||||
await socket.SendAsync(new ArraySegment<byte>(data), SocketFlags.None).ConfigureAwait(false);
|
||||
OnSend?.Invoke(new RemoteInfo { EndPoint = socket.RemoteEndPoint, Data = data, IsIpv6 = IsIP6 });
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
socket.Close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EonaCat.Dns.Core.Sockets.Tcp
|
||||
{
|
||||
public class SocketTcpServer
|
||||
{
|
||||
public event Action<RemoteInfo> OnConnect;
|
||||
public event Action<RemoteInfo> OnReceive;
|
||||
public event Action<RemoteInfo> OnSend;
|
||||
public event Action<RemoteInfo> OnDisconnect;
|
||||
|
||||
private TcpListener listener;
|
||||
|
||||
public SocketTcpServer(IPAddress ipAddress, int port)
|
||||
{
|
||||
listener = new TcpListener(ipAddress, port);
|
||||
}
|
||||
|
||||
public SocketTcpServer(string ipAddress, int port)
|
||||
{
|
||||
IPAddress address = IPAddress.Parse(ipAddress);
|
||||
listener = new TcpListener(address, port);
|
||||
}
|
||||
|
||||
public async Task Start()
|
||||
{
|
||||
listener.Start();
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
|
||||
Socket socket = client.Client;
|
||||
OnConnect?.Invoke(new RemoteInfo { Socket = socket, IsTcp = true, IsIpv6 = socket.RemoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6, EndPoint = socket.RemoteEndPoint });
|
||||
_ = StartReceiving(socket);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
Console.WriteLine($"SocketException: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartReceiving(Socket socket)
|
||||
{
|
||||
byte[] buffer = new byte[1024];
|
||||
while (socket.Connected)
|
||||
{
|
||||
try
|
||||
{
|
||||
int received = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None).ConfigureAwait(false);
|
||||
if (received > 0)
|
||||
{
|
||||
byte[] data = new byte[received];
|
||||
Array.Copy(buffer, data, received);
|
||||
OnReceive?.Invoke(new RemoteInfo { Socket = socket, IsTcp = true, IsIpv6 = socket.RemoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6, EndPoint = socket.RemoteEndPoint, Data = data});
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
Console.WriteLine($"SocketException: {ex.Message}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
OnDisconnect?.Invoke(new RemoteInfo {Socket = socket, IsIpv6 = socket.RemoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6, IsTcp = true, EndPoint = socket.RemoteEndPoint });
|
||||
}
|
||||
|
||||
public async Task SendTo(Socket socket, byte[] data)
|
||||
{
|
||||
await socket.SendAsync(new ArraySegment<byte>(data), SocketFlags.None).ConfigureAwait(false);
|
||||
OnSend?.Invoke(new RemoteInfo { Socket = socket, IsTcp = true, EndPoint = socket.RemoteEndPoint, IsIpv6 = socket.RemoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6, Data = data });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
|
||||
namespace EonaCat.Dns.Core.Sockets.Udp
|
||||
{
|
||||
public class SocketUdpClient
|
||||
{
|
||||
public event Action<EndPoint> OnConnect;
|
||||
public event Action<byte[]> OnReceive;
|
||||
public event Action<EndPoint> OnDisconnect;
|
||||
public event Action<EndPoint, byte[]> OnSend;
|
||||
|
||||
private UdpClient udpClient;
|
||||
|
||||
public SocketUdpClient(IPAddress ipAddress, int port)
|
||||
{
|
||||
CreateUdpClient(ipAddress, port);
|
||||
}
|
||||
|
||||
private void CreateUdpClient(IPAddress ipAddress, int port)
|
||||
{
|
||||
IsIP6 = ipAddress.AddressFamily == AddressFamily.InterNetworkV6;
|
||||
udpClient = new UdpClient(ipAddress.AddressFamily);
|
||||
|
||||
if (IsIP6)
|
||||
{
|
||||
udpClient.Client.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
|
||||
}
|
||||
|
||||
udpClient.Client.Bind(new IPEndPoint(IsIP6 ? IPAddress.IPv6Any : IPAddress.Any, 0));
|
||||
|
||||
if (IsMulticastGroupEnabled)
|
||||
{
|
||||
udpClient.JoinMulticastGroup(ipAddress);
|
||||
}
|
||||
|
||||
_ = StartReceivingAsync();
|
||||
}
|
||||
|
||||
public bool IsMulticastGroupEnabled { get; set; }
|
||||
|
||||
public bool IsIP6 { get; private set; }
|
||||
|
||||
public SocketUdpClient(string ipAddress, int port)
|
||||
{
|
||||
if (!IPAddress.TryParse(ipAddress, out IPAddress ip))
|
||||
{
|
||||
throw new Exception("Invalid ipAddress given");
|
||||
}
|
||||
|
||||
CreateUdpClient(ip, port);
|
||||
}
|
||||
|
||||
private Task StartReceivingAsync()
|
||||
{
|
||||
return Task.Factory.StartNew(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
UdpReceiveResult result = await udpClient.ReceiveAsync();
|
||||
OnReceive?.Invoke(result.Buffer);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
Console.WriteLine($"SocketException: {ex.Message}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
OnDisconnect?.Invoke(udpClient.Client.LocalEndPoint);
|
||||
});
|
||||
}
|
||||
|
||||
public async Task SendTo(EndPoint endPoint, byte[] data)
|
||||
{
|
||||
if (endPoint is IPEndPoint ipEndPoint)
|
||||
{
|
||||
await udpClient.SendAsync(data, ipEndPoint).AsTask().ConfigureAwait(false);
|
||||
OnSend?.Invoke(endPoint, data);
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
udpClient.Close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using static SQLite.SQLite3;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace EonaCat.Dns.Core.Sockets.Udp
|
||||
{
|
||||
public class SocketUdpServer
|
||||
{
|
||||
public event Action<RemoteInfo> OnReceive;
|
||||
public event Action<RemoteInfo> OnSend;
|
||||
public event Action<RemoteInfo> OnDisconnect;
|
||||
|
||||
|
||||
private UdpClient udpClient;
|
||||
|
||||
public bool IsIP6 { get; private set; }
|
||||
|
||||
public SocketUdpServer(IPAddress ipAddress, int port)
|
||||
{
|
||||
CreateUdpServer(ipAddress, port);
|
||||
}
|
||||
|
||||
public SocketUdpServer(string ipAddress, int port)
|
||||
{
|
||||
if (!IPAddress.TryParse(ipAddress, out IPAddress ip))
|
||||
{
|
||||
throw new Exception("Invalid ipAddress given");
|
||||
}
|
||||
|
||||
CreateUdpServer(ip, port);
|
||||
}
|
||||
|
||||
|
||||
private static void SetConnectionReset(Socket socket)
|
||||
{
|
||||
if (socket.ProtocolType != ProtocolType.Udp && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable ICMP packet shutdown (forcibly closed)
|
||||
const int SioUdpConnReset = -1744830452;
|
||||
|
||||
byte[] inValue = { 0, 0, 0, 0 };
|
||||
socket.IOControl(SioUdpConnReset, inValue, null);
|
||||
}
|
||||
|
||||
private void CreateUdpServer(IPAddress ipAddress, int port)
|
||||
{
|
||||
IsIP6 = ipAddress.AddressFamily == AddressFamily.InterNetworkV6;
|
||||
udpClient = new UdpClient(ipAddress.AddressFamily);
|
||||
SetConnectionReset(udpClient.Client);
|
||||
|
||||
if (IsIP6)
|
||||
{
|
||||
udpClient.Client.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
|
||||
}
|
||||
|
||||
udpClient.Client.Bind(new IPEndPoint(ipAddress, port));
|
||||
}
|
||||
|
||||
public Task StartAsync()
|
||||
{
|
||||
return Task.Factory.StartNew(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
UdpReceiveResult result = await udpClient.ReceiveAsync().ConfigureAwait(false);
|
||||
OnReceive?.Invoke(new RemoteInfo()
|
||||
{
|
||||
EndPoint = result.RemoteEndPoint,
|
||||
IsIpv6 = result.RemoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6,
|
||||
Data = result.Buffer
|
||||
});
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
Console.WriteLine($"SocketException: {ex.Message}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task SendToAsync(EndPoint endPoint, byte[] data)
|
||||
{
|
||||
if (endPoint is IPEndPoint ipEndPoint)
|
||||
{
|
||||
await udpClient.SendAsync(data, ipEndPoint).AsTask().ConfigureAwait(false);
|
||||
OnSend?.Invoke(new RemoteInfo() { EndPoint = endPoint, Data = data, IsIpv6 = endPoint.AddressFamily == AddressFamily.InterNetworkV6 });
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendToAllAsync(byte[] data)
|
||||
{
|
||||
// get all connected clients
|
||||
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
|
||||
IPEndPoint[] endPoints = ipProperties.GetActiveUdpListeners();
|
||||
foreach (IPEndPoint endPoint in endPoints)
|
||||
{
|
||||
await udpClient.SendAsync(data, data.Length, endPoint).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue