This commit is contained in:
EonaCat 2023-07-12 20:44:27 +02:00
parent 604d9cbc6b
commit 04fd2329f1
6 changed files with 276 additions and 73 deletions

View File

@ -3,8 +3,7 @@
<LangVersion>Latest</LangVersion>
<TargetFrameworks>
netstandard2.1;
net5.0;
net6.0;
net6.0;
net7.0;
</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@ -17,9 +16,9 @@
<PackageTags>EonaCat, Network, .NET Standard, EonaCatHelpers, Jeroen, Saey, Protocol, Quic, UDP, TCP, Web, Server</PackageTags>
<PackageReleaseNotes></PackageReleaseNotes>
<Description>EonaCat Networking library with Quic, TCP, UDP and a Webserver</Description>
<Version>1.0.8</Version>
<AssemblyVersion>1.0.0.8</AssemblyVersion>
<FileVersion>1.0.0.8</FileVersion>
<Version>1.0.9</Version>
<AssemblyVersion>1.0.0.9</AssemblyVersion>
<FileVersion>1.0.0.9</FileVersion>
<PackageIcon>icon.png</PackageIcon>
</PropertyGroup>

View File

@ -18,10 +18,19 @@ namespace EonaCat.Network
internal static Logging Logger = new Logging();
private static QuicServer _quicServer;
/// <summary>
/// OnQuicClientConnected event
/// </summary>
public static event EventHandler<QuicConnectionEventArgs> OnQuicClientConnected;
/// <summary>
/// OnQuicStreamOpened event
/// </summary>
public static event EventHandler<QuicStreamEventArgs> OnQuicStreamOpened;
/// <summary>
/// OnQuicStreamDataReceived event
/// </summary>
public static event EventHandler<QuicStreamEventArgs> OnQuicStreamDataReceived;
/// <summary>
@ -60,6 +69,13 @@ namespace EonaCat.Network
OnQuicStreamDataReceived?.Invoke(null, new QuicStreamEventArgs { Stream = stream, Data = data });
}
/// <summary>
/// Start a Quic client
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
/// <param name="streamType"></param>
/// <returns></returns>
public static QuicStream QuicStartClient(string ip, int port = 11000, StreamType streamType = StreamType.ClientBidirectional)
{
QuicClient client = new QuicClient();

View File

@ -1,20 +1,45 @@
using System;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace EonaCat.Network
{
public class SocketTcpClient
{
private const int BUFFER_SIZE = 4096;
/// <summary>
/// OnConnect event
/// </summary>
public event Action<RemoteInfo> OnConnect;
/// <summary>
/// OnReceive event
/// </summary>
public event Action<RemoteInfo> OnReceive;
/// <summary>
/// OnDisconnect event
/// </summary>
public event Action<RemoteInfo> OnDisconnect;
public event Action<RemoteInfo> OnSend;
/// <summary>
/// OnError event
/// </summary>
public event Action<Exception, string> OnError;
private Socket socket;
/// <summary>
/// Create TCP client
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public Task ConnectAsync(string ipAddress, int port)
{
if (!IPAddress.TryParse(ipAddress, out IPAddress ip))
@ -27,13 +52,13 @@ namespace EonaCat.Network
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);
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 });
await StartReceivingAsync();
OnConnect?.Invoke(new RemoteInfo { IsTcp = true, IsIpv6 = IsIp6 });
_ = StartReceivingAsync();
}
catch (SocketException ex)
{
@ -42,11 +67,11 @@ namespace EonaCat.Network
}
}
public bool IsIP6 { get; private set; }
public bool IsIp6 { get; set; }
private async Task StartReceivingAsync()
{
byte[] buffer = new byte[1024];
byte[] buffer = new byte[BUFFER_SIZE]; // Increased buffer size for better performance
while (socket.Connected)
{
try
@ -54,13 +79,14 @@ namespace EonaCat.Network
int received = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None).ConfigureAwait(false);
if (received > 0)
{
byte[] data = buffer.AsSpan(0, received).ToArray();
byte[] data = new byte[received];
Buffer.BlockCopy(buffer, 0, data, 0, received);
OnReceive?.Invoke(new RemoteInfo
{
IsTcp = true,
Data = data,
EndPoint = socket.RemoteEndPoint,
IsIpv6 = IsIP6
IsIpv6 = socket.AddressFamily == AddressFamily.InterNetworkV6
});
}
else
@ -74,15 +100,22 @@ namespace EonaCat.Network
break;
}
}
OnDisconnect?.Invoke(new RemoteInfo { IsTcp = true, EndPoint = socket.RemoteEndPoint, IsIpv6 = IsIP6 });
OnDisconnect?.Invoke(new RemoteInfo { IsTcp = true, EndPoint = socket.RemoteEndPoint, IsIpv6 = socket.AddressFamily == AddressFamily.InterNetworkV6 });
}
public async Task SendAsync(byte[] data)
/// <summary>
/// Send data
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public 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 });
return socket.SendAsync(new ArraySegment<byte>(data), SocketFlags.None);
}
/// <summary>
/// Disconnect
/// </summary>
public void Disconnect()
{
socket.Close();

View File

@ -8,41 +8,79 @@ namespace EonaCat.Network
{
public class SocketTcpServer
{
private const int BUFFER_SIZE = 4096;
/// <summary>
/// OnConnect event
/// </summary>
public event Action<RemoteInfo> OnConnect;
/// <summary>
/// OnReceive event
/// </summary>
public event Action<RemoteInfo> OnReceive;
/// <summary>
/// OnSend event
/// </summary>
public event Action<RemoteInfo> OnSend;
/// <summary>
/// OnDisconnect event
/// </summary>
public event Action<RemoteInfo> OnDisconnect;
/// <summary>
/// OnError event
/// </summary>
public event Action<Exception, string> OnError;
private TcpListener listener;
private readonly TcpListener _listener;
private CancellationTokenSource _cancellationTokenSource;
private Task _acceptTask;
/// <summary>
/// Create TCP server
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
public SocketTcpServer(IPAddress ipAddress, int port)
{
listener = new TcpListener(ipAddress, port);
_listener = new TcpListener(ipAddress, port);
}
/// <summary>
/// Create TCP server
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
public SocketTcpServer(string ipAddress, int port)
{
IPAddress address = IPAddress.Parse(ipAddress);
listener = new TcpListener(address, port);
_listener = new TcpListener(address, port);
}
public async Task StartAsync(CancellationToken cancellationToken = default)
/// <summary>
/// Start TCP server
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task StartAsync(CancellationToken cancellationToken = default)
{
_listener.Start();
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
_acceptTask = AcceptConnectionsAsync(_cancellationTokenSource.Token);
return _acceptTask;
}
private async Task AcceptConnectionsAsync(CancellationToken cancellationToken)
{
listener.Start();
while (!cancellationToken.IsCancellationRequested)
{
try
{
Socket socket = await listener.AcceptSocketAsync().ConfigureAwait(false);
OnConnect?.Invoke(new RemoteInfo
{
Socket = socket,
IsTcp = true,
IsIpv6 = socket.RemoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6,
EndPoint = socket.RemoteEndPoint
});
await StartReceivingAsync(socket, cancellationToken).ConfigureAwait(false);
var socket = await _listener.AcceptSocketAsync().ConfigureAwait(false);
_ = HandleConnectionAsync(socket, cancellationToken);
}
catch (SocketException ex)
{
@ -51,24 +89,33 @@ namespace EonaCat.Network
}
}
private async Task StartReceivingAsync(Socket socket, CancellationToken cancellationToken)
private async Task HandleConnectionAsync(Socket socket, CancellationToken cancellationToken)
{
byte[] buffer = new byte[1024];
OnConnect?.Invoke(new RemoteInfo
{
Socket = socket,
IsTcp = true,
IsIpv6 = socket.AddressFamily == AddressFamily.InterNetworkV6,
EndPoint = socket.RemoteEndPoint
});
byte[] buffer = new byte[BUFFER_SIZE];
while (!cancellationToken.IsCancellationRequested)
{
try
{
int received = await socket.ReceiveAsync(new Memory<byte>(buffer), SocketFlags.None, cancellationToken)
.ConfigureAwait(false);
int received = await socket.ReceiveAsync(new Memory<byte>(buffer), SocketFlags.None, cancellationToken).ConfigureAwait(false);
if (received > 0)
{
byte[] data = new byte[received];
Buffer.BlockCopy(buffer, 0, data, 0, received);
OnReceive?.Invoke(new RemoteInfo
{
Socket = socket,
IsTcp = true,
IsIpv6 = socket.RemoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6,
IsIpv6 = socket.AddressFamily == AddressFamily.InterNetworkV6,
EndPoint = socket.RemoteEndPoint,
Data = new Memory<byte>(buffer, 0, received).ToArray()
Data = data
});
}
else
@ -82,12 +129,51 @@ namespace EonaCat.Network
break;
}
}
OnDisconnect?.Invoke(new RemoteInfo
{
Socket = socket,
IsTcp = true,
EndPoint = socket.RemoteEndPoint,
IsIpv6 = socket.AddressFamily == AddressFamily.InterNetworkV6
});
}
public async Task SendTo(Socket socket, byte[] data)
/// <summary>
/// Send data to socket
/// </summary>
/// <param name="socket"></param>
/// <param name="data"></param>
/// <returns></returns>
public async Task SendToAsync(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 });
OnSend?.Invoke(new RemoteInfo
{
Socket = socket,
IsTcp = true,
EndPoint = socket.RemoteEndPoint,
IsIpv6 = socket.AddressFamily == AddressFamily.InterNetworkV6,
Data = data
});
}
/// <summary>
/// Stop TCP server
/// </summary>
/// <returns></returns>
public async Task StopAsync()
{
_cancellationTokenSource.Cancel();
_listener.Stop();
if (_acceptTask != null)
{
try
{
await _acceptTask.ConfigureAwait(false);
}
catch (AggregateException) { }
}
}
}
}

