diff --git a/EonaCat.Network/System/Sockets/Client.cs b/EonaCat.Network/System/Sockets/Client.cs deleted file mode 100644 index b45b8b3..0000000 --- a/EonaCat.Network/System/Sockets/Client.cs +++ /dev/null @@ -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 - { - /// - /// Unique client id. - /// - public string Guid { get; set; } - - /// - /// Client socket. - /// - public Socket Socket { get; set; } - - /// - /// Receive buffer. - /// - public byte[] Buffer { get; set; } - - /// - /// Buffer size - /// - public static int BufferSize { get; set; } = 1024 * 1024; // 1024KB - - /// - /// Socket connection state. - /// - public bool IsConnected => !((Socket.Poll(1000, SelectMode.SelectRead) && (Socket.Available == 0)) || !Socket.Connected); - /// - /// Client ip. - /// - public IPAddress RemoteIP => (Socket.RemoteEndPoint as IPEndPoint)?.Address; - /// - /// Client port. - /// - public int RemotePort => (Socket.RemoteEndPoint as IPEndPoint).Port; - - public event SendBytesTo SendData; - - public Client() - { - Buffer = new byte[BufferSize]; - } - - public void Dispose() - { - Socket.Close(); - } - - /// - /// Send data to the client. - /// - /// Data for sending - 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); - } - } -} \ No newline at end of file diff --git a/EonaCat.Network/System/Sockets/NetworkType.cs b/EonaCat.Network/System/Sockets/NetworkType.cs deleted file mode 100644 index 4da774e..0000000 --- a/EonaCat.Network/System/Sockets/NetworkType.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace EonaCat.Sockets -{ - [Flags] - public enum NetworkType - { - Tcp, - Udp - } -} diff --git a/EonaCat.Network/System/Sockets/RemoteInfo.cs b/EonaCat.Network/System/Sockets/RemoteInfo.cs new file mode 100644 index 0000000..21f3405 --- /dev/null +++ b/EonaCat.Network/System/Sockets/RemoteInfo.cs @@ -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; } + } +} diff --git a/EonaCat.Network/System/Sockets/SocketClient.cs b/EonaCat.Network/System/Sockets/SocketClient.cs deleted file mode 100644 index 8e05950..0000000 --- a/EonaCat.Network/System/Sockets/SocketClient.cs +++ /dev/null @@ -1,189 +0,0 @@ -using System; -using System.IO; -using System.Net; -using System.Net.Sockets; - -namespace EonaCat.Sockets -{ - public class SocketClient : Client, IDisposable - { - /// - /// Client type - /// - public NetworkType ClientType { get; set; } - - /// - /// Timer interval - /// - public int LoopInterval { get; set; } = 100; - - /// - /// Only for UDP. Sending 1 byte data for check connected state. - /// - public int UDPDataInterval { get; set; } = 5000; - - /// - /// Called when client connected - /// - public event EventHandler OnConnected; - /// - /// Called when client disconnected - /// - public event EventHandler OnDisconnected; - - /// - /// Called when received data from server - /// - public event EventHandler OnReceived; - - /// - /// Called when client has an error - /// - public event EventHandler OnError; - - private System.Timers.Timer UDPTimer { get; set; } - private System.Timers.Timer DisconnectTimer { get; set; } - - /// - /// SocketClient class constructor. - /// - /// Client type - 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(); - } - - /// - /// Connect to remote host - /// - /// Remote ip address - /// Remote port - public void Connect(IPAddress address, int port) => Connect(new IPEndPoint(address, port)); - - /// - /// Connect to remote host - /// - /// Remote end point - 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)); - } - } - - /// - /// Disconnect from remote host - /// - 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)); - } - } - } -} diff --git a/EonaCat.Network/System/Sockets/SocketClientEventArgs.cs b/EonaCat.Network/System/Sockets/SocketClientEventArgs.cs deleted file mode 100644 index f7729fb..0000000 --- a/EonaCat.Network/System/Sockets/SocketClientEventArgs.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace EonaCat.Sockets -{ - public class SocketClientEventArgs : EventArgs - { - public byte[] Data { get; set; } - } -} diff --git a/EonaCat.Network/System/Sockets/SocketServer.cs b/EonaCat.Network/System/Sockets/SocketServer.cs deleted file mode 100644 index 6077249..0000000 --- a/EonaCat.Network/System/Sockets/SocketServer.cs +++ /dev/null @@ -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 - { - /// - /// Unique server id. - /// - public string Guid { get; set; } - - /// - /// Listening ip address. - /// - public IPAddress IPAddress { get; set; } - - /// - /// Listening port. - /// - public int Port { get; set; } - - /// - /// List of connected clients. - /// - public List ConnectedClients { get; set; } = new List(); - - /// - /// Interval to check for disconnected clients - /// - public int DisconnectionCheckInterval { get; set; } = 50; - - /// - /// Determines if we need to reuse the socket address - /// (default: false) - /// - public bool ReuseAddress { get; set; } - - /// - /// Determines if the socket is in blocking mode - /// - public bool IsSocketBlockingDisabled { get; set; } - - - /// - /// Socket backlog. The maximum length of the pending connections queue. - /// - public int Backlog { get; set; } = 0; - /// - /// Server type. - /// - public NetworkType ServerType { get; set; } - - /// - /// Only for UDP. - /// (default: true) - /// - public bool UseClientManager { get; set; } = true; - - /// - /// Only for UDP. - /// Accept 1 byte data for connected state check. - /// - private int _UDPDataInterval { get => (int)(UDPDataInterval * 1.5); } - public int UDPDataInterval { get; set; } = 5000; - - /// - /// Called when new client connected - /// - public event EventHandler OnConnected; - /// - /// Called when client disconnected - /// - public event EventHandler OnDisconnected; - /// - /// Called when received data from client - /// - public event EventHandler OnReceived; - /// - /// Called when server catch error - /// - public event EventHandler OnError; - /// - /// Called when executing Start() function - /// - public event EventHandler OnStart; - /// - /// Called when executing Stop() function - /// - public event EventHandler OnStop; - - private Socket Server { get; set; } - private EonaCatTimer DisconnectTimerUdp { get; set; } - private EonaCatTimer DisconnectTimerTcp { get; set; } - private Dictionary 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); - } - - /// - /// Constructor - /// - /// Server type (TCP, UDP) - /// Server listening ip - /// Server listening port - 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 _removedEndpoints = new List(); - - 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(); - } - - /// - /// Start server - /// - 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(); - 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)); - } - } - - /// - /// Setup this udp socket to override the default one - /// (default: null => using default) - /// - public Socket CustomUdpSocket { get; set; } - - /// - /// Setup this tcp socket to override the default one - /// (default: null => using default) - /// - 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; - } - - /// - /// Stop server - /// - 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)); - } - } - } -} \ No newline at end of file diff --git a/EonaCat.Network/System/Sockets/SocketServerEventArgs.cs b/EonaCat.Network/System/Sockets/SocketServerEventArgs.cs deleted file mode 100644 index ac64e80..0000000 --- a/EonaCat.Network/System/Sockets/SocketServerEventArgs.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; - -namespace EonaCat.Sockets -{ - public class SocketServerDataEventArgs : EventArgs - { - /// - /// Client. - /// - public Client Client { get; set; } - - /// - /// Received data. - /// - public byte[] Data { get; set; } - } - - public class SocketServerClientEventArgs : EventArgs - { - /// - /// Client connection. - /// - public Client Client { get; set; } - } -} diff --git a/EonaCat.Network/System/Sockets/Tcp/SocketTcpClient.cs b/EonaCat.Network/System/Sockets/Tcp/SocketTcpClient.cs new file mode 100644 index 0000000..6aa4e09 --- /dev/null +++ b/EonaCat.Network/System/Sockets/Tcp/SocketTcpClient.cs @@ -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 OnConnect; + public event Action OnReceive; + public event Action OnDisconnect; + public event Action 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(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(data), SocketFlags.None).ConfigureAwait(false); + OnSend?.Invoke(new RemoteInfo { EndPoint = socket.RemoteEndPoint, Data = data, IsIpv6 = IsIP6 }); + } + + public void Disconnect() + { + socket.Close(); + } + } +} diff --git a/EonaCat.Network/System/Sockets/Tcp/SocketTcpServer.cs b/EonaCat.Network/System/Sockets/Tcp/SocketTcpServer.cs new file mode 100644 index 0000000..9616d1a --- /dev/null +++ b/EonaCat.Network/System/Sockets/Tcp/SocketTcpServer.cs @@ -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 OnConnect; + public event Action OnReceive; + public event Action OnSend; + public event Action 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(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(data), SocketFlags.None).ConfigureAwait(false); + OnSend?.Invoke(new RemoteInfo { Socket = socket, IsTcp = true, EndPoint = socket.RemoteEndPoint, IsIpv6 = socket.RemoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6, Data = data }); + } + } +} \ No newline at end of file diff --git a/EonaCat.Network/System/Sockets/Udp/SocketUdpClient.cs b/EonaCat.Network/System/Sockets/Udp/SocketUdpClient.cs new file mode 100644 index 0000000..3900ae1 --- /dev/null +++ b/EonaCat.Network/System/Sockets/Udp/SocketUdpClient.cs @@ -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 OnConnect; + public event Action OnReceive; + public event Action OnDisconnect; + public event Action 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(); + } + } +} diff --git a/EonaCat.Network/System/Sockets/Udp/SocketUdpServer.cs b/EonaCat.Network/System/Sockets/Udp/SocketUdpServer.cs new file mode 100644 index 0000000..7c5aeb0 --- /dev/null +++ b/EonaCat.Network/System/Sockets/Udp/SocketUdpServer.cs @@ -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 OnReceive; + public event Action OnSend; + public event Action 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); + } + } + } +}