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