View File

@ -8,14 +8,39 @@ namespace EonaCat.Network
{
public class SocketUdpClient
{
/// <summary>
/// OnConnect event
/// </summary>
public event Action<EndPoint> OnConnect;
/// <summary>
/// OnReceive event
/// </summary>
public event Action<byte[]> OnReceive;
/// <summary>
/// OnDisconnect event
/// </summary>
public event Action<EndPoint> OnDisconnect;
/// <summary>
/// OnSend event
/// </summary>
public event Action<EndPoint, byte[]> OnSend;
/// <summary>
/// OnError event
/// </summary>
public event Action<Exception, string> OnError;
private UdpClient udpClient;
private UdpClient _udpClient;
/// <summary>
/// Create UDP client
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <param name="cancellationToken"></param>
public SocketUdpClient(IPAddress ipAddress, int port, CancellationToken cancellationToken = default)
{
CreateUdpClient(ipAddress, port, cancellationToken);
@ -23,45 +48,36 @@ namespace EonaCat.Network
private void CreateUdpClient(IPAddress ipAddress, int port, CancellationToken cancellationToken = default)
{
IsIP6 = ipAddress.AddressFamily == AddressFamily.InterNetworkV6;
udpClient = new UdpClient(ipAddress.AddressFamily);
IsIp6 = ipAddress.AddressFamily == AddressFamily.InterNetworkV6;
_udpClient = new UdpClient(ipAddress.AddressFamily);
if (IsIP6)
if (IsIp6)
{
udpClient.Client.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
_udpClient.Client.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
}
udpClient.Client.Bind(new IPEndPoint(IsIP6 ? IPAddress.IPv6Any : IPAddress.Any, 0));
_udpClient.Client.Bind(new IPEndPoint(IsIp6 ? IPAddress.IPv6Any : IPAddress.Any, 0));
if (IsMulticastGroupEnabled)
{
udpClient.JoinMulticastGroup(ipAddress);
_udpClient.JoinMulticastGroup(ipAddress);
}
OnConnect?.Invoke(_udpClient.Client.RemoteEndPoint);
_ = StartReceivingAsync(cancellationToken);
}
public bool IsMulticastGroupEnabled { get; set; }
public bool IsIP6 { get; private 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 async Task StartReceivingAsync(CancellationToken cancellationToken = default)
private async Task StartReceivingAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
UdpReceiveResult result = await udpClient.ReceiveAsync().ConfigureAwait(false);
var result = await _udpClient.ReceiveAsync().ConfigureAwait(false);
OnReceive?.Invoke(result.Buffer);
}
catch (SocketException ex)
@ -70,21 +86,30 @@ namespace EonaCat.Network
break;
}
}
OnDisconnect?.Invoke(udpClient.Client.RemoteEndPoint);
OnDisconnect?.Invoke(_udpClient.Client.RemoteEndPoint);
}
/// <summary>
/// Send data to endPoint
/// </summary>
/// <param name="endPoint"></param>
/// <param name="data"></param>
/// <returns></returns>
public async Task SendTo(EndPoint endPoint, byte[] data)
{
if (endPoint is IPEndPoint ipEndPoint)
{
await udpClient.SendAsync(data, data.Length, ipEndPoint).ConfigureAwait(false);
await _udpClient.SendAsync(data, data.Length, ipEndPoint).ConfigureAwait(false);
OnSend?.Invoke(endPoint, data);
}
}
/// <summary>
/// Disconnect UDP client
/// </summary>
public void Disconnect()
{
udpClient.Close();
_udpClient.Close();
}
}
}

