diff --git a/EasyTcp3/EasyTcp3.Actions/ActionUtils/EasyTcpActionFilter.cs b/EasyTcp3/EasyTcp3.Actions/ActionUtils/EasyTcpActionFilter.cs index 9cab856..e68fbc9 100644 --- a/EasyTcp3/EasyTcp3.Actions/ActionUtils/EasyTcpActionFilter.cs +++ b/EasyTcp3/EasyTcp3.Actions/ActionUtils/EasyTcpActionFilter.cs @@ -1,9 +1,11 @@ +using System; + namespace EasyTcp3.Actions.ActionUtils { /// /// Filter attribute for EasyTcpActions /// - public interface IEasyTcpActionFilter + public abstract class EasyTcpActionFilter : Attribute { /// /// Determines whether client has access to an action @@ -12,6 +14,6 @@ public interface IEasyTcpActionFilter /// EasyTcpActionServer/EasyTcpActionClient as sender /// /// - public bool HasAccess(object sender, ActionMessage message); + public abstract bool HasAccess(object sender, ActionMessage message); } } \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3.Actions/ActionsCore/Action.cs b/EasyTcp3/EasyTcp3.Actions/ActionsCore/Action.cs index 001dab0..9033eac 100644 --- a/EasyTcp3/EasyTcp3.Actions/ActionsCore/Action.cs +++ b/EasyTcp3/EasyTcp3.Actions/ActionsCore/Action.cs @@ -30,7 +30,7 @@ public class Action /// /// List with EasyTcpAction filters /// - public List Filters; + public List Filters; /// /// Instance of EasyTcpActionDelegate* @@ -50,7 +50,7 @@ public Action(MethodInfo method, Dictionary classInstances) if (classInstance == null) EasyTcpAction = Delegate.CreateDelegate(methodType, method); EasyTcpAction = Delegate.CreateDelegate(methodType, classInstance, method); - var filters = method.GetCustomAttributes().OfType().ToList(); + var filters = method.GetCustomAttributes().OfType().ToList(); if (filters.Any()) Filters = filters; } diff --git a/EasyTcp3/EasyTcp3.Actions/EasyTcp3.Actions.csproj b/EasyTcp3/EasyTcp3.Actions/EasyTcp3.Actions.csproj index c1fd956..82ccdba 100644 --- a/EasyTcp3/EasyTcp3.Actions/EasyTcp3.Actions.csproj +++ b/EasyTcp3/EasyTcp3.Actions/EasyTcp3.Actions.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + netstandard2.0 true 2.4.0 EasyTcp.Actions diff --git a/EasyTcp3/EasyTcp3.Actions/EasyTcpActionClient.cs b/EasyTcp3/EasyTcp3.Actions/EasyTcpActionClient.cs index 8327a1c..ad48966 100644 --- a/EasyTcp3/EasyTcp3.Actions/EasyTcpActionClient.cs +++ b/EasyTcp3/EasyTcp3.Actions/EasyTcpActionClient.cs @@ -58,7 +58,7 @@ public EasyTcpActionClient(IEasyTcpProtocol protocol = null, Assembly assembly = string nameSpace = null) : base(protocol) { AddActions(assembly ?? Assembly.GetCallingAssembly(), nameSpace); - OnDataReceive += async (sender, message) => + OnDataReceiveAsync += async (sender, message) => { try { await Actions.ExecuteAction(Interceptor, FireOnUnknownAction, sender, message); } catch (Exception ex) { FireOnError(ex); } diff --git a/EasyTcp3/EasyTcp3.Actions/EasyTcpActionServer.cs b/EasyTcp3/EasyTcp3.Actions/EasyTcpActionServer.cs index b7c01a7..d1a2c97 100644 --- a/EasyTcp3/EasyTcp3.Actions/EasyTcpActionServer.cs +++ b/EasyTcp3/EasyTcp3.Actions/EasyTcpActionServer.cs @@ -59,7 +59,7 @@ public EasyTcpActionServer(IEasyTcpProtocol protocol = null, Assembly assembly = : base(protocol) { AddActions(assembly ?? Assembly.GetCallingAssembly(), nameSpace); - OnDataReceive += async (sender, message) => + OnDataReceiveAsync += async (sender, message) => { try { await Actions.ExecuteAction(Interceptor, FireOnUnknownAction, sender, message); } catch (Exception ex) { FireOnError(ex); } diff --git a/EasyTcp3/EasyTcp3.Encryption/EasyTcp3.Encryption.csproj b/EasyTcp3/EasyTcp3.Encryption/EasyTcp3.Encryption.csproj index fbacb41..f61efd0 100644 --- a/EasyTcp3/EasyTcp3.Encryption/EasyTcp3.Encryption.csproj +++ b/EasyTcp3/EasyTcp3.Encryption/EasyTcp3.Encryption.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + netstandard2.0 true 2.2.1 EasyTcp.Encryption @@ -17,7 +17,7 @@ - + diff --git a/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/EncryptedPrefixLengthProtocol.cs b/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/EncryptedPrefixLengthProtocol.cs index f2d4f07..554af90 100644 --- a/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/EncryptedPrefixLengthProtocol.cs +++ b/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/EncryptedPrefixLengthProtocol.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Net.Sockets; +using System.Threading.Tasks; using EasyEncrypt2; using EasyTcp3.Protocols; using EasyTcp3.Protocols.Tcp; @@ -84,11 +85,11 @@ public override byte[] CreateMessage(params byte[][] data) /// /// ignored /// - public override void DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) + public override async Task DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) { if (!(ReceivingLength = !ReceivingLength)) { - BufferSize = BitConverter.ToUInt16(client.Buffer, 0); + BufferSize = BitConverter.ToUInt16(data, 0); if (BufferSize == 0) client.Dispose(); } else @@ -96,7 +97,7 @@ public override void DataReceive(byte[] data, int receivedBytes, EasyTcpClient c BufferSize = 2; try { - client.DataReceiveHandler(new Message(client.Buffer, client).Decrypt(Encrypter)); + await client.DataReceiveHandler(new Message(data, client).Decrypt(Encrypter)); } catch { OnDecryptionError(client); } } diff --git a/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/DefaultSslProtocol.cs b/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/DefaultSslProtocol.cs index d229a1d..d1be203 100644 --- a/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/DefaultSslProtocol.cs +++ b/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/DefaultSslProtocol.cs @@ -3,6 +3,7 @@ using System.Net.Security; using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; using EasyTcp3.Protocols; using EasyTcp3.Server; @@ -13,6 +14,8 @@ namespace EasyTcp3.Encryption.Protocols.Tcp.Ssl /// public abstract class DefaultSslProtocol : IEasyTcpProtocol { + public byte[] ReceiveBuffer; + /// /// Instance of SslStream, /// null for base protocol of server @@ -80,8 +83,15 @@ public virtual Socket GetSocket(AddressFamily addressFamily) => /// public virtual void StartAcceptingClients(EasyTcpServer server) { - server.BaseSocket.Listen(5000); - server.BaseSocket.BeginAccept(OnConnectCallback, server); + if (server.AcceptArgs == null) + { + server.BaseSocket.Listen(50000); + server.AcceptArgs = new SocketAsyncEventArgs {UserToken = server}; + server.AcceptArgs.Completed += (_, ar) => OnConnectCallback(ar); + } + + server.AcceptArgs.AcceptSocket = null; + if (!server.BaseSocket.AcceptAsync(server.AcceptArgs)) OnConnectCallback(server.AcceptArgs); } /// @@ -92,8 +102,10 @@ public virtual void EnsureDataReceiverIsRunning(EasyTcpClient client) { if (IsListening) return; IsListening = true; - ((DefaultSslProtocol) client.Protocol).SslStream.BeginRead(client.Buffer = new byte[BufferSize], 0, - client.Buffer.Length, OnReceiveCallback, client); + + var protocol = (DefaultSslProtocol)client.Protocol; + ((DefaultSslProtocol) client.Protocol).SslStream.BeginRead(protocol.ReceiveBuffer = new byte[BufferSize], 0, + protocol.ReceiveBuffer.Length, OnReceiveCallback, client); } /// @@ -114,7 +126,7 @@ public virtual void SendMessage(EasyTcpClient client, byte[] message) if (client?.BaseSocket == null || !client.BaseSocket.Connected) throw new Exception("Could not send data: Client not connected or null"); - client.FireOnDataSend(new Message(message, client)); + client.FireOnDataSend(message, client); SslStream.BeginWrite(message, 0, message.Length, ar => { var stream = ar.AsyncState as SslStream; @@ -192,7 +204,7 @@ public virtual bool OnConnectServer(EasyTcpClient client) /// received data, has size of clients buffer /// amount of received bytes /// - public abstract void DataReceive(byte[] data, int receivedBytes, EasyTcpClient client); + public abstract Task DataReceive(byte[] data, int receivedBytes, EasyTcpClient client); /* * Internal methods @@ -231,29 +243,30 @@ protected virtual void HandleDisconnect(EasyTcpClient client) /// Fired when new client connects /// /// - protected virtual void OnConnectCallback(IAsyncResult ar) + protected virtual void OnConnectCallback(SocketAsyncEventArgs ar) { - var server = ar.AsyncState as EasyTcpServer; + var server = ar.UserToken as EasyTcpServer; if (server?.BaseSocket == null || !server.IsRunning) return; try { - var client = new EasyTcpClient(server.BaseSocket.EndAccept(ar), + var client = new EasyTcpClient(ar.AcceptSocket, (IEasyTcpProtocol) server.Protocol.Clone()) { Serialize = server.Serialize, Deserialize = server.Deserialize }; - client.OnDataReceive += (_, message) => server.FireOnDataReceive(message); + client.OnDataReceiveAsync += async (_, message) => await server.FireOnDataReceive(message); client.OnDataSend += (_, message) => server.FireOnDataSend(message); client.OnDisconnect += (_, c) => server.FireOnDisconnect(c); client.OnError += (_, exception) => server.FireOnError(exception); - server.BaseSocket.BeginAccept(OnConnectCallback, server); + + StartAcceptingClients(server); if (!client.Protocol.OnConnectServer(client)) return; server.FireOnConnect(client); - if (client.BaseSocket != null) //Check if user aborted OnConnect with Client.Dispose() - lock (server.UnsafeConnectedClients) + if (client.BaseSocket != null) // Check if user aborted OnConnect with Client.Dispose() + lock (server.UnsafeConnectedClients) server.UnsafeConnectedClients.Add(client); } catch (Exception ex) @@ -267,7 +280,7 @@ protected virtual void OnConnectCallback(IAsyncResult ar) /// Fired when new data is received /// /// - protected virtual void OnReceiveCallback(IAsyncResult ar) + protected virtual async void OnReceiveCallback(IAsyncResult ar) { var client = ar.AsyncState as EasyTcpClient; if (client == null) return; @@ -278,7 +291,9 @@ protected virtual void OnReceiveCallback(IAsyncResult ar) int receivedBytes = SslStream.EndRead(ar); if (receivedBytes != 0) { - DataReceive(client.Buffer, receivedBytes, client); + var protocol = (DefaultSslProtocol)client.Protocol; + await DataReceive(protocol.ReceiveBuffer, receivedBytes, client); + if (client.BaseSocket == null) HandleDisconnect(client); // Check if client is disposed by DataReceive else EnsureDataReceiverIsRunning(client); diff --git a/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/DelimiterSslProtocol.cs b/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/DelimiterSslProtocol.cs index 154424e..f99ba98 100644 --- a/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/DelimiterSslProtocol.cs +++ b/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/DelimiterSslProtocol.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Security.Cryptography.X509Certificates; using System.Text; +using System.Threading.Tasks; namespace EasyTcp3.Encryption.Protocols.Tcp.Ssl { @@ -157,7 +158,7 @@ public override object Clone() /// received data, has size of clients buffer /// amount of received bytes /// - public override void DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) + public override async Task DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) { byte receivedByte = data[0]; // Size of buffer is always 1 ReceivedBytes.Add(receivedByte); @@ -173,7 +174,7 @@ public override void DataReceive(byte[] data, int receivedBytes, EasyTcpClient c byte[] receivedData = AutoRemoveDelimiter ? ReceivedBytes.Take(receivedBytesLength).ToArray() // Remove delimiter from message : ReceivedBytes.ToArray(); - client.DataReceiveHandler(new Message(receivedData, client)); + await client.DataReceiveHandler(new Message(receivedData, client)); ReceivedBytes.Clear(); } } diff --git a/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/NoneSslProtocol.cs b/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/NoneSslProtocol.cs index 1ceebbd..234ccb2 100644 --- a/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/NoneSslProtocol.cs +++ b/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/NoneSslProtocol.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; namespace EasyTcp3.Encryption.Protocols.Tcp.Ssl { @@ -82,11 +83,11 @@ public override object Clone() /// received data, has size of clients buffer /// amount of received bytes /// - public override void DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) + public override async Task DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) { byte[] receivedData = new byte[receivedBytes]; Buffer.BlockCopy(data,0,receivedData,0,receivedBytes); - client.DataReceiveHandler(new Message(receivedData, client)); + await client.DataReceiveHandler(new Message(receivedData, client)); } } } \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/PrefixLengthSslProtocol.cs b/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/PrefixLengthSslProtocol.cs index 428696e..583cdd6 100644 --- a/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/PrefixLengthSslProtocol.cs +++ b/EasyTcp3/EasyTcp3.Encryption/Protocols/Tcp/Ssl/PrefixLengthSslProtocol.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; namespace EasyTcp3.Encryption.Protocols.Tcp.Ssl { @@ -83,17 +84,17 @@ public override object Clone() /// /// ignored /// - public override void DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) + public override async Task DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) { if (!(ReceivingLength = !ReceivingLength)) { - BufferSize = BitConverter.ToUInt16(client.Buffer, 0); + BufferSize = BitConverter.ToUInt16(data, 0); if (BufferSize == 0) client.Dispose(); } else { BufferSize = 2; - client.DataReceiveHandler(new Message(client.Buffer, client)); + await client.DataReceiveHandler(new Message(data, client)); } } } diff --git a/EasyTcp3/EasyTcp3.Examples/Actions/AuthorizationExample.cs b/EasyTcp3/EasyTcp3.Examples/Actions/AuthorizationExample.cs index cfa7911..f746ea2 100644 --- a/EasyTcp3/EasyTcp3.Examples/Actions/AuthorizationExample.cs +++ b/EasyTcp3/EasyTcp3.Examples/Actions/AuthorizationExample.cs @@ -62,7 +62,7 @@ public static void Connect() * example filter attribute for authorization */ [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - public class EasyTcpAuthorization : Attribute, IEasyTcpActionFilter + public class EasyTcpAuthorization : EasyTcpActionFilter { private readonly UserRole[] _allowedRoles; @@ -83,7 +83,7 @@ public class EasyTcpAuthorization : Attribute, IEasyTcpActionFilter /// /// /// - public bool HasAccess(object sender, ActionMessage message) + public override bool HasAccess(object sender, ActionMessage message) { var hasRole = message.Client.Session.TryGetValue("UserRole", out object userRole); if (!hasRole) return false; diff --git a/EasyTcp3/EasyTcp3.Examples/LargeArray/LargeArrayExample.cs b/EasyTcp3/EasyTcp3.Examples/LargeArray/LargeArrayExample.cs index ad62041..8e09cdf 100644 --- a/EasyTcp3/EasyTcp3.Examples/LargeArray/LargeArrayExample.cs +++ b/EasyTcp3/EasyTcp3.Examples/LargeArray/LargeArrayExample.cs @@ -34,7 +34,7 @@ public static void Connect() client.SendLargeArray(new byte[1000000]); // Send large array without length prefix - client.SendLargeArray(new byte[10000], false); + client.SendLargeArray(new byte[10000], sendLengthPrefix: false); Console.ReadLine(); } @@ -50,7 +50,7 @@ public async Task LargeArrayReceive(Message message) Console.WriteLine($"Received {largeArray.Length} bytes"); // Receive array with known length - var largeArray2 = await message.ReceiveLargeArrayAsync(10000); + var largeArray2 = await message.ReceiveLargeArrayAsync(count: 10000); Console.WriteLine($"Received {largeArray2.Length} bytes"); } } diff --git a/EasyTcp3/EasyTcp3.Examples/Program.cs b/EasyTcp3/EasyTcp3.Examples/Program.cs index 3f986c5..702ff0c 100644 --- a/EasyTcp3/EasyTcp3.Examples/Program.cs +++ b/EasyTcp3/EasyTcp3.Examples/Program.cs @@ -8,7 +8,7 @@ class Program static void Main() { // Run test methods here - MultiThreadedActionSpeedTest.Run(); + ThroughputTest.Run(); } } } \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3.Examples/SpeedTest/ActionSpeedTestClient.cs b/EasyTcp3/EasyTcp3.Examples/SpeedTest/ActionSpeedTestClient.cs deleted file mode 100644 index 25e0fb6..0000000 --- a/EasyTcp3/EasyTcp3.Examples/SpeedTest/ActionSpeedTestClient.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Diagnostics; -using System.Net; -using System.Text; -using System.Threading; -using EasyTcp3.Actions; -using EasyTcp3.Actions.ActionUtils; -using EasyTcp3.ClientUtils; -using EasyTcp3.Server; -using EasyTcp3.Server.ServerUtils; - -namespace EasyTcp3.Examples.SpeedTest -{ - /// - /// This class contains a basic speedtest of the SendActionAndGetReply method, - /// this includes the Send and Receive functions + the actions performance - /// - /// It uses the action echo server as test server - /// - public static class ActionSpeedTestClient - { - const int Port = 5_001; - const int MessageCount = 1000_000; - const string Message = "Message"; - - public static void RunSpeedTest() - { - using var server = new EasyTcpActionServer().Start(Port); - - var client = new EasyTcpClient(); - if (!client.Connect(IPAddress.Loopback, Port)) return; - - byte[] message = Encoding.UTF8.GetBytes(Message); - using var resetEvent = new ManualResetEventSlim(); - Stopwatch sw = new Stopwatch(); - sw.Start(); - - int counter = 0; - client.OnDataReceive += (o, message) => - { - if(Interlocked.Increment(ref counter) == MessageCount) resetEvent.Set(); - }; - - for (int x = 0; x < MessageCount; x++) client.SendAction("ECHO", message); - - resetEvent.Wait(); - Console.WriteLine($"Send {counter} action messages"); - Console.WriteLine($"ElapsedMilliseconds SpeedTest: {sw.ElapsedMilliseconds}"); - Console.WriteLine($"Average SpeedTest: {sw.ElapsedMilliseconds / (double) MessageCount}"); - Console.WriteLine($"Messages/Second: {MessageCount / sw.Elapsed.TotalSeconds}"); - } - } -} \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3.Examples/SpeedTest/MultiThreadedActionSpeedTest.cs b/EasyTcp3/EasyTcp3.Examples/SpeedTest/MultiThreadedActionSpeedTest.cs deleted file mode 100644 index 84c0b31..0000000 --- a/EasyTcp3/EasyTcp3.Examples/SpeedTest/MultiThreadedActionSpeedTest.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using EasyTcp3.Actions; -using EasyTcp3.Actions.ActionUtils; -using EasyTcp3.ClientUtils; -using EasyTcp3.Protocols; -using EasyTcp3.Protocols.Tcp; -using EasyTcp3.Server.ServerUtils; - -namespace EasyTcp3.Examples.SpeedTest -{ - public class MultiThreadedActionSpeedTest - { - const int ClientsCount = 20_000; // Max: ushort / 2 because of ip restrictions - const int MessageCount = 1_000_000; - private static readonly string MessageDataString = new string('H', 100); - const int Port = 50013; - private const int ThreadAmount = 8; - private static readonly IEasyTcpProtocol ServerProtocol = new PrefixLengthProtocol(); - private static readonly IEasyTcpProtocol ClientProtocol = new PrefixLengthProtocol(); - - public static void Run() - { - using var server = new EasyTcpActionServer(ServerProtocol).Start(Port); - - byte[] messageData = Encoding.UTF8.GetBytes(MessageDataString); - var clientList = new ConcurrentQueue(); - int counter = 0; - using var resetEvent = new ManualResetEventSlim(); - Stopwatch st = Stopwatch.StartNew(); - - Parallel.For(0, ClientsCount, new ParallelOptions {MaxDegreeOfParallelism = ThreadAmount}, i => - { - var client = new EasyTcpClient((IEasyTcpProtocol) ClientProtocol.Clone()); - client.OnDataReceive += (o, message) => - { - if(Interlocked.Increment(ref counter) == MessageCount) resetEvent.Set(); - }; - if (client.Connect(IPAddress.Any, Port)) clientList.Enqueue(client); - }); - Console.WriteLine($"Connected with {clientList.Count} clients"); - Console.WriteLine($"Total: {st.ElapsedMilliseconds}ms"); - Console.WriteLine($"Average milliseconds per client: {st.ElapsedMilliseconds / (double) clientList.Count}"); - Console.WriteLine($"Connections/Second: {clientList.Count / st.Elapsed.TotalSeconds}"); - - Console.WriteLine($"\n\nSending {MessageCount} messages"); - st.Restart(); - Parallel.For(0, MessageCount, new ParallelOptions {MaxDegreeOfParallelism = ThreadAmount}, i => - { - clientList.TryDequeue(out EasyTcpClient client); - client.SendAction("ECHO", messageData); - clientList.Enqueue(client); - }); - - resetEvent.Wait(); - Console.WriteLine($"Total: {st.ElapsedMilliseconds}ms"); - Console.WriteLine($"Average milliseconds per message: {st.ElapsedMilliseconds / (double) MessageCount}"); - Console.WriteLine($"Messages/Second: {MessageCount / st.Elapsed.TotalSeconds}"); - - foreach (var client in clientList) client.Dispose(); - Console.ReadLine(); - } - } -} \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3.Examples/SpeedTest/MultiThreadedSpeedTest.cs b/EasyTcp3/EasyTcp3.Examples/SpeedTest/MultiThreadedSpeedTest.cs deleted file mode 100644 index 533bd39..0000000 --- a/EasyTcp3/EasyTcp3.Examples/SpeedTest/MultiThreadedSpeedTest.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using EasyTcp3.ClientUtils; -using EasyTcp3.Protocols; -using EasyTcp3.Protocols.Tcp; -using EasyTcp3.Server; -using EasyTcp3.Server.ServerUtils; - -namespace EasyTcp3.Examples.SpeedTest -{ - public static class MultiThreadedSpeedTest - { - const int ClientsCount = 20_000; // Max: ushort / 2 because of ip restrictions - const int MessageCount = 1_000_000; - private static readonly string MessageDataString = new string('H', 100); - const int Port = 50013; - private const int ThreadAmount = 8; - private static readonly IEasyTcpProtocol ServerProtocol = new PrefixLengthProtocol(); - private static readonly IEasyTcpProtocol ClientProtocol = new PrefixLengthProtocol(); - - public static void Run() - { - using var server = new EasyTcpServer(ServerProtocol).Start(Port); - server.OnDataReceive += (o, message) => message.Client.Send(message); - - byte[] messageData = Encoding.UTF8.GetBytes(MessageDataString); - var clientList = new ConcurrentQueue(); - int counter = 0; - using var resetEvent = new ManualResetEventSlim(); - Stopwatch st = Stopwatch.StartNew(); - - Parallel.For(0, ClientsCount, new ParallelOptions {MaxDegreeOfParallelism = ThreadAmount}, i => - { - var client = new EasyTcpClient((IEasyTcpProtocol) ClientProtocol.Clone()); - client.OnDataReceive += (o, message) => - { - if(Interlocked.Increment(ref counter) == MessageCount) resetEvent.Set(); - }; - if (client.Connect(IPAddress.Any, Port)) clientList.Enqueue(client); - }); - Console.WriteLine($"Connected with {clientList.Count} clients"); - Console.WriteLine($"Total: {st.ElapsedMilliseconds}ms"); - Console.WriteLine($"Average milliseconds per client: {st.ElapsedMilliseconds / (double) clientList.Count}"); - Console.WriteLine($"Connections/Second: {clientList.Count / st.Elapsed.TotalSeconds}"); - - Console.WriteLine($"\n\nSending {MessageCount} messages"); - st.Restart(); - Parallel.For(0, MessageCount, new ParallelOptions {MaxDegreeOfParallelism = ThreadAmount}, i => - { - clientList.TryDequeue(out EasyTcpClient client); - client.Send(messageData); - clientList.Enqueue(client); - }); - - resetEvent.Wait(); - Console.WriteLine($"Total: {st.ElapsedMilliseconds}ms"); - Console.WriteLine($"Average milliseconds per message: {st.ElapsedMilliseconds / (double) MessageCount}"); - Console.WriteLine($"Messages/Second: {MessageCount / st.Elapsed.TotalSeconds}"); - - foreach (var client in clientList) client.Dispose(); - Console.ReadLine(); - } - } -} \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3.Examples/SpeedTest/SpeedTestClient.cs b/EasyTcp3/EasyTcp3.Examples/SpeedTest/SpeedTestClient.cs deleted file mode 100644 index 52d1a01..0000000 --- a/EasyTcp3/EasyTcp3.Examples/SpeedTest/SpeedTestClient.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Diagnostics; -using System.Net; -using System.Text; -using System.Threading; -using EasyTcp3.ClientUtils; -using EasyTcp3.Server; -using EasyTcp3.Server.ServerUtils; - -namespace EasyTcp3.Examples.SpeedTest -{ - /// - /// This class contains a basic speedtest of the SendAndGetReply method, - /// this includes the Send and Receive functions - /// - /// It uses the echo server as test server - /// - public static class SpeedTestClient - { - const int Port = 5_001; - const int MessageCount = 100_000; - const string Message = "Message"; - - public static void RunSpeedTest() - { - using var server = new EasyTcpServer().Start(Port); - server.OnDataReceive += (o, message) => message.Client.Send(message); - - var client = new EasyTcpClient(); - if (!client.Connect(IPAddress.Loopback, Port)) return; - - byte[] message = Encoding.UTF8.GetBytes(Message); - using var resetEvent = new ManualResetEventSlim(); - Stopwatch sw = new Stopwatch(); - sw.Start(); - - int counter = 0; - client.OnDataReceive += (o, message) => - { - if(Interlocked.Increment(ref counter) == MessageCount) resetEvent.Set(); - }; - - for (int x = 0; x < MessageCount; x++) client.Send(message); - - resetEvent.Wait(); - Console.WriteLine($"Send {counter} messages"); - Console.WriteLine($"ElapsedMilliseconds SpeedTest: {sw.ElapsedMilliseconds}"); - Console.WriteLine($"Average SpeedTest: {sw.ElapsedMilliseconds / (double) MessageCount}"); - Console.WriteLine($"Messages/Second: {MessageCount / sw.Elapsed.TotalSeconds}"); - } - } -} \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3.Examples/SpeedTest/ThroughputTest.cs b/EasyTcp3/EasyTcp3.Examples/SpeedTest/ThroughputTest.cs new file mode 100644 index 0000000..0c47ede --- /dev/null +++ b/EasyTcp3/EasyTcp3.Examples/SpeedTest/ThroughputTest.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using EasyTcp3.ClientUtils; +using EasyTcp3.Server; +using EasyTcp3.Server.ServerUtils; + +namespace EasyTcp3.Examples.SpeedTest +{ + public class ThroughputTest + { + const int ClientsCount = 100; // Max: ushort / 2 because of ip restrictions + private static readonly string MessageDataString = new string('H', 100); + const int Port = 1111; + private const int ThreadAmount = 16; + + public static void Run() + { + + + byte[] messageData = Encoding.UTF8.GetBytes(MessageDataString); + var clientList = new ConcurrentQueue(); + int counter = 0; + Stopwatch st = Stopwatch.StartNew(); + + Parallel.For(0, ClientsCount, new ParallelOptions {MaxDegreeOfParallelism = ThreadAmount}, i => + { + var client = new EasyTcpClient(); + client.OnConnect += (o, client) => client.Send(messageData); + client.OnDataReceive += (o,message) => + { + Interlocked.Increment(ref counter); + message.Client.Send(message); + }; + var socket = client.Protocol.GetSocket(AddressFamily.InterNetwork); + socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); + if (client.Connect(IPAddress.Any, Port, socket: socket)) clientList.Enqueue(client); + }); + + while (true) + { + Console.Clear(); + Console.WriteLine($"Total: {st.ElapsedMilliseconds}ms, {counter} messages"); + Console.WriteLine($"Average response time server in milliseconds: {st.ElapsedMilliseconds / (double) counter * ClientsCount}"); + Console.WriteLine($"Average milliseconds spend per message: {st.ElapsedMilliseconds / (double) counter}"); + Console.WriteLine($"Messages/Second: {counter / st.Elapsed.TotalSeconds}"); + Task.Delay(50).Wait(); + } + } + } +} \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3.Examples/Streams/StreamExample.cs b/EasyTcp3/EasyTcp3.Examples/Streams/StreamExample.cs index e9bba8e..9c01c2a 100644 --- a/EasyTcp3/EasyTcp3.Examples/Streams/StreamExample.cs +++ b/EasyTcp3/EasyTcp3.Examples/Streams/StreamExample.cs @@ -35,7 +35,7 @@ public static void Connect() // Send stream without length prefix var stream2 = new MemoryStream(new byte[10000]); - client.SendStream(stream2, false); + client.SendStream(stream2, sendLengthPrefix: false); Console.ReadLine(); // Writing / reading the base stream is also possible @@ -56,7 +56,7 @@ public async Task ReceiveStream(Message message) // Receive stream with known length await using var stream2 = new MemoryStream(); - await message.ReceiveStreamAsync(stream2, 10000); + await message.ReceiveStreamAsync(stream2, count: 10000); Console.WriteLine($"Received {stream2.Length} bytes"); } } diff --git a/EasyTcp3/EasyTcp3.Logging/EasyTcp3.Logging.csproj b/EasyTcp3/EasyTcp3.Logging/EasyTcp3.Logging.csproj index 7bae677..e21fdae 100644 --- a/EasyTcp3/EasyTcp3.Logging/EasyTcp3.Logging.csproj +++ b/EasyTcp3/EasyTcp3.Logging/EasyTcp3.Logging.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + netstandard2.0 true 1.1.0 EasyTcp.Logging @@ -14,6 +14,7 @@ EasyTcp.Logging true Socket Networking Tcp TcpClient TcpServer Server SimpleTcp EasyTcp AsyncTcp Logging + 8 diff --git a/EasyTcp3/EasyTcp3.Test/Actions/Filter/FilterTest.cs b/EasyTcp3/EasyTcp3.Test/Actions/Filter/FilterTest.cs index c279bdc..48c6a8f 100644 --- a/EasyTcp3/EasyTcp3.Test/Actions/Filter/FilterTest.cs +++ b/EasyTcp3/EasyTcp3.Test/Actions/Filter/FilterTest.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using EasyTcp3.Actions; using EasyTcp3.Actions.ActionUtils; @@ -39,9 +38,9 @@ public void TestFilters() public void Auth() => Interlocked.Increment(ref _counter); } - public class EasyTcpTestAuthorization : Attribute, IEasyTcpActionFilter + public class EasyTcpTestAuthorization : EasyTcpActionFilter { - public bool HasAccess(object sender, ActionMessage message) + public override bool HasAccess(object sender, ActionMessage message) { if (message.Client.Session.TryGetValue("role", out object role) && role as string == "user") return true; else return false; diff --git a/EasyTcp3/EasyTcp3.Test/EasyTcp/Client/Stream/StreamAsync.cs b/EasyTcp3/EasyTcp3.Test/EasyTcp/Client/Stream/StreamAsync.cs index 9158f2d..9ea487e 100644 --- a/EasyTcp3/EasyTcp3.Test/EasyTcp/Client/Stream/StreamAsync.cs +++ b/EasyTcp3/EasyTcp3.Test/EasyTcp/Client/Stream/StreamAsync.cs @@ -27,10 +27,10 @@ public async Task Stream1() //Client -> -(Stream)> Server (Client sends mess string testData = "123", data = ""; - server.OnDataReceive += (sender, message) => //Receive stream from client + server.OnDataReceiveAsync += async (sender, message) => //Receive stream from client { - using var stream = new MemoryStream(); - message.ReceiveStreamAsync(stream).Wait(); + await using var stream = new MemoryStream(); + await message.ReceiveStreamAsync(stream); data = Encoding.UTF8.GetString(stream.ToArray()); }; @@ -55,14 +55,14 @@ public void Stream2()//Client -> Server -(Stream)> Client (Client requests s using var client = new EasyTcpClient(); Assert.IsTrue(client.Connect(IPAddress.Any, port)); - server.OnDataReceive += (sender, message) => //Send stream if client requests + server.OnDataReceiveAsync += async (sender, message) => //Send stream if client requests { - using var dataStream = new MemoryStream(Encoding.UTF8.GetBytes(testData)); + await using var dataStream = new MemoryStream(Encoding.UTF8.GetBytes(testData)); message.Client.Send("Stream"); - message.Client.SendStreamAsync(dataStream).Wait(); + await message.Client.SendStreamAsync(dataStream); }; - client.OnDataReceive += async (sender, message) => //Receive stream from server + client.OnDataReceiveAsync += async (sender, message) => //Receive stream from server { await using var stream = new MemoryStream(); await message.ReceiveStreamAsync(stream); diff --git a/EasyTcp3/EasyTcp3.Test/EasyTcp/Client/Stream/StreamCompression.cs b/EasyTcp3/EasyTcp3.Test/EasyTcp/Client/Stream/StreamCompression.cs new file mode 100644 index 0000000..ff2b386 --- /dev/null +++ b/EasyTcp3/EasyTcp3.Test/EasyTcp/Client/Stream/StreamCompression.cs @@ -0,0 +1,40 @@ +using System.IO; +using System.Net; +using System.Text; +using EasyTcp3.ClientUtils; +using EasyTcp3.Server; +using EasyTcp3.Server.ServerUtils; +using NUnit.Framework; + +namespace EasyTcp3.Test.EasyTcp.Client.Stream +{ + public class StreamCompression + { + [Test] + public void Stream1() //Client -> -(Stream)> Server (Client sends message to server) + { + ushort port = TestHelper.GetPort(); + using var server = new EasyTcpServer().Start(port); + + using var client = new EasyTcpClient(); + Assert.IsTrue(client.Connect(IPAddress.Any, port)); + + string testData = "123", data = null; + + server.OnDataReceive += (sender, message) => //Receive stream from client + { + using var stream = new MemoryStream(); + message.ReceiveStream(stream, compression: true); + data = Encoding.UTF8.GetString(stream.ToArray()); + }; + + //Send stream to server + using var dataStream = new MemoryStream(Encoding.UTF8.GetBytes(testData)); + client.Send("Stream"); + client.SendStream(dataStream, compression: true); + + TestHelper.WaitWhileTrue(() => data == null); + Assert.AreEqual(testData, data); + } + } +} \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3.Test/Encryption/Ssl/StreamSsl.cs b/EasyTcp3/EasyTcp3.Test/Encryption/Ssl/StreamSsl.cs index 89daaa4..247976c 100644 --- a/EasyTcp3/EasyTcp3.Test/Encryption/Ssl/StreamSsl.cs +++ b/EasyTcp3/EasyTcp3.Test/Encryption/Ssl/StreamSsl.cs @@ -24,9 +24,9 @@ public void Stream() string testData = "123", data = null; - server.OnDataReceive += (sender, message) => //Receive stream from client + server.OnDataReceiveAsync += async (sender, message) => //Receive stream from client { - using var stream = new MemoryStream(); + await using var stream = new MemoryStream(); message.ReceiveStream(stream); data = Encoding.UTF8.GetString(stream.ToArray()); }; diff --git a/EasyTcp3/EasyTcp3/ClientUtils/Async/LargeArrayAsyncUtil.cs b/EasyTcp3/EasyTcp3/ClientUtils/Async/LargeArrayAsyncUtil.cs index 655d57e..283253b 100644 --- a/EasyTcp3/EasyTcp3/ClientUtils/Async/LargeArrayAsyncUtil.cs +++ b/EasyTcp3/EasyTcp3/ClientUtils/Async/LargeArrayAsyncUtil.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.IO.Compression; using System.Threading.Tasks; namespace EasyTcp3.ClientUtils.Async @@ -15,14 +16,17 @@ public static class ArrayAsyncUtil /// /// /// + /// /// determines whether prefix with length of the data is send - public static async Task SendLargeArrayAsync(this EasyTcpClient client, byte[] array, bool sendLengthPrefix = true) + public static async Task SendLargeArrayAsync(this EasyTcpClient client, byte[] array,bool compression = false, bool sendLengthPrefix = true) { if(client?.BaseSocket == null) throw new Exception("Client is not connected"); - await using var networkStream = client.Protocol.GetStream(client); - if(sendLengthPrefix) await networkStream.WriteAsync(BitConverter.GetBytes(array.Length)); - await networkStream.WriteAsync(array); + using var networkStream = client.Protocol.GetStream(client); + using var dataStream = compression ? new GZipStream(networkStream, CompressionMode.Compress) : networkStream; + + if(sendLengthPrefix) await dataStream.WriteAsync(BitConverter.GetBytes(array.Length),0, 4); + await dataStream.WriteAsync(array,0,array.Length); } /// @@ -30,28 +34,30 @@ public static async Task SendLargeArrayAsync(this EasyTcpClient client, byte[] a /// Use this method only when not listening for incoming messages (In the OnReceive event) /// /// + /// /// length of data, use prefix when 0 /// /// stream is not writable - public static async Task ReceiveLargeArrayAsync(this Message message, int count = 0, int bufferSize = 1024) + public static async Task ReceiveLargeArrayAsync(this Message message, bool compression = false, int count = 0, int bufferSize = 1024) { if(message?.Client?.BaseSocket == null) throw new Exception("Client is not connected"); - await using var networkStream = message.Client.Protocol.GetStream(message.Client); + using var networkStream = message.Client.Protocol.GetStream(message.Client); + using var dataStream = compression ? new GZipStream(networkStream, CompressionMode.Decompress) : networkStream; // Get length from stream if (count == 0) { var length = new byte[4]; - await networkStream.ReadAsync(length, 0, length.Length); - count = BitConverter.ToInt32(length); + await dataStream.ReadAsync(length, 0, length.Length); + count = BitConverter.ToInt32(length, 0); } var receivedArray = new byte[count]; int read, totalReceivedBytes = 0; while (totalReceivedBytes < count && - (read = await networkStream.ReadAsync(receivedArray, totalReceivedBytes, Math.Min(bufferSize, count - totalReceivedBytes))) > 0) + (read = await dataStream.ReadAsync(receivedArray, totalReceivedBytes, Math.Min(bufferSize, count - totalReceivedBytes))) > 0) totalReceivedBytes += read; return receivedArray; } diff --git a/EasyTcp3/EasyTcp3/ClientUtils/Async/ReceiveAsyncUtil.cs b/EasyTcp3/EasyTcp3/ClientUtils/Async/ReceiveAsyncUtil.cs index 42ee7c6..f42ba95 100644 --- a/EasyTcp3/EasyTcp3/ClientUtils/Async/ReceiveAsyncUtil.cs +++ b/EasyTcp3/EasyTcp3/ClientUtils/Async/ReceiveAsyncUtil.cs @@ -32,6 +32,7 @@ public static async Task ReceiveAsync(this EasyTcpClient client, TimeSp // Function is no longer used when signal is disposed, therefore ignore this warning // ReSharper disable once AccessToDisposedClosure signal.Release(); + return Task.CompletedTask; }; await signal.WaitAsync(timeout ?? TimeSpan.FromMilliseconds(DefaultTimeout)); diff --git a/EasyTcp3/EasyTcp3/ClientUtils/Async/StreamAsyncUtil.cs b/EasyTcp3/EasyTcp3/ClientUtils/Async/StreamAsyncUtil.cs index 85bd9e9..7499f7f 100644 --- a/EasyTcp3/EasyTcp3/ClientUtils/Async/StreamAsyncUtil.cs +++ b/EasyTcp3/EasyTcp3/ClientUtils/Async/StreamAsyncUtil.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.IO.Compression; using System.Threading.Tasks; namespace EasyTcp3.ClientUtils.Async @@ -15,23 +16,27 @@ public static class StreamAsyncUtil /// /// /// input stream + /// /// determines whether prefix with length of the data is send /// /// stream is not readable - public static async Task SendStreamAsync(this EasyTcpClient client, Stream stream, bool sendLengthPrefix = true, + public static async Task SendStreamAsync(this EasyTcpClient client, Stream stream, bool compression = false, + bool sendLengthPrefix = true, int bufferSize = 1024) { - if(client?.BaseSocket == null) throw new Exception("Client is not connected"); + if (client?.BaseSocket == null) throw new Exception("Client is not connected"); if (!stream.CanRead) throw new InvalidDataException("Stream is not readable"); - await using var networkStream = client.Protocol.GetStream(client); - if (sendLengthPrefix) await networkStream.WriteAsync(BitConverter.GetBytes(stream.Length)); + using var networkStream = client.Protocol.GetStream(client); + using var dataStream = compression ? new GZipStream(networkStream, CompressionMode.Decompress) : networkStream; + + if (sendLengthPrefix) await dataStream.WriteAsync(BitConverter.GetBytes(stream.Length), 0, 8); var buffer = new byte[bufferSize]; int read; while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0) - await networkStream.WriteAsync(buffer, 0, read); + await dataStream.WriteAsync(buffer, 0, read); } /// @@ -40,23 +45,26 @@ public static async Task SendStreamAsync(this EasyTcpClient client, Stream strea /// /// /// output stream for receiving data + /// /// length of data, use prefix when 0 /// /// stream is not writable - public static async Task ReceiveStreamAsync(this Message message, Stream stream, long count = 0, + public static async Task ReceiveStreamAsync(this Message message, Stream stream, bool compression = false, + long count = 0, int bufferSize = 1024) { - if(message?.Client?.BaseSocket == null) throw new Exception("Client is not connected"); + if (message?.Client?.BaseSocket == null) throw new Exception("Client is not connected"); if (!stream.CanWrite) throw new InvalidDataException("Stream is not writable"); - await using var networkStream = message.Client.Protocol.GetStream(message.Client); + using var networkStream = message.Client.Protocol.GetStream(message.Client); + using var dataStream = compression ? new GZipStream(networkStream, CompressionMode.Decompress) : networkStream; //Get length of stream if (count == 0) { var length = new byte[8]; - await networkStream.ReadAsync(length, 0, length.Length); - count = BitConverter.ToInt64(length); + await dataStream.ReadAsync(length, 0, length.Length); + count = BitConverter.ToInt64(length, 0); } var buffer = new byte[bufferSize]; @@ -64,7 +72,8 @@ public static async Task ReceiveStreamAsync(this Message message, Stream stream, int read; while (totalReceivedBytes < count && - (read = await networkStream.ReadAsync(buffer, 0, (int)Math.Min(bufferSize, count - totalReceivedBytes))) > 0) + (read = await dataStream.ReadAsync(buffer, 0, + (int) Math.Min(bufferSize, count - totalReceivedBytes))) > 0) { await stream.WriteAsync(buffer, 0, read); totalReceivedBytes += read; diff --git a/EasyTcp3/EasyTcp3/ClientUtils/LargeArrayUtil.cs b/EasyTcp3/EasyTcp3/ClientUtils/LargeArrayUtil.cs index 409d9ea..845376e 100644 --- a/EasyTcp3/EasyTcp3/ClientUtils/LargeArrayUtil.cs +++ b/EasyTcp3/EasyTcp3/ClientUtils/LargeArrayUtil.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.IO.Compression; namespace EasyTcp3.ClientUtils { @@ -14,14 +15,18 @@ public static class ArrayUtil /// /// /// + /// /// determines whether prefix with length of the data is send - public static void SendLargeArray(this EasyTcpClient client, byte[] array, bool sendLengthPrefix = true) + public static void SendLargeArray(this EasyTcpClient client, byte[] array, bool compression = false, + bool sendLengthPrefix = true) { - if(client?.BaseSocket == null) throw new Exception("Client is not connected"); - + if (client?.BaseSocket == null) throw new Exception("Client is not connected"); + using var networkStream = client.Protocol.GetStream(client); - if(sendLengthPrefix) networkStream.Write(BitConverter.GetBytes(array.Length)); - networkStream.Write(array); + using var dataStream = compression ? new GZipStream(networkStream, CompressionMode.Compress) : networkStream; + + if (sendLengthPrefix) dataStream.Write(BitConverter.GetBytes(array.Length), 0, 4); + dataStream.Write(array, 0, array.Length); } /// @@ -29,20 +34,24 @@ public static void SendLargeArray(this EasyTcpClient client, byte[] array, bool /// Use this method only when not listening for incoming messages (In the OnReceive event) /// /// + /// /// length of data, use prefix when 0 /// /// stream is not writable - public static byte[] ReceiveLargeArray(this Message message, int count = 0, int bufferSize = 1024) + public static byte[] ReceiveLargeArray(this Message message, bool compression = false, int count = 0, + int bufferSize = 1024) { - if(message?.Client?.BaseSocket == null) throw new Exception("Client is not connected"); - + if (message?.Client?.BaseSocket == null) throw new Exception("Client is not connected"); + using var networkStream = message.Client.Protocol.GetStream(message.Client); + using var dataStream = + compression ? new GZipStream(networkStream, CompressionMode.Decompress) : networkStream; // Get length from stream if (count == 0) { var length = new byte[4]; - networkStream.Read(length, 0, length.Length); + dataStream.Read(length, 0, length.Length); count = BitConverter.ToInt32(length, 0); } @@ -50,7 +59,7 @@ public static byte[] ReceiveLargeArray(this Message message, int count = 0, int int read, totalReceivedBytes = 0; while (totalReceivedBytes < count && - (read = networkStream.Read(receivedArray, totalReceivedBytes, + (read = dataStream.Read(receivedArray, totalReceivedBytes, Math.Min(bufferSize, count - totalReceivedBytes))) > 0) totalReceivedBytes += read; return receivedArray; diff --git a/EasyTcp3/EasyTcp3/ClientUtils/SendAndGetReplyUtil.cs b/EasyTcp3/EasyTcp3/ClientUtils/SendAndGetReplyUtil.cs index fb96177..bf6ca1f 100644 --- a/EasyTcp3/EasyTcp3/ClientUtils/SendAndGetReplyUtil.cs +++ b/EasyTcp3/EasyTcp3/ClientUtils/SendAndGetReplyUtil.cs @@ -1,6 +1,7 @@ using System; using System.Text; using System.Threading; +using System.Threading.Tasks; using EasyTcp3.EasyTcpPacketUtils; namespace EasyTcp3.ClientUtils @@ -34,6 +35,7 @@ public static Message SendAndGetReply(this EasyTcpClient client, TimeSpan? timeo // Function is no longer used when signal is disposed, therefore ignore this warning // ReSharper disable once AccessToDisposedClosure signal.Set(); + return Task.CompletedTask; }; client.Send(data); diff --git a/EasyTcp3/EasyTcp3/ClientUtils/StreamUtil.cs b/EasyTcp3/EasyTcp3/ClientUtils/StreamUtil.cs index 06b6a1c..70aae62 100644 --- a/EasyTcp3/EasyTcp3/ClientUtils/StreamUtil.cs +++ b/EasyTcp3/EasyTcp3/ClientUtils/StreamUtil.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.IO.Compression; namespace EasyTcp3.ClientUtils { @@ -15,22 +16,25 @@ public static class StreamUtil /// /// input stream /// determines whether prefix with length of the data is send + /// /// /// stream is not readable - public static void SendStream(this EasyTcpClient client, Stream stream, bool sendLengthPrefix = true, + public static void SendStream(this EasyTcpClient client, Stream stream, bool compression = false, bool sendLengthPrefix = true, int bufferSize = 1024) { if(client?.BaseSocket == null) throw new Exception("Client is not connected"); if (!stream.CanRead) throw new InvalidDataException("Stream is not readable"); using var networkStream = client.Protocol.GetStream(client); - if (sendLengthPrefix) networkStream.Write(BitConverter.GetBytes(stream.Length)); + using var dataStream = compression ? new GZipStream(networkStream, CompressionMode.Compress) : networkStream; + + if (sendLengthPrefix) dataStream.Write(BitConverter.GetBytes(stream.Length),0,8); var buffer = new byte[bufferSize]; int read; while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) - networkStream.Write(buffer, 0, read); + dataStream.Write(buffer, 0, read); } /// @@ -40,21 +44,23 @@ public static void SendStream(this EasyTcpClient client, Stream stream, bool sen /// /// output stream for receiving data /// length of data, use prefix when 0 + /// /// /// stream is not writable - public static void ReceiveStream(this Message message, Stream stream, long count = 0, int bufferSize = 1024) + public static void ReceiveStream(this Message message, Stream stream, bool compression = false, long count = 0, int bufferSize = 1024) { if(message?.Client?.BaseSocket == null) throw new Exception("Client is not connected"); if (!stream.CanWrite) throw new InvalidDataException("Stream is not writable"); using var networkStream = message.Client.Protocol.GetStream(message.Client); + using var dataStream = compression ? new GZipStream(networkStream, CompressionMode.Decompress) : networkStream; //Get length of stream if (count == 0) { var length = new byte[8]; - networkStream.Read(length, 0, length.Length); - count = BitConverter.ToInt64(length); + dataStream.Read(length, 0, length.Length); + count = BitConverter.ToInt64(length, 0); } var buffer = new byte[bufferSize]; @@ -62,7 +68,7 @@ public static void ReceiveStream(this Message message, Stream stream, long count int read; while (totalReceivedBytes < count && - (read = networkStream.Read(buffer, 0, (int)Math.Min(bufferSize, count - totalReceivedBytes))) > 0) + (read = dataStream.Read(buffer, 0, (int)Math.Min(bufferSize, count - totalReceivedBytes))) > 0) { stream.Write(buffer, 0, read); totalReceivedBytes += read; diff --git a/EasyTcp3/EasyTcp3/EasyTcp3.csproj b/EasyTcp3/EasyTcp3/EasyTcp3.csproj index af32d26..a73f918 100644 --- a/EasyTcp3/EasyTcp3/EasyTcp3.csproj +++ b/EasyTcp3/EasyTcp3/EasyTcp3.csproj @@ -1,7 +1,6 @@ - netstandard2.1 true 3.5.0 EasyTcp @@ -14,5 +13,7 @@ EasyTcp true Socket Networking Tcp TcpClient TcpServer Server SimpleTcp EasyTcp AsyncTcp + 8 + netstandard2.0;netstandard2.1 \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3/EasyTcpClient.cs b/EasyTcp3/EasyTcp3/EasyTcpClient.cs index c7762ee..a407b8a 100644 --- a/EasyTcp3/EasyTcp3/EasyTcpClient.cs +++ b/EasyTcp3/EasyTcp3/EasyTcpClient.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Net.Sockets; +using System.Threading.Tasks; using EasyTcp3.Protocols; using EasyTcp3.Protocols.Tcp; @@ -33,7 +34,6 @@ public IEasyTcpProtocol Protocol private IEasyTcpProtocol _protocol; - /// /// List with session variables /// @@ -57,11 +57,6 @@ public Dictionary Session public Func Deserialize = (b, t) => throw new Exception("Assign a function to deserialize first before using serialisation"); - /// - /// Buffer with received data - /// - public byte[] Buffer; - /// /// Event that is fired when client connected to remote host /// @@ -73,10 +68,20 @@ public Dictionary Session public event EventHandler OnDisconnect; /// - /// Event that is fired when client receives data from remote host + /// Async event that is fired when client receives data from remote host /// public event EventHandler OnDataReceive; + /// + /// Event that is fired when client receives data from remote host + /// + public event OnDataReceiveAsyncDelegate OnDataReceiveAsync; + + /// + /// Delegate type for OnDataReceiveAsync + /// + public delegate Task OnDataReceiveAsyncDelegate(object sender, Message message); + /// /// Event that is fired when client sends any data to remote host /// @@ -96,12 +101,13 @@ public Dictionary Session /// Fire the OnDisconnect event /// public void FireOnDisconnect() => OnDisconnect?.Invoke(this, this); - + /// /// Fire the OnDataSend event /// - /// - public void FireOnDataSend(Message message) => OnDataSend?.Invoke(this, message); + /// + /// + public void FireOnDataSend(byte[] data, EasyTcpClient client) => OnDataSend?.Invoke(this, new Message(data, client)); /// /// Fire the OnError event, @@ -118,12 +124,16 @@ public void FireOnError(Exception exception) /// Fire the OnDataReceive event /// /// received message - public void FireOnDataReceiveEvent(Message message) => OnDataReceive?.Invoke(this, message); + public async Task FireOnDataReceiveEvent(Message message) + { + if (OnDataReceiveAsync != null) await OnDataReceiveAsync.Invoke(this, message); + OnDataReceive?.Invoke(this, message); + } /// /// Execute custom action when receiving data /// - public Action DataReceiveHandler; + public Func DataReceiveHandler; /// /// Set DataReceiveHandler back to default behavior (calling OnDataReceive) diff --git a/EasyTcp3/EasyTcp3/EasyTcpPacketUtils/EasyTcpPacket.cs b/EasyTcp3/EasyTcp3/EasyTcpPacketUtils/EasyTcpPacket.cs new file mode 100644 index 0000000..a1207e0 --- /dev/null +++ b/EasyTcp3/EasyTcp3/EasyTcpPacketUtils/EasyTcpPacket.cs @@ -0,0 +1,17 @@ +namespace EasyTcp3.EasyTcpPacketUtils +{ + /// + /// + public static class EasyTcpPacket + { + /// + /// Create package from byte array + /// + /// data of new package + /// compress data using GZIP if set to true + /// package type + /// new package + public static T From(byte[] data, bool compression = false) where T : IEasyTcpPacket, new() + => compression ? new T {Data = data}.Compress() : new T {Data = data}; + } +} \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3/EasyTcpPacketUtils/IEasyTcpPacket.cs b/EasyTcp3/EasyTcp3/EasyTcpPacketUtils/IEasyTcpPacket.cs index 3efb867..19017d7 100644 --- a/EasyTcp3/EasyTcp3/EasyTcpPacketUtils/IEasyTcpPacket.cs +++ b/EasyTcp3/EasyTcp3/EasyTcpPacketUtils/IEasyTcpPacket.cs @@ -11,15 +11,5 @@ public interface IEasyTcpPacket /// set => create class from byte[] /// public byte[] Data { get; set; } - - /// - /// Create package from byte array - /// - /// data of new package - /// compress data using GZIP if set to true - /// package type - /// new package - public static T From(byte[] data, bool compression = false) where T : IEasyTcpPacket, new() - => compression ? new T {Data = data}.Compress() : new T {Data = data}; } } \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3/Message.cs b/EasyTcp3/EasyTcp3/Message.cs index c24efec..ea23fdf 100644 --- a/EasyTcp3/EasyTcp3/Message.cs +++ b/EasyTcp3/EasyTcp3/Message.cs @@ -85,49 +85,49 @@ public Message(byte[] data, EasyTcpClient client = null) /// Convert data to UShort /// /// data as UShort - public ushort ToUShort() => BitConverter.ToUInt16(Data); + public ushort ToUShort() => BitConverter.ToUInt16(Data, 0); /// /// Convert data to Short /// /// data as Short - public short ToShort() => BitConverter.ToInt16(Data); + public short ToShort() => BitConverter.ToInt16(Data, 0); /// /// Convert data to UInt /// /// data as UInt - public uint ToUInt() => BitConverter.ToUInt32(Data); + public uint ToUInt() => BitConverter.ToUInt32(Data, 0); /// /// Convert data to Int /// /// data as Int - public int ToInt() => BitConverter.ToInt16(Data); + public int ToInt() => BitConverter.ToInt16(Data, 0); /// /// Convert data to ULong /// /// data as ULong - public ulong ToULong() => BitConverter.ToUInt64(Data); + public ulong ToULong() => BitConverter.ToUInt64(Data, 0); /// /// Convert data to Long /// /// data as Long - public long ToLong() => BitConverter.ToInt64(Data); + public long ToLong() => BitConverter.ToInt64(Data, 0); /// /// Convert data to Double /// /// data as Double - public double ToDouble() => BitConverter.ToDouble(Data); + public double ToDouble() => BitConverter.ToDouble(Data, 0); /// /// Convert data to Bool /// /// data as Bool - public bool ToBool() => BitConverter.ToBoolean(Data); + public bool ToBool() => BitConverter.ToBoolean(Data, 0); /// /// Convert data to String @@ -147,7 +147,7 @@ public Message(byte[] data, EasyTcpClient client = null) /// /// Packet type /// data as custom IEasyTcpPacket - public T ToPacket() where T : IEasyTcpPacket, new() => IEasyTcpPacket.From(Data); + public T ToPacket() where T : IEasyTcpPacket, new() => EasyTcpPacket.From(Data); /// /// Deserialize object from byte[] diff --git a/EasyTcp3/EasyTcp3/Protocols/IEasyTcpProtocol.cs b/EasyTcp3/EasyTcp3/Protocols/IEasyTcpProtocol.cs index bf2a472..ac2a877 100644 --- a/EasyTcp3/EasyTcp3/Protocols/IEasyTcpProtocol.cs +++ b/EasyTcp3/EasyTcp3/Protocols/IEasyTcpProtocol.cs @@ -51,36 +51,24 @@ public interface IEasyTcpProtocol : ICloneable, IDisposable /// public void SendMessage(EasyTcpClient client, byte[] message); - /* - * Optional - */ - /// /// Get receiving/sending stream /// /// - public Stream GetStream(EasyTcpClient client) => new NetworkStream(client.BaseSocket); - + public Stream GetStream(EasyTcpClient client); + /// /// Method that is triggered when client connects /// Default behavior is starting listening for incoming data /// /// - public bool OnConnect(EasyTcpClient client) - { - EnsureDataReceiverIsRunning(client); - return true; - } + public bool OnConnect(EasyTcpClient client); /// /// Method that is triggered when client connects to server /// Default behavior is starting listening for incoming data /// /// - public bool OnConnectServer(EasyTcpClient client) - { - EnsureDataReceiverIsRunning(client); - return true; - } + public bool OnConnectServer(EasyTcpClient client); } } \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3/Protocols/Tcp/DefaultTcpProtocol.cs b/EasyTcp3/EasyTcp3/Protocols/Tcp/DefaultTcpProtocol.cs index 82a69bc..5f0be57 100644 --- a/EasyTcp3/EasyTcp3/Protocols/Tcp/DefaultTcpProtocol.cs +++ b/EasyTcp3/EasyTcp3/Protocols/Tcp/DefaultTcpProtocol.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Net.Sockets; +using System.Threading.Tasks; using EasyTcp3.Server; namespace EasyTcp3.Protocols.Tcp @@ -10,6 +11,11 @@ namespace EasyTcp3.Protocols.Tcp /// public abstract class DefaultTcpProtocol : IEasyTcpProtocol { + /// + /// AsyncEventArgs with received data + /// + public SocketAsyncEventArgs ReceiveBuffer; + /// /// Default socket for protocol /// @@ -24,8 +30,15 @@ public virtual Socket GetSocket(AddressFamily addressFamily) => /// public virtual void StartAcceptingClients(EasyTcpServer server) { - server.BaseSocket.Listen(50000); - server.BaseSocket.BeginAccept(OnConnectCallback, server); + if (server.AcceptArgs == null) + { + server.BaseSocket.Listen(50000); + server.AcceptArgs = new SocketAsyncEventArgs {UserToken = server}; + server.AcceptArgs.Completed += (_, ar) => OnConnectCallback(ar); + } + + server.AcceptArgs.AcceptSocket = null; + if (!server.BaseSocket.AcceptAsync(server.AcceptArgs)) OnConnectCallback(server.AcceptArgs); } /// @@ -34,10 +47,21 @@ public virtual void StartAcceptingClients(EasyTcpServer server) /// public virtual void EnsureDataReceiverIsRunning(EasyTcpClient client) { - if(IsListening) return; + if (IsListening) return; IsListening = true; - client.BaseSocket.BeginReceive(client.Buffer = new byte[BufferSize], 0, - client.Buffer.Length, SocketFlags.None, OnReceiveCallback, client); + + var protocol = (DefaultTcpProtocol) client.Protocol; + + if (protocol.ReceiveBuffer == null) + { + protocol.ReceiveBuffer = new SocketAsyncEventArgs {UserToken = client}; + protocol.ReceiveBuffer.Completed += (_, ar) => OnReceiveCallback(ar); + } + + var bufferSize = BufferSize; + protocol.ReceiveBuffer.SetBuffer(new byte[bufferSize], 0, bufferSize); + if (!client.BaseSocket.ReceiveAsync(protocol.ReceiveBuffer)) OnReceiveCallback(protocol.ReceiveBuffer); + } /// @@ -58,12 +82,36 @@ public virtual void SendMessage(EasyTcpClient client, byte[] message) if (client?.BaseSocket == null || !client.BaseSocket.Connected) throw new Exception("Could not send data: Client not connected or null"); - client.FireOnDataSend(new Message(message, client)); - client.BaseSocket.BeginSend(message, 0, message.Length, SocketFlags.None, ar => - { - var socket = ar.AsyncState as Socket; - socket?.EndSend(ar); - }, client.BaseSocket); + client.FireOnDataSend(message, client); + client.BaseSocket.Send(message, SocketFlags.None); + } + + /// + /// Get receiving/sending stream + /// + /// + public Stream GetStream(EasyTcpClient client) => new NetworkStream(client.BaseSocket); + + /// + /// Method that is triggered when client connects + /// Default behavior is starting listening for incoming data + /// + /// + public bool OnConnect(EasyTcpClient client) + { + EnsureDataReceiverIsRunning(client); + return true; + } + + /// + /// Method that is triggered when client connects to server + /// Default behavior is starting listening for incoming data + /// + /// + public bool OnConnectServer(EasyTcpClient client) + { + EnsureDataReceiverIsRunning(client); + return true; } /// @@ -78,6 +126,7 @@ public virtual void SendMessage(EasyTcpClient client, byte[] message) /// public virtual void Dispose() { + ReceiveBuffer?.Dispose(); } /* @@ -95,7 +144,7 @@ public virtual void Dispose() /// received data, has size of clients buffer /// amount of received bytes /// - public abstract void DataReceive(byte[] data, int receivedBytes, EasyTcpClient client); + public abstract Task DataReceive(byte[] data, int receivedBytes, EasyTcpClient client); /* * Internal methods @@ -121,24 +170,25 @@ protected virtual void HandleDisconnect(EasyTcpClient client) /// Fired when new client connects /// /// - protected virtual void OnConnectCallback(IAsyncResult ar) + protected virtual void OnConnectCallback(SocketAsyncEventArgs ar) { - var server = ar.AsyncState as EasyTcpServer; + var server = ar.UserToken as EasyTcpServer; if (server?.BaseSocket == null || !server.IsRunning) return; try { - var client = new EasyTcpClient(server.BaseSocket.EndAccept(ar), + var client = new EasyTcpClient(ar.AcceptSocket, (IEasyTcpProtocol) server.Protocol.Clone()) { Serialize = server.Serialize, Deserialize = server.Deserialize }; - client.OnDataReceive += (_, message) => server.FireOnDataReceive(message); + client.OnDataReceiveAsync += async (_, message) => await server.FireOnDataReceive(message); client.OnDataSend += (_, message) => server.FireOnDataSend(message); client.OnDisconnect += (_, c) => server.FireOnDisconnect(c); client.OnError += (_, exception) => server.FireOnError(exception); - server.BaseSocket.BeginAccept(OnConnectCallback, server); + + StartAcceptingClients(server); if (!client.Protocol.OnConnectServer(client)) return; server.FireOnConnect(client); @@ -157,20 +207,18 @@ protected virtual void OnConnectCallback(IAsyncResult ar) /// Fired when new data is received /// /// - protected virtual void OnReceiveCallback(IAsyncResult ar) + protected virtual async void OnReceiveCallback(SocketAsyncEventArgs ar) { - var client = ar.AsyncState as EasyTcpClient; + var client = ar.UserToken as EasyTcpClient; if (client == null) return; IsListening = false; try { - int receivedBytes = client.BaseSocket.EndReceive(ar, out SocketError socketErr); - if (receivedBytes != 0 || socketErr != SocketError.Success) + if (ar.BytesTransferred != 0) { - DataReceive(client.Buffer, receivedBytes, client); - if (client.BaseSocket == null) - HandleDisconnect(client); // Check if client is disposed by DataReceive + await DataReceive(ar.Buffer, ar.BytesTransferred, client); + if (client.BaseSocket == null) HandleDisconnect(client); // Check if client is disposed by DataReceive else EnsureDataReceiverIsRunning(client); } else HandleDisconnect(client); diff --git a/EasyTcp3/EasyTcp3/Protocols/Tcp/DelimiterProtocol.cs b/EasyTcp3/EasyTcp3/Protocols/Tcp/DelimiterProtocol.cs index fd5d4ec..c07eb1e 100644 --- a/EasyTcp3/EasyTcp3/Protocols/Tcp/DelimiterProtocol.cs +++ b/EasyTcp3/EasyTcp3/Protocols/Tcp/DelimiterProtocol.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading.Tasks; namespace EasyTcp3.Protocols.Tcp { @@ -103,7 +104,7 @@ public override byte[] CreateMessage(params byte[][] data) /// received data, has size of clients buffer /// amount of received bytes /// - public override void DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) + public override async Task DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) { byte receivedByte = data[0]; // Size of buffer is always 1 ReceivedBytes.Add(receivedByte); @@ -119,7 +120,7 @@ public override void DataReceive(byte[] data, int receivedBytes, EasyTcpClient c byte[] receivedData = AutoRemoveDelimiter ? ReceivedBytes.Take(receivedBytesLength).ToArray() // Remove delimiter from message : ReceivedBytes.ToArray(); - client.DataReceiveHandler(new Message(receivedData, client)); + await client.DataReceiveHandler(new Message(receivedData, client)); ReceivedBytes.Clear(); } } diff --git a/EasyTcp3/EasyTcp3/Protocols/Tcp/NoneProtocol.cs b/EasyTcp3/EasyTcp3/Protocols/Tcp/NoneProtocol.cs index 2c82a98..dd5bcc5 100644 --- a/EasyTcp3/EasyTcp3/Protocols/Tcp/NoneProtocol.cs +++ b/EasyTcp3/EasyTcp3/Protocols/Tcp/NoneProtocol.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; namespace EasyTcp3.Protocols.Tcp { @@ -64,11 +65,11 @@ public override byte[] CreateMessage(params byte[][] data) /// received data, has size of clients buffer /// amount of received bytes /// - public override void DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) + public override async Task DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) { byte[] receivedData = new byte[receivedBytes]; Buffer.BlockCopy(data,0,receivedData,0,receivedBytes); - client.DataReceiveHandler(new Message(receivedData, client)); + await client.DataReceiveHandler(new Message(receivedData, client)); } } } \ No newline at end of file diff --git a/EasyTcp3/EasyTcp3/Protocols/Tcp/PrefixLengthProtocol.cs b/EasyTcp3/EasyTcp3/Protocols/Tcp/PrefixLengthProtocol.cs index b89548d..a0860fa 100644 --- a/EasyTcp3/EasyTcp3/Protocols/Tcp/PrefixLengthProtocol.cs +++ b/EasyTcp3/EasyTcp3/Protocols/Tcp/PrefixLengthProtocol.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; namespace EasyTcp3.Protocols.Tcp { @@ -66,17 +67,17 @@ public override byte[] CreateMessage(params byte[][] data) /// /// ignored /// - public override void DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) + public override async Task DataReceive(byte[] data, int receivedBytes, EasyTcpClient client) { if (!(ReceivingLength = !ReceivingLength)) { - BufferSize = BitConverter.ToUInt16(client.Buffer, 0); + BufferSize = BitConverter.ToUInt16(data, 0); if (BufferSize == 0) client.Dispose(); } else { BufferSize = 2; - client.DataReceiveHandler(new Message(client.Buffer, client)); + await client.DataReceiveHandler(new Message(data, client)); } } } diff --git a/EasyTcp3/EasyTcp3/Server/EasyTcpServer.cs b/EasyTcp3/EasyTcp3/Server/EasyTcpServer.cs index 2759015..a34f73e 100644 --- a/EasyTcp3/EasyTcp3/Server/EasyTcpServer.cs +++ b/EasyTcp3/EasyTcp3/Server/EasyTcpServer.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Net.Sockets; +using System.Threading.Tasks; using EasyTcp3.Protocols; using EasyTcp3.Protocols.Tcp; @@ -17,6 +18,11 @@ public class EasyTcpServer : IDisposable /// BaseSocket of server /// public Socket BaseSocket { get; protected internal set; } + + /// + /// AsyncEventArgs used to accept new sockets + /// + public SocketAsyncEventArgs AcceptArgs; /// /// Protocol for server, protocol determines all behavior of this server @@ -89,6 +95,16 @@ public IEasyTcpProtocol Protocol /// Event that is fired when server receives data /// public event EventHandler OnDataReceive; + + /// + /// Event that is fired when client receives data from remote host + /// + public event OnDataReceiveAsyncDelegate OnDataReceiveAsync; + + /// + /// Delegate type for OnDataReceiveAsync + /// + public delegate Task OnDataReceiveAsyncDelegate(object sender, Message message); /// /// Event that is fired when server sends data to a client @@ -120,8 +136,12 @@ public void FireOnDisconnect(EasyTcpClient client) /// Fire the OnDataReceive event /// /// - public void FireOnDataReceive(Message message) => OnDataReceive?.Invoke(this, message); - + public async Task FireOnDataReceive(Message message) + { + if (OnDataReceiveAsync != null) await OnDataReceiveAsync.Invoke(this, message); + OnDataReceive?.Invoke(this, message); + } + /// /// Fire the OnDataSend event /// @@ -157,6 +177,7 @@ public void Dispose() } UnsafeConnectedClients.Clear(); + AcceptArgs?.Dispose(); Protocol?.Dispose(); BaseSocket.Dispose(); BaseSocket = null; diff --git a/README.md b/README.md index 9a41f2b..510323a 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,22 @@


-# EasyTcp +# Features +- Simple and Easy to use +- High performance (~430.000 round trips/second)* +- Very lightweight (use only what you need) +- Async support +- Actions wich make creating API's realy simple (See EasyTcp.Actions) +- Support for sending raw streams/very large arrays +- Serialistaion and compression +- Different kinds of framing +- MIT license +- Unit tested (~250 unit tests) + +\* Tested on local machine(linux, ryzen 7) with clients and server running under the same process. See EasyTcp.Examples/SpeedTest/ThroughputTest.cs + +# Packages +## EasyTcp EasyTcp3 is a package that makes creating tcp servers and clients simple.
It is very fast, simple, supports framing, serialisation, disconnect detection, event handlers and more.
See the [EasyTcp.Examples](https://github.com/Job79/EasyTcp/tree/master/EasyTcp3/EasyTcp3.Examples) folder for documentation. @@ -30,7 +45,7 @@ if(!client.Connect("127.0.0.1", PORT)) return; client.Send("Hello server"); ``` -# EasyTcp.Actions +## EasyTcp.Actions EasyTcp.Actions adds support to EasyTcp for triggering functions based on received data.
It does this without giving up (noticeable) performance, and makes creating big servers/clients easy.
See the [EasyTcp.Examples](https://github.com/Job79/EasyTcp/tree/master/EasyTcp3/EasyTcp3.Examples) folder for documentation. @@ -64,7 +79,7 @@ client.SendAction("ECHO","Hello me"); // Trigger the ECHO action server side client.SendAction("BROADCAST","Hello everyone"); // Trigger the BROADCAST action server side ``` -# EasyTcp.Encryption +## EasyTcp.Encryption EasyTcp.Encryption adds ssl adn encryption support to EasyTcp.
See the [EasyTcp.Examples](https://github.com/Job79/EasyTcp/tree/master/EasyTcp3/EasyTcp3.Examples) folder for documentation. ```cs @@ -91,6 +106,18 @@ using var client = new EasyTcpClient().UseEncryption(encrypter); if(!client.Connect("127.0.0.1", PORT)) return; client.Send("Hello encrypted server!"); // All data is automatically encrypted ``` + +## EasyTcp.Logging +EasyTcp.Logging adds support for logging of incoming/outgoing messages/connections and errors. +```cs +using var server = new EasyTcpServer().UseServerLogging(Console.WriteLine).Start(Port); + +using var client = new EasyTcpClient().UseClientLogging(Console.WriteLine); +if(!client.Connect("127.0.0.1", Port)) return; +client.Send("Hello server!"); +Console.ReadLine(); +``` + # Contribution / Help / Questions / Feedback Create a issue, pull request or send an email to jobse@pm.me