This commit is contained in:
EonaCat 2023-03-25 16:08:18 +01:00
parent fb824136d9
commit 4767d7604c
11 changed files with 392 additions and 862 deletions

View File

@ -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);
}
}
}

View File

@ -1,11 +0,0 @@
using System;
namespace EonaCat.Sockets
{
[Flags]
public enum NetworkType
{
Tcp,
Udp
}
}

View File

@ -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; }
}
}

View File

@ -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));
}
}
}
}

View File

@ -1,9 +0,0 @@
using System;
namespace EonaCat.Sockets
{
public class SocketClientEventArgs : EventArgs
{
public byte[] Data { get; set; }
}
}

View File

@ -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));
}
}
}
}

View File

@ -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; }
}
}

View File

@ -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();
}
}
}

View File

@ -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 });
}
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}
}
}