View File

@ -10,21 +10,49 @@ namespace EonaCat.Network
{
public class SocketUdpServer
{
/// <summary>
/// OnReceive event
/// </summary>
public event Action<RemoteInfo> OnReceive;
/// <summary>
/// OnSend event
/// </summary>
public event Action<RemoteInfo> OnSend;
/// <summary>
/// OnError event
/// </summary>
public event Action<Exception, string> OnError;
/// <summary>
/// OnDisconnect event
/// </summary>
public event Action<RemoteInfo> OnDisconnect;
private UdpClient udpClient;
public bool IsIP6 { get; private set; }
/// <summary>
/// Determines if the UDP server is IpV6
/// </summary>
public bool IsIp6 { get; private set; }
/// <summary>
/// Create UDP server
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
public SocketUdpServer(IPAddress ipAddress, int port)
{
CreateUdpServer(ipAddress, port);
}
/// <summary>
/// Create UDP server
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <exception cref="Exception"></exception>
public SocketUdpServer(string ipAddress, int port)
{
if (!IPAddress.TryParse(ipAddress, out IPAddress ip))
@ -35,7 +63,6 @@ namespace EonaCat.Network
CreateUdpServer(ip, port);
}
private static void SetConnectionReset(Socket socket)
{
if (socket.ProtocolType != ProtocolType.Udp && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@ -52,11 +79,11 @@ namespace EonaCat.Network
private void CreateUdpServer(IPAddress ipAddress, int port)
{
IsIP6 = ipAddress.AddressFamily == AddressFamily.InterNetworkV6;
IsIp6 = ipAddress.AddressFamily == AddressFamily.InterNetworkV6;
udpClient = new UdpClient(ipAddress.AddressFamily);
SetConnectionReset(udpClient.Client);
if (IsIP6)
if (IsIp6)
{
udpClient.Client.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
}
@ -64,13 +91,18 @@ namespace EonaCat.Network
udpClient.Client.Bind(new IPEndPoint(ipAddress, port));
}
/// <summary>
/// Start UDP server
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task StartAsync(CancellationToken cancellationToken = default)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
UdpReceiveResult result = await udpClient.ReceiveAsync().ConfigureAwait(false);
var result = await udpClient.ReceiveAsync().ConfigureAwait(false);
OnReceive?.Invoke(new RemoteInfo()
{
EndPoint = result.RemoteEndPoint,
@ -85,6 +117,12 @@ namespace EonaCat.Network
}
}
/// <summary>
/// Send data to endPoint
/// </summary>
/// <param name="endPoint"></param>
/// <param name="data"></param>
/// <returns></returns>
public async Task SendToAsync(EndPoint endPoint, byte[] data)
{
if (endPoint is IPEndPoint ipEndPoint)
@ -94,15 +132,21 @@ namespace EonaCat.Network
}
}
/// <summary>
/// Send data to all clients
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public async Task SendToAllAsync(byte[] data)
{
// get all connected clients
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] endPoints = ipProperties.GetActiveUdpListeners();
foreach (IPEndPoint endPoint in endPoints)
var ipProperties = IPGlobalProperties.GetIPGlobalProperties();
var endPoints = ipProperties.GetActiveUdpListeners();
foreach (var endPoint in endPoints)
{
await udpClient.SendAsync(data, data.Length, endPoint).ConfigureAwait(false);
}
}
}
}