NetSharp

NetSharp.git
git clone git://git.lenczewski.org/NetSharp.git
Log | Files | Refs | README | LICENSE

commit d652bdc062aefec5267d47284a4db0b651c36fc9
parent b59b0e98dc9f339088acb5fc966143be8a2d9ce1
Author: Mikolaj Lenczewski <mikolaj.lenczewski308@gmail.com>
Date:   Fri, 10 Apr 2020 12:08:54 +0100

Made udp server and clients asynchronous, using SocketAsyncEventArgs. Added lightweight ObjectPool implementation

Diffstat:
MNetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs | 60++++++------------------------------------------------------
MNetSharp/NetSharp/Sockets/SocketClient.cs | 33+++++++++++++++++++++------------
ANetSharp/NetSharp/Utils/MyObjectPool.cs | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
MNetSharp/NetSharpExamples/Program.cs | 16++++++++--------
4 files changed, 90 insertions(+), 74 deletions(-)

diff --git a/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs b/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs @@ -21,7 +21,7 @@ namespace NetSharp.Sockets.Datagram private readonly ConcurrentDictionary<EndPoint, RemoteDatagramClientToken> connectedClientTokens; - private readonly ObjectPool<SocketAsyncEventArgs> ArgsPool; + private readonly MyObjectPool<SocketAsyncEventArgs> ArgsPool; private readonly struct RemoteDatagramClientToken { @@ -70,7 +70,7 @@ namespace NetSharp.Sockets.Datagram return true; } - ArgsPool = new ObjectPool<SocketAsyncEventArgs>(CreateArgs, ResetArgs, DestroyArgs, ReBufferArgsPredicate); + ArgsPool = new MyObjectPool<SocketAsyncEventArgs>(CreateArgs, ResetArgs, DestroyArgs, ReBufferArgsPredicate); } protected override SocketAsyncEventArgs GenerateConnectionArgs(EndPoint remoteEndPoint) @@ -229,6 +229,8 @@ namespace NetSharp.Sockets.Datagram break; case SocketAsyncOperation.ReceiveFrom: + DoReceiveFrom(AnyRemoteEndPoint); // start a new receive from operation immediately, to not drop any packets + CompleteReceiveFrom(args); break; @@ -281,14 +283,14 @@ namespace NetSharp.Sockets.Datagram if (!completesAsync) { + DoReceiveFrom(AnyRemoteEndPoint); // start a new receive from operation immediately, to not drop any packets + CompleteReceiveFrom(args); } } private void CompleteReceiveFrom(SocketAsyncEventArgs receiveArgs) { - DoReceiveFrom(AnyRemoteEndPoint); // start a new receive from operation immediately, to not drop any packets - SocketOperationToken receiveToken = (SocketOperationToken)receiveArgs.UserToken; TransmissionResult receiveResult = new TransmissionResult(receiveArgs); @@ -535,54 +537,4 @@ namespace NetSharp.Sockets.Datagram */ } } - - internal class ObjectPool<T> where T : class - { - internal delegate T CreateObjectDelegate(); - - internal delegate bool KeepObjectPredicate(in T instance); - - internal delegate void ResetObjectDelegate(T instance); - - internal delegate void DestroyObjectDelegate(T instance); - - private readonly CreateObjectDelegate createObjectDelegate; - private readonly KeepObjectPredicate rebufferObjectPredicate; - private readonly ResetObjectDelegate resetObjectDelegate; - private readonly DestroyObjectDelegate destroyObjectDelegate; - - private readonly ConcurrentBag<T> objectBuffer; - - public ObjectPool(in CreateObjectDelegate createDelegate, in ResetObjectDelegate resetDelegate, in DestroyObjectDelegate destroyDelegate, in KeepObjectPredicate keepObjectPredicate) - { - createObjectDelegate = createDelegate; - - resetObjectDelegate = resetDelegate; - - destroyObjectDelegate = destroyDelegate; - - rebufferObjectPredicate = keepObjectPredicate; - - objectBuffer = new ConcurrentBag<T>(); - } - - public T Rent() - { - return objectBuffer.TryTake(out T result) ? result : createObjectDelegate(); - } - - public void Return(T instance) - { - if (rebufferObjectPredicate(instance)) - { - resetObjectDelegate(instance); - - objectBuffer.Add(instance); - } - else - { - destroyObjectDelegate(instance); - } - } - } } \ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/SocketClient.cs b/NetSharp/NetSharp/Sockets/SocketClient.cs @@ -4,6 +4,8 @@ using System; using System.Buffers; using System.Net; using System.Net.Sockets; +using System.Threading.Tasks; +using NetSharp.Utils; namespace NetSharp.Sockets { @@ -11,38 +13,45 @@ namespace NetSharp.Sockets { protected readonly ArrayPool<byte> BufferPool; + protected readonly SocketAsyncEventArgs Args; + protected SocketClient(in AddressFamily connectionAddressFamily, in SocketType connectionSocketType, in ProtocolType connectionProtocolType) : base(in connectionAddressFamily, in connectionSocketType, in connectionProtocolType) { BufferPool = ArrayPool<byte>.Create(NetworkPacket.TotalSize, 10); + + Args = new SocketAsyncEventArgs(); + Args.Completed += SocketAsyncOperations.HandleIoCompleted; } - public int SendBytes(Memory<byte> outgoingDataBuffer) + public int SendBytes(Memory<byte> outgoingDataBuffer, SocketFlags flags = SocketFlags.None) { - return connection.Send(outgoingDataBuffer.Span); + byte[] temporaryBuffer = BufferPool.Rent(NetworkPacket.TotalSize); + outgoingDataBuffer.CopyTo(temporaryBuffer); + int sentBytes = connection.Send(temporaryBuffer); + BufferPool.Return(temporaryBuffer); + + return sentBytes; } - public int SendBytesTo(Memory<byte> outgoingDataBuffer, EndPoint remoteEndPoint) + public ValueTask<TransmissionResult> SendBytesTo(Memory<byte> outgoingDataBuffer, EndPoint remoteEndPoint, SocketFlags flags = SocketFlags.None) { - return connection.SendTo(outgoingDataBuffer.ToArray(), remoteEndPoint); + return SocketAsyncOperations.SendToAsync(Args, connection, remoteEndPoint, flags, outgoingDataBuffer); } - public int ReceiveBytes(Memory<byte> incomingDataBuffer) + public int ReceiveBytes(Memory<byte> incomingDataBuffer, SocketFlags flags = SocketFlags.None) { - byte[] temporaryBuffer = new byte[NetworkPacket.TotalSize]; + byte[] temporaryBuffer = BufferPool.Rent(NetworkPacket.TotalSize); int receivedBytes = connection.Receive(temporaryBuffer); temporaryBuffer.CopyTo(incomingDataBuffer); + BufferPool.Return(temporaryBuffer); return receivedBytes; } - public int ReceiveBytesFrom(Memory<byte> incomingDataBuffer, ref EndPoint remoteEndPoint) + public ValueTask<TransmissionResult> ReceiveBytesFrom(Memory<byte> incomingDataBuffer, ref EndPoint remoteEndPoint, SocketFlags flags = SocketFlags.None) { - byte[] temporaryBuffer = new byte[NetworkPacket.TotalSize]; - int receivedBytes = connection.ReceiveFrom(temporaryBuffer, ref remoteEndPoint); - temporaryBuffer.CopyTo(incomingDataBuffer); - - return receivedBytes; + return SocketAsyncOperations.ReceiveFromAsync(Args, connection, remoteEndPoint, flags, incomingDataBuffer); } } } \ No newline at end of file diff --git a/NetSharp/NetSharp/Utils/MyObjectPool.cs b/NetSharp/NetSharp/Utils/MyObjectPool.cs @@ -0,0 +1,54 @@ +using System.Collections.Concurrent; + +namespace NetSharp.Utils +{ + internal class MyObjectPool<T> where T : class + { + internal delegate T CreateObjectDelegate(); + + internal delegate bool KeepObjectPredicate(in T instance); + + internal delegate void ResetObjectDelegate(T instance); + + internal delegate void DestroyObjectDelegate(T instance); + + private readonly CreateObjectDelegate createObjectDelegate; + private readonly KeepObjectPredicate rebufferObjectPredicate; + private readonly ResetObjectDelegate resetObjectDelegate; + private readonly DestroyObjectDelegate destroyObjectDelegate; + + private readonly ConcurrentBag<T> objectBuffer; + + internal MyObjectPool(in CreateObjectDelegate createDelegate, in ResetObjectDelegate resetDelegate, in DestroyObjectDelegate destroyDelegate, in KeepObjectPredicate keepObjectPredicate) + { + createObjectDelegate = createDelegate; + + resetObjectDelegate = resetDelegate; + + destroyObjectDelegate = destroyDelegate; + + rebufferObjectPredicate = keepObjectPredicate; + + objectBuffer = new ConcurrentBag<T>(); + } + + internal T Rent() + { + return objectBuffer.TryTake(out T result) ? result : createObjectDelegate(); + } + + internal void Return(T instance) + { + if (rebufferObjectPredicate(instance)) + { + resetObjectDelegate(instance); + + objectBuffer.Add(instance); + } + else + { + destroyObjectDelegate(instance); + } + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Program.cs b/NetSharp/NetSharpExamples/Program.cs @@ -13,7 +13,7 @@ using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; - +using NetSharp.Utils; using NetworkPacket = NetSharp.Packets.NetworkPacket; using SocketServer = NetSharp.Sockets.SocketServer; @@ -44,14 +44,14 @@ namespace NetSharpExamples private static async Task TestSocketClient() { - const int clientCount = 20; + const int clientCount = 24; const long packetsToSend = 100_000; Task[] clientTasks = new Task[clientCount]; double[] clientBandwidths = new double[clientCount]; HashSet<int> activeTasks = new HashSet<int>(clientCount); - async void ClientTask(object id) + async Task ClientTask(object id) { try { @@ -103,7 +103,7 @@ namespace NetSharpExamples #if TCP int sendResult = client.SendBytes(requestBufferMemory); #else - int sendResult = client.SendBytesTo(requestBufferMemory, ServerEndPoint); + TransmissionResult sendResult = await client.SendBytesTo(requestBufferMemory, ServerEndPoint); #endif bandwidthStopwatch.Stop(); @@ -112,7 +112,7 @@ namespace NetSharpExamples #if DEBUG lock (typeof(Console)) { - Console.WriteLine($"[Client {id}, Packet {i}] Sent {sendResult} bytes to {ServerEndPoint}"); + Console.WriteLine($"[Client {id}, Packet {i}] Sent {sendResult.Count} bytes to {ServerEndPoint}"); Console.WriteLine($"[Client {id}, Packet {i}] >>>> {Encoding.UTF8.GetString(requestBufferMemory.Span)}"); } #endif @@ -125,7 +125,7 @@ namespace NetSharpExamples #if TCP int receiveResult = client.ReceiveBytes(responseBufferMemory); #else - int receiveResult = client.ReceiveBytesFrom(responseBufferMemory, ref serverEndPoint); + TransmissionResult receiveResult = await client.ReceiveBytesFrom(responseBufferMemory, ref serverEndPoint); #endif bandwidthStopwatch.Stop(); @@ -134,7 +134,7 @@ namespace NetSharpExamples #if DEBUG lock (typeof(Console)) { - Console.WriteLine($"[Client {id}, Packet {i}] Received {receiveResult} bytes from {serverEndPoint}"); + Console.WriteLine($"[Client {id}, Packet {i}] Received {receiveResult.Count} bytes from {serverEndPoint}"); Console.WriteLine($"[Client {id}, Packet {i}] <<<< {Encoding.UTF8.GetString(responseBufferMemory.Span)}"); } #endif @@ -200,7 +200,7 @@ namespace NetSharpExamples for (int clientId = 0; clientId < clientCount; clientId++) { - clientTasks[clientId] = Task.Factory.StartNew(ClientTask, clientId, TaskCreationOptions.LongRunning); + clientTasks[clientId] = Task.Factory.StartNew(ClientTask, clientId, TaskCreationOptions.LongRunning).Result; } await Task.WhenAll(clientTasks);