NetSharp

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

commit 07fb8aa4fb1ca2251cdaf1fe0be66cb4cb3b038e
parent 7545617da2caa6168b44bf53b5e4873dc01ae6c4
Author: Mikolaj Lenczewski <mikolaj.lenczewski308@gmail.com>
Date:   Sun,  3 May 2020 13:21:12 +0100

Implemented stream and datagram network classes (again), and added benchmarks. The rewrite gave us like 100-200 MBps extra performance in the server, so im ok with it.

Diffstat:
MNetSharp/NetSharp/DatagramNetworkConnection.cs | 267++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
MNetSharp/NetSharp/NetSharp.xml | 97++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
MNetSharp/NetSharp/NetworkConnectionBase.cs | 42+++++++++++++++---------------------------
MNetSharp/NetSharp/NetworkReaderBase.cs | 25++++++++++---------------
MNetSharp/NetSharp/NetworkWriterBase.cs | 12+++++++++---
MNetSharp/NetSharp/Sockets/Datagram/DatagramSocketClient.cs | 2+-
MNetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs | 4++--
MNetSharp/NetSharp/Sockets/RawSocketClient.cs | 2+-
MNetSharp/NetSharp/Sockets/RawSocketServer.cs | 11+++++++----
MNetSharp/NetSharp/Sockets/SocketConnectionBase.cs | 25++++++++++++-------------
MNetSharp/NetSharp/Sockets/Stream/StreamSocketClient.cs | 4++--
MNetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs | 2+-
ANetSharp/NetSharp/StreamNetworkConnection.cs | 646+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MNetSharp/NetSharp/Utils/SlimObjectPool.cs | 4++--
ANetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkReaderBenchmark.cs | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkWriterAsyncBenchmark.cs | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkWriterSyncBenchmark.cs | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DNetSharp/NetSharpExamples/Benchmarks/DatagramNetworkReaderBenchmark.cs | 110-------------------------------------------------------------------------------
ANetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkReaderBenchmark.cs | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkWriterAsyncBenchmark.cs | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkWriterSyncBenchmark.cs | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketClientAsyncBenchmark.cs | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketClientSyncBenchmark.cs | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketServerBenchmark.cs | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DNetSharp/NetSharpExamples/Benchmarks/TcpSocketClientAsyncBenchmark.cs | 122-------------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Benchmarks/TcpSocketClientSyncBenchmark.cs | 122-------------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Benchmarks/TcpSocketServerBenchmark.cs | 141-------------------------------------------------------------------------------
ANetSharp/NetSharpExamples/Benchmarks/UDP Socket Connection Benchmarks/UdpSocketClientAsyncBenchmark.cs | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharpExamples/Benchmarks/UDP Socket Connection Benchmarks/UdpSocketClientSyncBenchmark.cs | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharpExamples/Benchmarks/UDP Socket Connection Benchmarks/UdpSocketServerBenchmark.cs | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DNetSharp/NetSharpExamples/Benchmarks/UdpSocketClientAsyncBenchmark.cs | 89-------------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Benchmarks/UdpSocketClientSyncBenchmark.cs | 100-------------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Benchmarks/UdpSocketServerBenchmark.cs | 119-------------------------------------------------------------------------------
ANetSharp/NetSharpExamples/Examples/TCP Socket Connection Examples/TcpSocketClientExample.cs | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharpExamples/Examples/TCP Socket Connection Examples/TcpSocketServerExample.cs | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
DNetSharp/NetSharpExamples/Examples/TcpSocketClientExample.cs | 80-------------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Examples/TcpSocketServerExample.cs | 56--------------------------------------------------------
ANetSharp/NetSharpExamples/Examples/UDP Socket Connection Examples/UdpSocketClientExample.cs | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharpExamples/Examples/UDP Socket Connection Examples/UdpSocketServerExample.cs | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
DNetSharp/NetSharpExamples/Examples/UdpSocketClientExample.cs | 72------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Examples/UdpSocketServerExample.cs | 56--------------------------------------------------------
ANetSharp/NetSharpExamples/INetSharpBenchmark.cs | 7+++++++
MNetSharp/NetSharpExamples/NetSharpExamples.csproj | 5+++++
MNetSharp/NetSharpExamples/NetSharpExamples.xml | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
MNetSharp/NetSharpExamples/Program.cs | 84++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
45 files changed, 2805 insertions(+), 1241 deletions(-)

diff --git a/NetSharp/NetSharp/DatagramNetworkConnection.cs b/NetSharp/NetSharp/DatagramNetworkConnection.cs @@ -1,7 +1,6 @@ using System; using System.Net; using System.Net.Sockets; -using System.Threading; using System.Threading.Tasks; namespace NetSharp @@ -9,39 +8,39 @@ namespace NetSharp public sealed class DatagramNetworkReader : NetworkReaderBase<SocketAsyncEventArgs> { /// <inheritdoc /> - public DatagramNetworkReader(ref Socket rawConnection, NetworkRequestHandler? requestHandler, EndPoint defaultEndPoint, int maxPooledBufferSize, int preallocatedStateObjects = 0) - : base(ref rawConnection, requestHandler, defaultEndPoint, maxPooledBufferSize, preallocatedStateObjects) + public DatagramNetworkReader(ref Socket rawConnection, NetworkRequestHandler? requestHandler, EndPoint defaultEndPoint, int maxPooledBufferSize, + int maxPooledBuffersPerBucket = 1000, uint preallocatedStateObjects = 0) : base(ref rawConnection, defaultEndPoint, requestHandler, maxPooledBufferSize, + maxPooledBuffersPerBucket, preallocatedStateObjects) { } private void CompleteReceiveFrom(SocketAsyncEventArgs args) { - RentedBufferHandle receiveBufferHandle = (RentedBufferHandle) args.UserToken; + byte[] receiveBuffer = args.Buffer; switch (args.SocketError) { case SocketError.Success: - RentedBufferHandle responseBufferHandle = RentBuffer(BufferSize); + byte[] responseBuffer = BufferPool.Rent(BufferSize); bool responseExists = - RequestHandler(args.RemoteEndPoint, receiveBufferHandle.RentedBuffer, responseBufferHandle.RentedBuffer); - ReturnBuffer(receiveBufferHandle); + RequestHandler(args.RemoteEndPoint, receiveBuffer, responseBuffer); + BufferPool.Return(receiveBuffer, true); if (responseExists) { - args.SetBuffer(responseBufferHandle.RentedBuffer); - args.UserToken = responseBufferHandle; + args.SetBuffer(responseBuffer, 0, BufferSize); StartSendTo(args); return; } - ReturnBuffer(responseBufferHandle); + BufferPool.Return(responseBuffer, true); break; default: - ReturnBuffer(receiveBufferHandle); + BufferPool.Return(receiveBuffer, true); StateObjectPool.Return(args); break; } @@ -49,9 +48,9 @@ namespace NetSharp private void CompleteSendTo(SocketAsyncEventArgs args) { - RentedBufferHandle sendBufferHandle = (RentedBufferHandle)args.UserToken; + byte[] sendBuffer = args.Buffer; - ReturnBuffer(sendBufferHandle); + BufferPool.Return(sendBuffer, true); StateObjectPool.Return(args); } @@ -84,19 +83,18 @@ namespace NetSharp private void StartReceiveFrom(SocketAsyncEventArgs args) { - RentedBufferHandle receiveBufferHandle = RentBuffer(BufferSize); - - args.SetBuffer(receiveBufferHandle.RentedBuffer, 0, BufferSize); - args.UserToken = receiveBufferHandle; + byte[] receiveBuffer = BufferPool.Rent(BufferSize); if (ShutdownToken.IsCancellationRequested) { - ReturnBuffer(receiveBufferHandle); + BufferPool.Return(receiveBuffer, true); StateObjectPool.Return(args); return; } + args.SetBuffer(receiveBuffer, 0, BufferSize); + if (Connection.ReceiveFromAsync(args)) return; StartDefaultReceiveFrom(); @@ -105,11 +103,11 @@ namespace NetSharp private void StartSendTo(SocketAsyncEventArgs args) { - RentedBufferHandle sendBufferHandle = (RentedBufferHandle) args.UserToken; + byte[] sendBuffer = args.Buffer; if (ShutdownToken.IsCancellationRequested) { - ReturnBuffer(sendBufferHandle); + BufferPool.Return(sendBuffer, true); StateObjectPool.Return(args); return; @@ -121,7 +119,7 @@ namespace NetSharp } /// <inheritdoc /> - protected override bool CanReuseStateObject(in SocketAsyncEventArgs instance) + protected override bool CanReuseStateObject(ref SocketAsyncEventArgs instance) { return true; } @@ -161,44 +159,253 @@ namespace NetSharp public sealed class DatagramNetworkWriter : NetworkWriterBase<SocketAsyncEventArgs> { /// <inheritdoc /> - public DatagramNetworkWriter(ref Socket rawConnection, int maxPooledBufferSize, int preallocatedStateObjects = 0) : base(ref rawConnection, maxPooledBufferSize, preallocatedStateObjects) + public DatagramNetworkWriter(ref Socket rawConnection, EndPoint defaultEndPoint, int maxPooledBufferSize, int maxPooledBuffersPerBucket = 1000, + uint preallocatedStateObjects = 0) : base(ref rawConnection, defaultEndPoint, maxPooledBufferSize, maxPooledBuffersPerBucket, preallocatedStateObjects) { } + private void CompleteReceiveFrom(SocketAsyncEventArgs args) + { + AsyncDatagramReadToken token = (AsyncDatagramReadToken)args.UserToken; + + byte[] receiveBuffer = token.TransmissionBuffer; + + switch (args.SocketError) + { + case SocketError.Success: + receiveBuffer.CopyTo(token.UserBuffer); + token.CompletionSource.SetResult(args.BytesTransferred); + break; + + case SocketError.OperationAborted: + token.CompletionSource.SetCanceled(); + break; + + default: + int errorCode = (int)args.SocketError; + token.CompletionSource.SetException(new SocketException(errorCode)); + break; + } + + BufferPool.Return(receiveBuffer, true); + StateObjectPool.Return(args); + } + + private void CompleteSendTo(SocketAsyncEventArgs args) + { + AsyncDatagramWriteToken token = (AsyncDatagramWriteToken)args.UserToken; + + byte[] sendBuffer = token.TransmissionBuffer; + + switch (args.SocketError) + { + case SocketError.Success: + token.CompletionSource.SetResult(args.BytesTransferred); + break; + + case SocketError.OperationAborted: + token.CompletionSource.SetCanceled(); + break; + + default: + int errorCode = (int)args.SocketError; + token.CompletionSource.SetException(new SocketException(errorCode)); + break; + } + + BufferPool.Return(sendBuffer, true); + StateObjectPool.Return(args); + } + + private void HandleIoCompleted(object sender, SocketAsyncEventArgs args) + { + switch (args.LastOperation) + { + case SocketAsyncOperation.Connect: + break; + + case SocketAsyncOperation.SendTo: + CompleteSendTo(args); + break; + + case SocketAsyncOperation.ReceiveFrom: + CompleteReceiveFrom(args); + break; + } + } + /// <inheritdoc /> - protected override bool CanReuseStateObject(in SocketAsyncEventArgs instance) + protected override bool CanReuseStateObject(ref SocketAsyncEventArgs instance) { - throw new NotImplementedException(); + return true; } /// <inheritdoc /> protected override SocketAsyncEventArgs CreateStateObject() { - throw new NotImplementedException(); + SocketAsyncEventArgs instance = new SocketAsyncEventArgs(); + instance.Completed += HandleIoCompleted; + + return instance; } /// <inheritdoc /> protected override void DestroyStateObject(SocketAsyncEventArgs instance) { - throw new NotImplementedException(); + instance.Completed -= HandleIoCompleted; + instance.Dispose(); } /// <inheritdoc /> protected override void ResetStateObject(ref SocketAsyncEventArgs instance) { - throw new NotImplementedException(); } /// <inheritdoc /> - public override int Write(EndPoint remoteEndPoint, ReadOnlyMemory<byte> writeBuffer, SocketFlags flags = SocketFlags.None) + public override int Read(ref EndPoint remoteEndPoint, Memory<byte> readBuffer, SocketFlags flags = SocketFlags.None) + { + int totalBytes = readBuffer.Length; + if (totalBytes > BufferSize) + { + throw new ArgumentException( + $"Cannot rent a temporary buffer of size: {totalBytes} bytes; maximum temporary buffer size: {BufferSize} bytes", + nameof(readBuffer.Length) + ); + } + + byte[] transmissionBuffer = BufferPool.Rent(BufferSize); + + int readBytes = Connection.ReceiveFrom(transmissionBuffer, flags, ref remoteEndPoint); + + transmissionBuffer.CopyTo(readBuffer); + BufferPool.Return(transmissionBuffer, true); + + return readBytes; + } + + /// <inheritdoc /> + public override ValueTask<int> ReadAsync(EndPoint remoteEndPoint, Memory<byte> readBuffer, SocketFlags flags = SocketFlags.None) { - throw new NotImplementedException(); + int totalBytes = readBuffer.Length; + if (totalBytes > BufferSize) + { + throw new ArgumentException( + $"Cannot rent a temporary buffer of size: {totalBytes} bytes; maximum temporary buffer size: {BufferSize} bytes", + nameof(readBuffer.Length) + ); + } + + TaskCompletionSource<int> tcs = new TaskCompletionSource<int>(); + SocketAsyncEventArgs args = StateObjectPool.Rent(); + + byte[] transmissionBuffer = BufferPool.Rent(BufferSize); + + args.SetBuffer(transmissionBuffer); + + args.RemoteEndPoint = remoteEndPoint; + args.SocketFlags = flags; + + AsyncDatagramReadToken token = new AsyncDatagramReadToken(tcs, ref transmissionBuffer, in readBuffer); + args.UserToken = token; + + if (Connection.ReceiveFromAsync(args)) return new ValueTask<int>(tcs.Task); + + int result = args.BytesTransferred; + + transmissionBuffer.CopyTo(readBuffer); + + BufferPool.Return(transmissionBuffer, true); + StateObjectPool.Return(args); + + return new ValueTask<int>(result); + } + + /// <inheritdoc /> + public override int Write(EndPoint remoteEndPoint, ReadOnlyMemory<byte> writeBuffer, + SocketFlags flags = SocketFlags.None) + { + int totalBytes = writeBuffer.Length; + if (totalBytes > BufferSize) + { + throw new ArgumentException( + $"Cannot rent a temporary buffer of size: {totalBytes} bytes; maximum temporary buffer size: {BufferSize} bytes", + nameof(writeBuffer.Length) + ); + } + + byte[] transmissionBuffer = BufferPool.Rent(BufferSize); + writeBuffer.CopyTo(transmissionBuffer); + + int writtenBytes = Connection.SendTo(transmissionBuffer, flags, remoteEndPoint); + + BufferPool.Return(transmissionBuffer); + + return writtenBytes; } /// <inheritdoc /> public override ValueTask<int> WriteAsync(EndPoint remoteEndPoint, ReadOnlyMemory<byte> writeBuffer, SocketFlags flags = SocketFlags.None) { - throw new NotImplementedException(); + int totalBytes = writeBuffer.Length; + if (totalBytes > BufferSize) + { + throw new ArgumentException( + $"Cannot rent a temporary buffer of size: {totalBytes} bytes; maximum temporary buffer size: {BufferSize} bytes", + nameof(writeBuffer.Length) + ); + } + + TaskCompletionSource<int> tcs = new TaskCompletionSource<int>(); + SocketAsyncEventArgs args = StateObjectPool.Rent(); + + byte[] transmissionBuffer = BufferPool.Rent(BufferSize); + writeBuffer.CopyTo(transmissionBuffer); + + args.SetBuffer(transmissionBuffer); + + args.RemoteEndPoint = remoteEndPoint; + args.SocketFlags = flags; + + AsyncDatagramWriteToken token = new AsyncDatagramWriteToken(tcs, ref transmissionBuffer); + args.UserToken = token; + + if (Connection.SendToAsync(args)) return new ValueTask<int>(tcs.Task); + + int result = args.BytesTransferred; + + BufferPool.Return(transmissionBuffer, true); + StateObjectPool.Return(args); + + return new ValueTask<int>(result); + } + + private readonly struct AsyncDatagramReadToken + { + public readonly TaskCompletionSource<int> CompletionSource; + public readonly byte[] TransmissionBuffer; + public readonly Memory<byte> UserBuffer; + + public AsyncDatagramReadToken(TaskCompletionSource<int> completionSource, ref byte[] transmissionBuffer, in Memory<byte> userBuffer) + { + CompletionSource = completionSource; + + TransmissionBuffer = transmissionBuffer; + + UserBuffer = userBuffer; + } + } + + private readonly struct AsyncDatagramWriteToken + { + public readonly TaskCompletionSource<int> CompletionSource; + public readonly byte[] TransmissionBuffer; + + public AsyncDatagramWriteToken(TaskCompletionSource<int> completionSource, ref byte[] transmissionBuffer) + { + CompletionSource = completionSource; + + TransmissionBuffer = transmissionBuffer; + } } } } \ No newline at end of file diff --git a/NetSharp/NetSharp/NetSharp.xml b/NetSharp/NetSharp/NetSharp.xml @@ -4,7 +4,7 @@ <name>NetSharp</name> </assembly> <members> - <member name="M:NetSharp.DatagramNetworkReader.#ctor(System.Net.Sockets.Socket@,NetSharp.NetworkRequestHandler,System.Net.EndPoint,System.Int32,System.Int32)"> + <member name="M:NetSharp.DatagramNetworkReader.#ctor(System.Net.Sockets.Socket@,NetSharp.NetworkRequestHandler,System.Net.EndPoint,System.Int32,System.Int32,System.UInt32)"> <inheritdoc /> </member> <member name="M:NetSharp.DatagramNetworkReader.CanReuseStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> @@ -22,7 +22,7 @@ <member name="M:NetSharp.DatagramNetworkReader.Start(System.UInt16)"> <inheritdoc /> </member> - <member name="M:NetSharp.DatagramNetworkWriter.#ctor(System.Net.Sockets.Socket@,System.Int32,System.Int32)"> + <member name="M:NetSharp.DatagramNetworkWriter.#ctor(System.Net.Sockets.Socket@,System.Net.EndPoint,System.Int32,System.Int32,System.UInt32)"> <inheritdoc /> </member> <member name="M:NetSharp.DatagramNetworkWriter.CanReuseStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> @@ -37,6 +37,12 @@ <member name="M:NetSharp.DatagramNetworkWriter.ResetStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> <inheritdoc /> </member> + <member name="M:NetSharp.DatagramNetworkWriter.Read(System.Net.EndPoint@,System.Memory{System.Byte},System.Net.Sockets.SocketFlags)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.DatagramNetworkWriter.ReadAsync(System.Net.EndPoint,System.Memory{System.Byte},System.Net.Sockets.SocketFlags)"> + <inheritdoc /> + </member> <member name="M:NetSharp.DatagramNetworkWriter.Write(System.Net.EndPoint,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)"> <inheritdoc /> </member> @@ -51,13 +57,13 @@ <member name="M:NetSharp.NetworkConnectionBase`1.Dispose"> <inheritdoc /> </member> - <member name="M:NetSharp.NetworkReaderBase`1.#ctor(System.Net.Sockets.Socket@,NetSharp.NetworkRequestHandler,System.Net.EndPoint,System.Int32,System.Int32)"> + <member name="M:NetSharp.NetworkReaderBase`1.#ctor(System.Net.Sockets.Socket@,System.Net.EndPoint,NetSharp.NetworkRequestHandler,System.Int32,System.Int32,System.UInt32)"> <inheritdoc /> </member> <member name="M:NetSharp.NetworkReaderBase`1.Dispose(System.Boolean)"> <inheritdoc /> </member> - <member name="M:NetSharp.NetworkWriterBase`1.#ctor(System.Net.Sockets.Socket@,System.Int32,System.Int32)"> + <member name="M:NetSharp.NetworkWriterBase`1.#ctor(System.Net.Sockets.Socket@,System.Net.EndPoint,System.Int32,System.Int32,System.UInt32)"> <inheritdoc /> </member> <member name="T:NetSharp.Packets.NetworkPacket"> @@ -334,7 +340,7 @@ Constructs a new instance of the <see cref="T:NetSharp.Sockets.RawSocketClient" /> class. </summary> <param name="rawConnection"> - The underlying <see cref="T:System.Net.Sockets.Socket"/> object which should be wrapped by this instance. + The underlying <see cref="T:System.Net.Sockets.Socket" /> object which should be wrapped by this instance. </param> <param name="pooledBufferMaxSize"> The maximum size in bytes of buffers held in the buffer pool. @@ -579,7 +585,7 @@ Constructs a new instance of the <see cref="T:NetSharp.Sockets.RawSocketServer" /> class. </summary> <param name="rawConnection"> - The underlying <see cref="T:System.Net.Sockets.Socket"/> object which should be wrapped by this instance. + The underlying <see cref="T:System.Net.Sockets.Socket" /> object which should be wrapped by this instance. </param> <param name="packetHandler"> The packet handler delegate to use to respond to incoming requests. @@ -598,8 +604,12 @@ <param name="remoteEndPoint"> The client from which the packet was received. </param> - <param name="requestBuffer">The data that was received.</param> - <param name="responseBuffer">The data that should be sent back.</param> + <param name="requestBuffer"> + The data that was received. + </param> + <param name="responseBuffer"> + The data that should be sent back. + </param> <returns> Whether to send back a response. </returns> @@ -617,12 +627,13 @@ </member> <member name="T:NetSharp.Sockets.SocketConnectionBase"> <summary> - Abstract base class for client and server wrappers around existing <see cref="T:System.Net.Sockets.Socket"/> objects. + Abstract base class for client and server wrappers around existing <see cref="T:System.Net.Sockets.Socket" /> objects. </summary> </member> - <member name="F:NetSharp.Sockets.SocketConnectionBase.MaxBufferSize"> + <member name="F:NetSharp.Sockets.SocketConnectionBase.ArgsPool"> <summary> - The maximum size of buffer that can be rented from the pool. + Pools <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> objects for use during network read/write operations and calls to + <see cref="T:System.Net.Sockets.Socket" />.XXXAsync( <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" />) methods. </summary> </member> <member name="F:NetSharp.Sockets.SocketConnectionBase.BufferPool"> @@ -630,10 +641,9 @@ Pools arrays to function as temporary buffers during network read/write operations. </summary> </member> - <member name="F:NetSharp.Sockets.SocketConnectionBase.ArgsPool"> + <member name="F:NetSharp.Sockets.SocketConnectionBase.MaxBufferSize"> <summary> - Pools <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> objects for use during network read/write operations and calls to - <see cref="T:System.Net.Sockets.Socket" />.XXXAsync( <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" />) methods. + The maximum size of buffer that can be rented from the pool. </summary> </member> <member name="F:NetSharp.Sockets.SocketConnectionBase.Connection"> @@ -646,7 +656,7 @@ Constructs a new instance of the <see cref="T:NetSharp.Sockets.SocketConnectionBase" /> class. </summary> <param name="rawConnection"> - The underlying <see cref="T:System.Net.Sockets.Socket"/> object which should be wrapped by this instance. + The underlying <see cref="T:System.Net.Sockets.Socket" /> object which should be wrapped by this instance. </param> <param name="pooledBufferMaxSize"> The maximum size in bytes of buffers held in the buffer pool. @@ -675,8 +685,8 @@ </member> <member name="M:NetSharp.Sockets.SocketConnectionBase.CreateTransmissionArgs"> <summary> - Delegate method used to construct fresh <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances for use in the <see cref="F:NetSharp.Sockets.SocketConnectionBase.ArgsPool" />. - The resulting instance should register <see cref="M:NetSharp.Sockets.SocketConnectionBase.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)" /> as an event handler for the + Delegate method used to construct fresh <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances for use in the <see cref="F:NetSharp.Sockets.SocketConnectionBase.ArgsPool" />. The + resulting instance should register <see cref="M:NetSharp.Sockets.SocketConnectionBase.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)" /> as an event handler for the <see cref="E:System.Net.Sockets.SocketAsyncEventArgs.Completed" /> event. </summary> <returns> @@ -685,9 +695,8 @@ </member> <member name="M:NetSharp.Sockets.SocketConnectionBase.DestroyTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs)"> <summary> - Delegate method to destroy used <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances that cannot be reused by the - <see cref="F:NetSharp.Sockets.SocketConnectionBase.ArgsPool" />. This method should deregister <see cref="M:NetSharp.Sockets.SocketConnectionBase.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)" /> as an event handler for the - <see cref="E:System.Net.Sockets.SocketAsyncEventArgs.Completed" /> event. + Delegate method to destroy used <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances that cannot be reused by the <see cref="F:NetSharp.Sockets.SocketConnectionBase.ArgsPool" />. This + method should deregister <see cref="M:NetSharp.Sockets.SocketConnectionBase.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)" /> as an event handler for the <see cref="E:System.Net.Sockets.SocketAsyncEventArgs.Completed" /> event. </summary> <param name="remoteConnectionArgs"> The <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> which should be destroyed. @@ -850,6 +859,51 @@ <member name="M:NetSharp.Sockets.Stream.StreamSocketServer.RunAsync(System.Threading.CancellationToken)"> <inheritdoc /> </member> + <member name="M:NetSharp.StreamNetworkReader.#ctor(System.Net.Sockets.Socket@,NetSharp.NetworkRequestHandler,System.Net.EndPoint,System.Int32,System.Int32,System.UInt32)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkReader.CanReuseStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkReader.CreateStateObject"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkReader.DestroyStateObject(System.Net.Sockets.SocketAsyncEventArgs)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkReader.ResetStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkReader.Start(System.UInt16)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkWriter.#ctor(System.Net.Sockets.Socket@,System.Net.EndPoint,System.Int32,System.Int32,System.UInt32)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkWriter.CanReuseStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkWriter.CreateStateObject"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkWriter.DestroyStateObject(System.Net.Sockets.SocketAsyncEventArgs)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkWriter.ResetStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkWriter.Read(System.Net.EndPoint@,System.Memory{System.Byte},System.Net.Sockets.SocketFlags)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkWriter.ReadAsync(System.Net.EndPoint,System.Memory{System.Byte},System.Net.Sockets.SocketFlags)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkWriter.Write(System.Net.EndPoint,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.StreamNetworkWriter.WriteAsync(System.Net.EndPoint,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)"> + <inheritdoc /> + </member> <member name="T:NetSharp.Utils.BiDictionary`2"> <summary> Represents a concurrent two-way dictionary, that can be indexed by either a key or a value. @@ -1263,4 +1317,4 @@ </returns> </member> </members> -</doc> +</doc> +\ No newline at end of file diff --git a/NetSharp/NetSharp/NetworkConnectionBase.cs b/NetSharp/NetSharp/NetworkConnectionBase.cs @@ -9,23 +9,33 @@ namespace NetSharp { public abstract class NetworkConnectionBase<TState> : IDisposable where TState : class { - private readonly ArrayPool<byte> bufferPool; - protected readonly SlimObjectPool<TState> StateObjectPool; + protected readonly ArrayPool<byte> BufferPool; protected readonly int BufferSize; protected readonly Socket Connection; + protected readonly EndPoint DefaultEndPoint; + protected readonly SlimObjectPool<TState> StateObjectPool; - protected NetworkConnectionBase(ref Socket rawConnection, int maxPooledBufferSize, int preallocatedStateObjects = 0) + protected NetworkConnectionBase(ref Socket rawConnection, EndPoint defaultEndPoint, int maxPooledBufferSize, + int maxPooledBuffersPerBucket = 1000, uint preallocatedStateObjects = 0) { Connection = rawConnection; BufferSize = maxPooledBufferSize; - bufferPool = ArrayPool<byte>.Create(maxPooledBufferSize, 1_000); + BufferPool = ArrayPool<byte>.Create(maxPooledBufferSize, maxPooledBuffersPerBucket); + + DefaultEndPoint = defaultEndPoint; StateObjectPool = new SlimObjectPool<TState>(CreateStateObject, ResetStateObject, DestroyStateObject, CanReuseStateObject); + + // TODO implement pooling in better way + for (uint i = 0; i < preallocatedStateObjects; i++) + { + StateObjectPool.Return(CreateStateObject()); + } } - protected abstract bool CanReuseStateObject(in TState instance); + protected abstract bool CanReuseStateObject(ref TState instance); protected abstract TState CreateStateObject(); @@ -41,35 +51,13 @@ namespace NetSharp StateObjectPool.Dispose(); } - protected RentedBufferHandle RentBuffer(int desiredBufferSize) - { - byte[] rentedBuffer = bufferPool.Rent(desiredBufferSize); - - return new RentedBufferHandle(ref rentedBuffer); - } - protected abstract void ResetStateObject(ref TState instance); - protected void ReturnBuffer(RentedBufferHandle handle) - { - bufferPool.Return(handle.RentedBuffer, true); - } - /// <inheritdoc /> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } - - protected readonly struct RentedBufferHandle - { - public readonly byte[] RentedBuffer; - - internal RentedBufferHandle(ref byte[] rentedBuffer) - { - RentedBuffer = rentedBuffer; - } - } } } \ No newline at end of file diff --git a/NetSharp/NetSharp/NetworkReaderBase.cs b/NetSharp/NetSharp/NetworkReaderBase.cs @@ -2,7 +2,6 @@ using System.Net; using System.Net.Sockets; using System.Threading; -using System.Threading.Tasks; namespace NetSharp { @@ -13,30 +12,20 @@ namespace NetSharp { private readonly CancellationTokenSource shutdownTokenSource; - protected readonly CancellationToken ShutdownToken; - protected readonly NetworkRequestHandler RequestHandler; - - protected readonly EndPoint DefaultEndPoint; + protected readonly CancellationToken ShutdownToken; /// <inheritdoc /> - protected NetworkReaderBase(ref Socket rawConnection, NetworkRequestHandler? requestHandler, EndPoint defaultEndPoint, int maxPooledBufferSize, int preallocatedStateObjects = 0) - : base(ref rawConnection, maxPooledBufferSize, preallocatedStateObjects) + protected NetworkReaderBase(ref Socket rawConnection, EndPoint defaultEndPoint, NetworkRequestHandler? requestHandler, int maxPooledBufferSize, + int maxPooledBuffersPerBucket = 1000, uint preallocatedStateObjects = 0) : base(ref rawConnection, defaultEndPoint, maxPooledBufferSize, + maxPooledBuffersPerBucket, preallocatedStateObjects) { shutdownTokenSource = new CancellationTokenSource(); ShutdownToken = shutdownTokenSource.Token; - DefaultEndPoint = defaultEndPoint; - RequestHandler = requestHandler ?? DefaultRequestHandler; } - public static bool DefaultRequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, - Memory<byte> responseBuffer) - { - return requestBuffer.TryCopyTo(responseBuffer); - } - /// <inheritdoc /> protected override void Dispose(bool disposing) { @@ -48,6 +37,12 @@ namespace NetSharp base.Dispose(disposing); } + public static bool DefaultRequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, + Memory<byte> responseBuffer) + { + return requestBuffer.TryCopyTo(responseBuffer); + } + public abstract void Start(ushort concurrentReadTasks); public void Stop() diff --git a/NetSharp/NetSharp/NetworkWriterBase.cs b/NetSharp/NetSharp/NetworkWriterBase.cs @@ -8,14 +8,20 @@ namespace NetSharp public abstract class NetworkWriterBase<TState> : NetworkConnectionBase<TState> where TState : class { /// <inheritdoc /> - protected NetworkWriterBase(ref Socket rawConnection, int maxPooledBufferSize, int preallocatedStateObjects = 0) - : base(ref rawConnection, maxPooledBufferSize, preallocatedStateObjects) + protected NetworkWriterBase(ref Socket rawConnection, EndPoint defaultEndPoint, int maxPooledBufferSize, int maxPooledBuffersPerBucket = 1000, + uint preallocatedStateObjects = 0) : base(ref rawConnection, defaultEndPoint, maxPooledBufferSize, maxPooledBuffersPerBucket, preallocatedStateObjects) { } - public abstract int Write(EndPoint remoteEndPoint, ReadOnlyMemory<byte> writeBuffer, + public abstract int Read(ref EndPoint remoteEndPoint, Memory<byte> readBuffer, + SocketFlags flags = SocketFlags.None); + + public abstract ValueTask<int> ReadAsync(EndPoint remoteEndPoint, Memory<byte> readBuffer, SocketFlags flags = SocketFlags.None); + public abstract int Write(EndPoint remoteEndPoint, ReadOnlyMemory<byte> writeBuffer, + SocketFlags flags = SocketFlags.None); + public abstract ValueTask<int> WriteAsync(EndPoint remoteEndPoint, ReadOnlyMemory<byte> writeBuffer, SocketFlags flags = SocketFlags.None); } diff --git a/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketClient.cs b/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketClient.cs @@ -143,7 +143,7 @@ namespace NetSharp.Sockets.Datagram } /// <inheritdoc /> - protected override bool CanTransmissionArgsBeReused(in SocketAsyncEventArgs args) + protected override bool CanTransmissionArgsBeReused(ref SocketAsyncEventArgs args) { return true; } diff --git a/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs b/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs @@ -66,7 +66,7 @@ namespace NetSharp.Sockets.Datagram /// </param> /// <inheritdoc /> public DatagramSocketServer(ref Socket rawConnection, in RawRequestPacketHandler packetHandler, in DatagramSocketServerOptions? serverOptions = null) - : base(ref rawConnection, + : base(ref rawConnection, NetworkPacket.TotalSize, serverOptions?.PreallocatedTransmissionArgs ?? DatagramSocketServerOptions.Defaults.PreallocatedTransmissionArgs, packetHandler) @@ -180,7 +180,7 @@ namespace NetSharp.Sockets.Datagram } /// <inheritdoc /> - protected override bool CanTransmissionArgsBeReused(in SocketAsyncEventArgs args) + protected override bool CanTransmissionArgsBeReused(ref SocketAsyncEventArgs args) { return true; } diff --git a/NetSharp/NetSharp/Sockets/RawSocketClient.cs b/NetSharp/NetSharp/Sockets/RawSocketClient.cs @@ -18,7 +18,7 @@ namespace NetSharp.Sockets /// Constructs a new instance of the <see cref="RawSocketClient" /> class. /// </summary> /// <param name="rawConnection"> - /// The underlying <see cref="Socket"/> object which should be wrapped by this instance. + /// The underlying <see cref="Socket" /> object which should be wrapped by this instance. /// </param> /// <param name="pooledBufferMaxSize"> /// The maximum size in bytes of buffers held in the buffer pool. diff --git a/NetSharp/NetSharp/Sockets/RawSocketServer.cs b/NetSharp/NetSharp/Sockets/RawSocketServer.cs @@ -1,5 +1,4 @@ using System; -using NetSharp.Packets; using System.Net; using System.Net.Sockets; @@ -39,7 +38,7 @@ namespace NetSharp.Sockets /// Constructs a new instance of the <see cref="RawSocketServer" /> class. /// </summary> /// <param name="rawConnection"> - /// The underlying <see cref="Socket"/> object which should be wrapped by this instance. + /// The underlying <see cref="Socket" /> object which should be wrapped by this instance. /// </param> /// <param name="packetHandler"> /// The packet handler delegate to use to respond to incoming requests. @@ -62,8 +61,12 @@ namespace NetSharp.Sockets /// <param name="remoteEndPoint"> /// The client from which the packet was received. /// </param> - /// <param name="requestBuffer">The data that was received.</param> - /// <param name="responseBuffer">The data that should be sent back.</param> + /// <param name="requestBuffer"> + /// The data that was received. + /// </param> + /// <param name="responseBuffer"> + /// The data that should be sent back. + /// </param> /// <returns> /// Whether to send back a response. /// </returns> diff --git a/NetSharp/NetSharp/Sockets/SocketConnectionBase.cs b/NetSharp/NetSharp/Sockets/SocketConnectionBase.cs @@ -8,14 +8,15 @@ using System.Net.Sockets; namespace NetSharp.Sockets { /// <summary> - /// Abstract base class for client and server wrappers around existing <see cref="Socket"/> objects. + /// Abstract base class for client and server wrappers around existing <see cref="Socket" /> objects. /// </summary> public abstract class SocketConnectionBase : IDisposable { /// <summary> - /// The maximum size of buffer that can be rented from the pool. + /// Pools <see cref="SocketAsyncEventArgs" /> objects for use during network read/write operations and calls to + /// <see cref="Socket" />.XXXAsync( <see cref="SocketAsyncEventArgs" />) methods. /// </summary> - protected readonly int MaxBufferSize; + protected readonly SlimObjectPool<SocketAsyncEventArgs> ArgsPool; /// <summary> /// Pools arrays to function as temporary buffers during network read/write operations. @@ -23,10 +24,9 @@ namespace NetSharp.Sockets protected readonly ArrayPool<byte> BufferPool; /// <summary> - /// Pools <see cref="SocketAsyncEventArgs" /> objects for use during network read/write operations and calls to - /// <see cref="Socket" />.XXXAsync( <see cref="SocketAsyncEventArgs" />) methods. + /// The maximum size of buffer that can be rented from the pool. /// </summary> - protected readonly SlimObjectPool<SocketAsyncEventArgs> ArgsPool; + protected readonly int MaxBufferSize; /// <summary> /// The underlying <see cref="Socket" /> which provides access to network operations. @@ -37,7 +37,7 @@ namespace NetSharp.Sockets /// Constructs a new instance of the <see cref="SocketConnectionBase" /> class. /// </summary> /// <param name="rawConnection"> - /// The underlying <see cref="Socket"/> object which should be wrapped by this instance. + /// The underlying <see cref="Socket" /> object which should be wrapped by this instance. /// </param> /// <param name="pooledBufferMaxSize"> /// The maximum size in bytes of buffers held in the buffer pool. @@ -83,11 +83,11 @@ namespace NetSharp.Sockets /// <returns> /// Whether the given <paramref name="args" /> should be reset and reused, or should be destroyed. /// </returns> - protected abstract bool CanTransmissionArgsBeReused(in SocketAsyncEventArgs args); + protected abstract bool CanTransmissionArgsBeReused(ref SocketAsyncEventArgs args); /// <summary> - /// Delegate method used to construct fresh <see cref="SocketAsyncEventArgs" /> instances for use in the <see cref="ArgsPool" />. - /// The resulting instance should register <see cref="HandleIoCompleted" /> as an event handler for the + /// Delegate method used to construct fresh <see cref="SocketAsyncEventArgs" /> instances for use in the <see cref="ArgsPool" />. The + /// resulting instance should register <see cref="HandleIoCompleted" /> as an event handler for the /// <see cref="SocketAsyncEventArgs.Completed" /> event. /// </summary> /// <returns> @@ -96,9 +96,8 @@ namespace NetSharp.Sockets protected abstract SocketAsyncEventArgs CreateTransmissionArgs(); /// <summary> - /// Delegate method to destroy used <see cref="SocketAsyncEventArgs" /> instances that cannot be reused by the - /// <see cref="ArgsPool" />. This method should deregister <see cref="HandleIoCompleted" /> as an event handler for the - /// <see cref="SocketAsyncEventArgs.Completed" /> event. + /// Delegate method to destroy used <see cref="SocketAsyncEventArgs" /> instances that cannot be reused by the <see cref="ArgsPool" />. This + /// method should deregister <see cref="HandleIoCompleted" /> as an event handler for the <see cref="SocketAsyncEventArgs.Completed" /> event. /// </summary> /// <param name="remoteConnectionArgs"> /// The <see cref="SocketAsyncEventArgs" /> which should be destroyed. diff --git a/NetSharp/NetSharp/Sockets/Stream/StreamSocketClient.cs b/NetSharp/NetSharp/Sockets/Stream/StreamSocketClient.cs @@ -45,7 +45,7 @@ namespace NetSharp.Sockets.Stream private readonly StreamSocketClientOptions clientOptions; public StreamSocketClient(ref Socket rawConnection, in StreamSocketClientOptions? clientOptions = null) - : base(ref rawConnection, + : base(ref rawConnection, NetworkPacket.TotalSize, clientOptions?.PreallocatedTransmissionArgs ?? StreamSocketClientOptions.Defaults.PreallocatedTransmissionArgs) { @@ -216,7 +216,7 @@ namespace NetSharp.Sockets.Stream } /// <inheritdoc /> - protected override bool CanTransmissionArgsBeReused(in SocketAsyncEventArgs args) + protected override bool CanTransmissionArgsBeReused(ref SocketAsyncEventArgs args) { return true; } diff --git a/NetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs b/NetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs @@ -237,7 +237,7 @@ namespace NetSharp.Sockets.Stream } /// <inheritdoc /> - protected override bool CanTransmissionArgsBeReused(in SocketAsyncEventArgs args) + protected override bool CanTransmissionArgsBeReused(ref SocketAsyncEventArgs args) { return true; } diff --git a/NetSharp/NetSharp/StreamNetworkConnection.cs b/NetSharp/NetSharp/StreamNetworkConnection.cs @@ -0,0 +1,645 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace NetSharp +{ + public sealed class StreamNetworkReader : NetworkReaderBase<SocketAsyncEventArgs> + { + /// <inheritdoc /> + public StreamNetworkReader(ref Socket rawConnection, NetworkRequestHandler? requestHandler, EndPoint defaultEndPoint, int maxPooledBufferSize, + int maxPooledBuffersPerBucket = 1000, uint preallocatedStateObjects = 0) : base(ref rawConnection, defaultEndPoint, requestHandler, maxPooledBufferSize, + maxPooledBuffersPerBucket, preallocatedStateObjects) + { + } + + private void CloseClientConnection(SocketAsyncEventArgs args) + { + TransmissionToken token = (TransmissionToken)args.UserToken; + + byte[] rentedBuffer = args.Buffer; + BufferPool.Return(rentedBuffer, true); + + Socket clientSocket = args.AcceptSocket; + + clientSocket.Shutdown(SocketShutdown.Both); + clientSocket.Close(); + clientSocket.Dispose(); + + StateObjectPool.Return(args); + } + + private void CompleteAccept(SocketAsyncEventArgs args) + { + switch (args.SocketError) + { + case SocketError.Success: + StartReceive(args); + break; + + default: + StateObjectPool.Return(args); + break; + } + } + + private void CompleteReceive(SocketAsyncEventArgs args) + { + TransmissionToken token = (TransmissionToken)args.UserToken; + + byte[] receiveBuffer = args.Buffer; + int expectedBytes = receiveBuffer.Length; + + switch (args.SocketError) + { + case SocketError.Success: + int receivedBytes = args.BytesTransferred, totalReceivedBytes = token.BytesTransferred; + + if (receivedBytes == 0) // connection is dead + { + CloseClientConnection(args); + } + else if (0 < totalReceivedBytes + receivedBytes && totalReceivedBytes + receivedBytes < expectedBytes) // transmission not complete + { + token = new TransmissionToken(in token, args.BytesTransferred); + args.UserToken = token; + + args.SetBuffer(totalReceivedBytes, expectedBytes - receivedBytes); + + ContinueReceive(args); + } + else if (totalReceivedBytes + receivedBytes == expectedBytes) // transmission complete + { + byte[] responseBufferHandle = BufferPool.Rent(expectedBytes); + + bool responseExists = + RequestHandler(args.RemoteEndPoint, receiveBuffer, responseBufferHandle); + BufferPool.Return(receiveBuffer, true); + + if (responseExists) + { + args.SetBuffer(responseBufferHandle, 0, BufferSize); + + TransmissionToken sendToken = new TransmissionToken(0); + args.UserToken = sendToken; + + StartSend(args); + return; + } + + BufferPool.Return(responseBufferHandle, true); + + StartReceive(args); + } + break; + + default: + CloseClientConnection(args); + break; + } + } + + private void CompleteSend(SocketAsyncEventArgs args) + { + TransmissionToken token = (TransmissionToken)args.UserToken; + + byte[] sendBuffer = args.Buffer; + int expectedBytes = sendBuffer.Length; + + switch (args.SocketError) + { + case SocketError.Success: + int sentBytes = args.BytesTransferred, totalSentBytes = token.BytesTransferred; + + if (sentBytes == 0) // connection is dead + { + CloseClientConnection(args); + } + else if (0 < totalSentBytes + sentBytes && totalSentBytes + sentBytes < expectedBytes) // transmission not complete + { + token = new TransmissionToken(in token, args.BytesTransferred); + args.UserToken = token; + + args.SetBuffer(totalSentBytes, expectedBytes - sentBytes); + + ContinueSend(args); + } + else if (totalSentBytes + sentBytes == expectedBytes) // transmission complete + { + BufferPool.Return(sendBuffer, true); + + StartReceive(args); + } + break; + + default: + CloseClientConnection(args); + break; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ContinueReceive(SocketAsyncEventArgs args) + { + if (ShutdownToken.IsCancellationRequested) + { + CloseClientConnection(args); + return; + } + + Socket clientSocket = args.AcceptSocket; + + if (clientSocket.ReceiveAsync(args)) return; + + CompleteReceive(args); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ContinueSend(SocketAsyncEventArgs args) + { + if (ShutdownToken.IsCancellationRequested) + { + CloseClientConnection(args); + return; + } + + Socket clientSocket = args.AcceptSocket; + + if (clientSocket.SendAsync(args)) return; + + CompleteSend(args); + } + + private void HandleIoCompleted(object sender, SocketAsyncEventArgs args) + { + switch (args.LastOperation) + { + case SocketAsyncOperation.Accept: + StartDefaultAccept(); + CompleteAccept(args); + break; + + case SocketAsyncOperation.Send: + CompleteSend(args); + break; + + case SocketAsyncOperation.Receive: + CompleteReceive(args); + break; + } + } + + private void StartAccept(SocketAsyncEventArgs args) + { + if (ShutdownToken.IsCancellationRequested) + { + return; + } + + if (Connection.AcceptAsync(args)) return; + + StartDefaultAccept(); + CompleteAccept(args); + } + + private void StartDefaultAccept() + { + if (ShutdownToken.IsCancellationRequested) + { + return; + } + + SocketAsyncEventArgs args = StateObjectPool.Rent(); + StartAccept(args); + } + + private void StartReceive(SocketAsyncEventArgs args) + { + if (ShutdownToken.IsCancellationRequested) + { + CloseClientConnection(args); + return; + } + + Socket clientSocket = args.AcceptSocket; + + byte[] receiveBuffer = BufferPool.Rent(BufferSize); + + args.SetBuffer(receiveBuffer, 0, BufferSize); + + TransmissionToken token = new TransmissionToken(0); + args.UserToken = token; + + if (clientSocket.ReceiveAsync(args)) return; + + CompleteReceive(args); + } + + private void StartSend(SocketAsyncEventArgs args) + { + if (ShutdownToken.IsCancellationRequested) + { + CloseClientConnection(args); + return; + } + + Socket clientSocket = args.AcceptSocket; + + if (clientSocket.SendAsync(args)) return; + + CompleteSend(args); + } + + /// <inheritdoc /> + protected override bool CanReuseStateObject(ref SocketAsyncEventArgs instance) + { + return true; + } + + /// <inheritdoc /> + protected override SocketAsyncEventArgs CreateStateObject() + { + SocketAsyncEventArgs args = new SocketAsyncEventArgs(); + args.Completed += HandleIoCompleted; + + return args; + } + + /// <inheritdoc /> + protected override void DestroyStateObject(SocketAsyncEventArgs instance) + { + instance.Completed -= HandleIoCompleted; + instance.Dispose(); + } + + /// <inheritdoc /> + protected override void ResetStateObject(ref SocketAsyncEventArgs instance) + { + } + + /// <inheritdoc /> + public override void Start(ushort concurrentReadTasks) + { + for (ushort i = 0; i < concurrentReadTasks; i++) + { + StartDefaultAccept(); + } + } + + private readonly struct TransmissionToken + { + public readonly int BytesTransferred; + + public TransmissionToken(int bytesTransferred) + { + BytesTransferred = bytesTransferred; + } + + public TransmissionToken(in TransmissionToken token, int newlyTransferredBytes) + { + BytesTransferred = token.BytesTransferred + newlyTransferredBytes; + } + } + } + + public sealed class StreamNetworkWriter : NetworkWriterBase<SocketAsyncEventArgs> + { + /// <inheritdoc /> + public StreamNetworkWriter(ref Socket rawConnection, EndPoint defaultEndPoint, int maxPooledBufferSize, int maxPooledBuffersPerBucket = 1000, + uint preallocatedStateObjects = 0) : base(ref rawConnection, defaultEndPoint, maxPooledBufferSize, maxPooledBuffersPerBucket, preallocatedStateObjects) + { + } + + private void CompleteConnect(SocketAsyncEventArgs args) + { + throw new NotImplementedException(); + } + + private void CompleteDisconnect(SocketAsyncEventArgs args) + { + throw new NotImplementedException(); + } + + private void CompleteReceive(SocketAsyncEventArgs args) + { + AsyncStreamReadToken token = (AsyncStreamReadToken)args.UserToken; + + byte[] receiveBufferHandle = args.Buffer; + int expectedBytes = receiveBufferHandle.Length; + + switch (args.SocketError) + { + case SocketError.Success: + int receivedBytes = args.BytesTransferred, totalReceivedBytes = token.TotalReadBytes; + + if (receivedBytes == 0) // connection is dead + { + token.CompletionSource.SetException(new SocketException((int)SocketError.HostDown)); + } + else if (0 < totalReceivedBytes + receivedBytes && totalReceivedBytes + receivedBytes < expectedBytes) // transmission not complete + { + // update user token to take account of newly read bytes + token = new AsyncStreamReadToken(in token, receivedBytes); + args.UserToken = token; + + args.SetBuffer(totalReceivedBytes, expectedBytes - receivedBytes); + + ContinueReceive(args); + return; + } + else if (totalReceivedBytes + receivedBytes == expectedBytes) // transmission complete + { + receiveBufferHandle.CopyTo(token.UserBuffer); + token.CompletionSource.SetResult(args.BytesTransferred); + } + break; + + case SocketError.OperationAborted: + token.CompletionSource.SetCanceled(); + break; + + default: + int errorCode = (int)args.SocketError; + token.CompletionSource.SetException(new SocketException(errorCode)); + break; + } + + BufferPool.Return(receiveBufferHandle, true); + StateObjectPool.Return(args); + } + + private void CompleteSend(SocketAsyncEventArgs args) + { + AsyncStreamWriteToken token = (AsyncStreamWriteToken)args.UserToken; + + byte[] sendBufferHandle = args.Buffer; + int expectedBytes = sendBufferHandle.Length; + + switch (args.SocketError) + { + case SocketError.Success: + int sentBytes = args.BytesTransferred, totalSentBytes = token.TotalWrittenBytes; + + if (sentBytes == 0) // connection is dead + { + token.CompletionSource.SetException(new SocketException((int)SocketError.HostDown)); + } + else if (0 < totalSentBytes + sentBytes && totalSentBytes + sentBytes < expectedBytes) // transmission not complete + { + // update user token to take account of newly written bytes + token = new AsyncStreamWriteToken(in token, sentBytes); + args.UserToken = token; + + args.SetBuffer(totalSentBytes, expectedBytes - sentBytes); + + ContinueSend(args); + return; + } + else if (totalSentBytes + sentBytes == expectedBytes) // transmission complete + { + token.CompletionSource.SetResult(args.BytesTransferred); + } + break; + + case SocketError.OperationAborted: + token.CompletionSource.SetCanceled(); + break; + + default: + int errorCode = (int)args.SocketError; + token.CompletionSource.SetException(new SocketException(errorCode)); + break; + } + + BufferPool.Return(sendBufferHandle, true); + StateObjectPool.Return(args); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ContinueReceive(SocketAsyncEventArgs args) + { + if (Connection.ReceiveAsync(args)) return; + + CompleteReceive(args); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ContinueSend(SocketAsyncEventArgs args) + { + if (Connection.SendAsync(args)) return; + + CompleteSend(args); + } + + private void HandleIoCompleted(object sender, SocketAsyncEventArgs args) + { + switch (args.LastOperation) + { + case SocketAsyncOperation.Connect: + CompleteConnect(args); + break; + + case SocketAsyncOperation.Disconnect: + CompleteDisconnect(args); + break; + + case SocketAsyncOperation.Receive: + CompleteReceive(args); + break; + + case SocketAsyncOperation.Send: + CompleteSend(args); + break; + } + } + + /// <inheritdoc /> + protected override bool CanReuseStateObject(ref SocketAsyncEventArgs instance) + { + return true; + } + + /// <inheritdoc /> + protected override SocketAsyncEventArgs CreateStateObject() + { + SocketAsyncEventArgs args = new SocketAsyncEventArgs(); + args.Completed += HandleIoCompleted; + + return args; + } + + /// <inheritdoc /> + protected override void DestroyStateObject(SocketAsyncEventArgs instance) + { + instance.Completed -= HandleIoCompleted; + instance.Dispose(); + } + + /// <inheritdoc /> + protected override void ResetStateObject(ref SocketAsyncEventArgs instance) + { + } + + /// <inheritdoc /> + public override int Read(ref EndPoint remoteEndPoint, Memory<byte> readBuffer, SocketFlags flags = SocketFlags.None) + { + int totalBytes = readBuffer.Length; + if (totalBytes > BufferSize) + { + throw new ArgumentException( + $"Cannot rent a temporary buffer of size: {totalBytes} bytes; maximum temporary buffer size: {BufferSize} bytes", + nameof(readBuffer.Length) + ); + } + + int readBytes = 0; + + byte[] transmissionBuffer = BufferPool.Rent(totalBytes); + + do + { + readBytes += Connection.Receive(transmissionBuffer, readBytes, totalBytes - readBytes, flags); + } while (readBytes < totalBytes && readBytes != 0); + + transmissionBuffer.CopyTo(readBuffer); + BufferPool.Return(transmissionBuffer, true); + + return readBytes; + } + + /// <inheritdoc /> + public override ValueTask<int> ReadAsync(EndPoint remoteEndPoint, Memory<byte> readBuffer, SocketFlags flags = SocketFlags.None) + { + int totalBytes = readBuffer.Length; + if (totalBytes > BufferSize) + { + throw new ArgumentException( + $"Cannot rent a temporary buffer of size: {totalBytes} bytes; maximum temporary buffer size: {BufferSize} bytes", + nameof(readBuffer.Length) + ); + } + + TaskCompletionSource<int> tcs = new TaskCompletionSource<int>(); + SocketAsyncEventArgs args = StateObjectPool.Rent(); + + byte[] transmissionBuffer = BufferPool.Rent(totalBytes); + + args.SetBuffer(transmissionBuffer, 0, BufferSize); + + args.RemoteEndPoint = remoteEndPoint; + args.SocketFlags = flags; + + AsyncStreamReadToken token = new AsyncStreamReadToken(tcs, 0, in readBuffer); + args.UserToken = token; + + if (!Connection.ReceiveAsync(args)) CompleteReceive(args); + + return new ValueTask<int>(tcs.Task); + } + + /// <inheritdoc /> + public override int Write(EndPoint remoteEndPoint, ReadOnlyMemory<byte> writeBuffer, SocketFlags flags = SocketFlags.None) + { + int totalBytes = writeBuffer.Length; + if (totalBytes > BufferSize) + { + throw new ArgumentException( + $"Cannot rent a temporary buffer of size: {totalBytes} bytes; maximum temporary buffer size: {BufferSize} bytes", + nameof(writeBuffer.Length) + ); + } + + int writtenBytes = 0; + + byte[] transmissionBuffer = BufferPool.Rent(totalBytes); + writeBuffer.CopyTo(transmissionBuffer); + + do + { + writtenBytes += Connection.Send(transmissionBuffer, writtenBytes, totalBytes - writtenBytes, flags); + } while (writtenBytes < totalBytes && writtenBytes != 0); + + BufferPool.Return(transmissionBuffer); + + return writtenBytes; + } + + /// <inheritdoc /> + public override ValueTask<int> WriteAsync(EndPoint remoteEndPoint, ReadOnlyMemory<byte> writeBuffer, SocketFlags flags = SocketFlags.None) + { + int totalBytes = writeBuffer.Length; + if (totalBytes > BufferSize) + { + throw new ArgumentException( + $"Cannot rent a temporary buffer of size: {totalBytes} bytes; maximum temporary buffer size: {BufferSize} bytes", + nameof(writeBuffer.Length) + ); + } + + TaskCompletionSource<int> tcs = new TaskCompletionSource<int>(); + SocketAsyncEventArgs args = StateObjectPool.Rent(); + + byte[] transmissionBuffer = BufferPool.Rent(totalBytes); + writeBuffer.CopyTo(transmissionBuffer); + + args.SetBuffer(transmissionBuffer, 0, BufferSize); + + args.RemoteEndPoint = remoteEndPoint; + args.SocketFlags = flags; + + AsyncStreamWriteToken token = new AsyncStreamWriteToken(tcs, 0); + args.UserToken = token; + + if (!Connection.SendAsync(args)) CompleteSend(args); + + return new ValueTask<int>(tcs.Task); + } + + private readonly struct AsyncStreamReadToken + { + public readonly TaskCompletionSource<int> CompletionSource; + public readonly int TotalReadBytes; + public readonly Memory<byte> UserBuffer; + + public AsyncStreamReadToken(TaskCompletionSource<int> completionSource, int totalReadBytes, in Memory<byte> userBuffer) + { + CompletionSource = completionSource; + + TotalReadBytes = totalReadBytes; + + UserBuffer = userBuffer; + } + + public AsyncStreamReadToken(in AsyncStreamReadToken previousToken, int newlyReadBytes) + { + CompletionSource = previousToken.CompletionSource; + + TotalReadBytes = previousToken.TotalReadBytes + newlyReadBytes; + + UserBuffer = previousToken.UserBuffer; + } + } + + private readonly struct AsyncStreamWriteToken + { + public readonly TaskCompletionSource<int> CompletionSource; + public readonly int TotalWrittenBytes; + + public AsyncStreamWriteToken(TaskCompletionSource<int> completionSource, int totalWrittenBytes) + { + CompletionSource = completionSource; + + TotalWrittenBytes = totalWrittenBytes; + } + + public AsyncStreamWriteToken(in AsyncStreamWriteToken previousToken, int newlyWrittenBytes) + { + CompletionSource = previousToken.CompletionSource; + + TotalWrittenBytes = previousToken.TotalWrittenBytes + newlyWrittenBytes; + } + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Utils/SlimObjectPool.cs b/NetSharp/NetSharp/Utils/SlimObjectPool.cs @@ -85,7 +85,7 @@ namespace NetSharp.Utils /// <returns> /// Whether the given instance should be placed back into the pool. /// </returns> - public delegate bool CanRebufferObjectPredicate(in T instance); + public delegate bool CanRebufferObjectPredicate(ref T instance); /// <summary> /// Delegate method for creating fresh <typeparamref name="T" /> instances to be stored in the pool. @@ -139,7 +139,7 @@ namespace NetSharp.Utils /// </param> public void Return(T instance) { - if (canObjectBeRebufferedPredicate(instance)) + if (canObjectBeRebufferedPredicate(ref instance)) { resetObjectDelegate(ref instance); diff --git a/NetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkReaderBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkReaderBenchmark.cs @@ -0,0 +1,111 @@ +using NetSharp; + +using System; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks +{ + public class DatagramNetworkReaderBenchmark : INetSharpExample, INetSharpBenchmark + { + private const int PacketSize = 8192, PacketCount = 100_000, ClientCount = 12; + + private double[] ClientBandwidths; + public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); + + public static readonly Encoding ServerEncoding = Encoding.UTF8; + public static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12370); + + /// <inheritdoc /> + public string Name { get; } = "Datagram Network Reader Benchmark"; + + private static bool RequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, Memory<byte> responseBuffer) + { + return requestBuffer.TryCopyTo(responseBuffer); + } + + private Task BenchmarkClientTask(object idObj) + { + try + { + int id = (int)idObj; + + BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); + + Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + clientSocket.Bind(ClientEndPoint); + + byte[] sendBuffer = new byte[PacketSize]; + byte[] receiveBuffer = new byte[PacketSize]; + + EndPoint remoteEndPoint = ServerEndPoint; + + lock (typeof(Console)) + { + Console.WriteLine($"[Client {id}] Starting client; sending messages to {remoteEndPoint}"); + } + + for (int i = 0; i < PacketCount; i++) + { + byte[] packetBuffer = ServerEncoding.GetBytes($"[Client {id}] Hello World! (Packet {i})"); + packetBuffer.CopyTo(sendBuffer, 0); + + benchmarkHelper.StartStopwatch(); + int sentBytes = clientSocket.SendTo(sendBuffer, remoteEndPoint); + + int receivedBytes = clientSocket.ReceiveFrom(receiveBuffer, ref remoteEndPoint); + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); + } + + benchmarkHelper.PrintBandwidthStats(id, PacketCount, PacketSize); + benchmarkHelper.PrintRttStats(id); + + ClientBandwidths[id] = benchmarkHelper.CalcBandwidth(PacketCount, PacketSize); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public async Task RunAsync() + { + if (PacketCount > 10_000) + { + Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); + } + + EndPoint defaultRemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + rawSocket.Bind(ServerEndPoint); + + using DatagramNetworkReader reader = new DatagramNetworkReader(ref rawSocket, RequestHandler, defaultRemoteEndPoint, PacketSize); + reader.Start(ClientCount); + + ClientBandwidths = new double[ClientCount]; + Task[] clientTasks = new Task[ClientCount]; + for (int i = 0; i < clientTasks.Length; i++) + { + clientTasks[i] = Task.Factory.StartNew(BenchmarkClientTask, i, TaskCreationOptions.LongRunning); + } + + await Task.WhenAll(clientTasks); + + Console.WriteLine($"Total estimated bandwidth: {ClientBandwidths.Sum():F5}"); + + reader.Stop(); + + rawSocket.Close(); + rawSocket.Dispose(); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkWriterAsyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkWriterAsyncBenchmark.cs @@ -0,0 +1,105 @@ +using NetSharp; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks +{ + public class DatagramNetworkWriterAsyncBenchmark : INetSharpExample, INetSharpBenchmark + { + private const int PacketSize = 8192, PacketCount = 100_000; + + public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); + + public static readonly Encoding ServerEncoding = Encoding.UTF8; + public static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12372); + + public static readonly ManualResetEventSlim ServerReadyEvent = new ManualResetEventSlim(); + + /// <inheritdoc /> + public string Name { get; } = "Datagram Network Writer Benchmark (Asynchronous)"; + + private static Task ServerTask(CancellationToken cancellationToken) + { + using Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + + server.Bind(ServerEndPoint); + ServerReadyEvent.Set(); + + byte[] transmissionBuffer = new byte[PacketSize]; + + EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + + while (!cancellationToken.IsCancellationRequested) + { + server.ReceiveFrom(transmissionBuffer, ref remoteEndPoint); + + server.SendTo(transmissionBuffer, remoteEndPoint); + } + + server.Close(); + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public async Task RunAsync() + { + if (PacketCount > 10_000) + { + Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); + } + + EndPoint defaultRemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + rawSocket.Bind(ClientEndPoint); + + using DatagramNetworkWriter writer = new DatagramNetworkWriter(ref rawSocket, defaultRemoteEndPoint, PacketSize); + + using CancellationTokenSource serverCts = new CancellationTokenSource(); + Task serverTask = Task.Factory.StartNew(state => ServerTask((CancellationToken)state), serverCts.Token, TaskCreationOptions.LongRunning); + + ServerReadyEvent.Wait(); + + BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); + + byte[] sendBuffer = new byte[PacketSize]; + byte[] receiveBuffer = new byte[PacketSize]; + + for (int i = 0; i < PacketCount; i++) + { + byte[] packetBuffer = ServerEncoding.GetBytes($"[Client 0] Hello World! (Packet {i})"); + packetBuffer.CopyTo(sendBuffer, 0); + + benchmarkHelper.StartStopwatch(); + int sendResult = await writer.WriteAsync(ServerEndPoint, sendBuffer); + + int receiveResult = await writer.ReadAsync(ServerEndPoint, receiveBuffer); + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); + } + + benchmarkHelper.PrintBandwidthStats(0, PacketCount, PacketSize); + benchmarkHelper.PrintRttStats(0); + + serverCts.Cancel(); + try + { + serverTask.Dispose(); + } + catch (Exception) + { + // ignored + } + + rawSocket.Close(); + rawSocket.Dispose(); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkWriterSyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkWriterSyncBenchmark.cs @@ -0,0 +1,101 @@ +using NetSharp; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks +{ + public class DatagramNetworkWriterSyncBenchmark : INetSharpExample, INetSharpBenchmark + { + private const int PacketSize = 8192, PacketCount = 100_000; + + public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); + + public static readonly Encoding ServerEncoding = Encoding.UTF8; + public static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12371); + + public static readonly ManualResetEventSlim ServerReadyEvent = new ManualResetEventSlim(); + + /// <inheritdoc /> + public string Name { get; } = "Datagram Network Writer Benchmark (Synchronous)"; + + private static Task ServerTask(CancellationToken cancellationToken) + { + using Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + + server.Bind(ServerEndPoint); + ServerReadyEvent.Set(); + + byte[] transmissionBuffer = new byte[PacketSize]; + + EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + + while (!cancellationToken.IsCancellationRequested) + { + int received = server.ReceiveFrom(transmissionBuffer, ref remoteEndPoint); + + int sent = server.SendTo(transmissionBuffer, remoteEndPoint); + } + + server.Close(); + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public Task RunAsync() + { + if (PacketCount > 10_000) + { + Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); + } + + EndPoint defaultRemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + rawSocket.Bind(ClientEndPoint); + + using DatagramNetworkWriter writer = new DatagramNetworkWriter(ref rawSocket, defaultRemoteEndPoint, PacketSize); + + using CancellationTokenSource serverCts = new CancellationTokenSource(); + Task serverTask = Task.Factory.StartNew(state => ServerTask((CancellationToken)state), serverCts.Token, TaskCreationOptions.LongRunning); + + ServerReadyEvent.Wait(); + + BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); + + byte[] sendBuffer = new byte[PacketSize]; + byte[] receiveBuffer = new byte[PacketSize]; + + EndPoint remoteEndPoint = ServerEndPoint; + + for (int i = 0; i < PacketCount; i++) + { + byte[] packetBuffer = ServerEncoding.GetBytes($"[Client 0] Hello World! (Packet {i})"); + packetBuffer.CopyTo(sendBuffer, 0); + + benchmarkHelper.StartStopwatch(); + int sendResult = writer.Write(ServerEndPoint, sendBuffer); + + int receiveResult = writer.Read(ref remoteEndPoint, receiveBuffer); + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); + } + + benchmarkHelper.PrintBandwidthStats(0, PacketCount, PacketSize); + benchmarkHelper.PrintRttStats(0); + + serverCts.Cancel(); + + rawSocket.Close(); + rawSocket.Dispose(); + + return Task.CompletedTask; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/DatagramNetworkReaderBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/DatagramNetworkReaderBenchmark.cs @@ -1,109 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using NetSharp; - -namespace NetSharpExamples.Benchmarks -{ - public class DatagramNetworkReaderBenchmark : INetSharpExample - { - private const int PacketSize = 8192, PacketCount = 1_000_000, ClientCount = 12; - - public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); - - public static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12349); - - public static readonly Encoding ServerEncoding = Encoding.UTF8; - - private double[] ClientBandwidths; - - /// <inheritdoc /> - public string Name { get; } = "Datagram Network Reader Benchmark"; - - private Task BenchmarkClientTask(object idObj) - { - try - { - int id = (int)idObj; - - BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); - - Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - clientSocket.Bind(ClientEndPoint); - - byte[] sendBuffer = new byte[PacketSize]; - byte[] receiveBuffer = new byte[PacketSize]; - - EndPoint remoteEndPoint = ServerEndPoint; - - lock (typeof(Console)) - { - Console.WriteLine($"[Client {id}] Starting client; sending messages to {remoteEndPoint}"); - } - - for (int i = 0; i < PacketCount; i++) - { - byte[] packetBuffer = ServerEncoding.GetBytes($"[Client {id}] Hello World! (Packet {i})"); - packetBuffer.CopyTo(sendBuffer, 0); - - benchmarkHelper.StartStopwatch(); - int sentBytes = clientSocket.SendTo(sendBuffer, remoteEndPoint); - - int receivedBytes = clientSocket.ReceiveFrom(receiveBuffer, ref remoteEndPoint); - benchmarkHelper.StopStopwatch(); - - benchmarkHelper.SnapshotRttStats(); - } - - benchmarkHelper.PrintBandwidthStats(id, PacketCount, PacketSize); - benchmarkHelper.PrintRttStats(id); - - ClientBandwidths[id] = benchmarkHelper.CalcBandwidth(PacketCount, PacketSize); - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - - return Task.CompletedTask; - } - - /// <inheritdoc /> - public async Task RunAsync() - { - EndPoint defaultRemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); - - Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - rawSocket.Bind(ServerEndPoint); - using DatagramNetworkReader reader = new DatagramNetworkReader(ref rawSocket, RequestHandler, defaultRemoteEndPoint, PacketSize); - reader.Start(ClientCount); - - ClientBandwidths = new double[ClientCount]; - Task[] clientTasks = new Task[ClientCount]; - for (int i = 0; i < clientTasks.Length; i++) - { - clientTasks[i] = Task.Factory.StartNew(BenchmarkClientTask, i, TaskCreationOptions.LongRunning); - } - - await Task.WhenAll(clientTasks); - - Console.WriteLine($"Total estimated bandwidth: {ClientBandwidths.Sum():F5}"); - - reader.Stop(); - - rawSocket.Close(); - rawSocket.Dispose(); - - Console.WriteLine($"UDP Server Benchmark finished!"); - } - - private static bool RequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, Memory<byte> responseBuffer) - { - return requestBuffer.TryCopyTo(responseBuffer); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkReaderBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkReaderBenchmark.cs @@ -0,0 +1,132 @@ +using NetSharp; + +using System; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks +{ + public class StreamNetworkReaderBenchmark : INetSharpExample, INetSharpBenchmark + { + private const int PacketSize = 8192, PacketCount = 100_000, ClientCount = 12; + + private double[] ClientBandwidths; + public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); + + public static readonly Encoding ServerEncoding = Encoding.UTF8; + public static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12373); + + /// <inheritdoc /> + public string Name { get; } = "Stream Network Reader Benchmark"; + + private static bool RequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, Memory<byte> responseBuffer) + { + return requestBuffer.TryCopyTo(responseBuffer); + } + + private Task BenchmarkClientTask(object idObj) + { + int id = (int)idObj; + + BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); + + Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + clientSocket.Bind(ClientEndPoint); + clientSocket.Connect(ServerEndPoint); + + byte[] sendBuffer = new byte[PacketSize]; + byte[] receiveBuffer = new byte[PacketSize]; + + EndPoint remoteEndPoint = ServerEndPoint; + + lock (typeof(Console)) + { + Console.WriteLine($"[Client {id}] Starting client; sending messages to {remoteEndPoint}"); + } + + for (int i = 0; i < PacketCount; i++) + { + byte[] packetBuffer = ServerEncoding.GetBytes($"[Client {id}] Hello World! (Packet {i})"); + packetBuffer.CopyTo(sendBuffer, 0); + + benchmarkHelper.StartStopwatch(); + + int totalSent = 0; + do + { + totalSent += clientSocket.Send(sendBuffer, totalSent, sendBuffer.Length - totalSent, + SocketFlags.None); + } while (totalSent != 0 && totalSent != sendBuffer.Length); + + if (totalSent == 0) + { + break; + } + + int totalReceived = 0; + do + { + totalReceived += clientSocket.Receive(receiveBuffer, totalReceived, + receiveBuffer.Length - totalReceived, SocketFlags.None); + } while (totalReceived != 0 && totalReceived != sendBuffer.Length); + + if (totalReceived == 0) + { + break; + } + + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); + } + + clientSocket.Disconnect(true); + clientSocket.Close(); + + benchmarkHelper.PrintBandwidthStats(id, PacketCount, PacketSize); + benchmarkHelper.PrintRttStats(id); + + ClientBandwidths[id] = benchmarkHelper.CalcBandwidth(PacketCount, PacketSize); + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public async Task RunAsync() + { + if (PacketCount > 10_000) + { + Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); + } + + EndPoint defaultEndPoint = new IPEndPoint(IPAddress.Any, 0); + + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + rawSocket.Bind(ServerEndPoint); + rawSocket.Listen(ClientCount); + + using StreamNetworkReader reader = new StreamNetworkReader(ref rawSocket, RequestHandler, defaultEndPoint, PacketSize); + reader.Start(ClientCount); + + ClientBandwidths = new double[ClientCount]; + Task[] clientTasks = new Task[ClientCount]; + for (int i = 0; i < clientTasks.Length; i++) + { + clientTasks[i] = Task.Factory.StartNew(BenchmarkClientTask, i, TaskCreationOptions.LongRunning); + } + + await Task.WhenAll(clientTasks); + + Console.WriteLine($"Total estimated bandwidth: {ClientBandwidths.Sum():F5}"); + + reader.Stop(); + + rawSocket.Close(); + rawSocket.Dispose(); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkWriterAsyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkWriterAsyncBenchmark.cs @@ -0,0 +1,130 @@ +using NetSharp; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks +{ + public class StreamNetworkWriterAsyncBenchmark : INetSharpExample, INetSharpBenchmark + { + private const int PacketSize = 8192, PacketCount = 100_000; + + public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); + + public static readonly Encoding ServerEncoding = Encoding.UTF8; + public static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12375); + + public static readonly ManualResetEventSlim ServerReadyEvent = new ManualResetEventSlim(); + + /// <inheritdoc /> + public string Name { get; } = "Stream Network Writer Benchmark (Asynchronous)"; + + private static Task ServerTask(CancellationToken cancellationToken) + { + using Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + server.Bind(ServerEndPoint); + ServerReadyEvent.Set(); + + byte[] transmissionBuffer = new byte[PacketSize]; + + server.Listen(1); + Socket clientSocket = server.Accept(); + + while (!cancellationToken.IsCancellationRequested) + { + int expectedBytes = transmissionBuffer.Length; + + int receivedBytes = 0; + do + { + receivedBytes += clientSocket.Receive(transmissionBuffer, receivedBytes, expectedBytes - receivedBytes, SocketFlags.None); + } while (receivedBytes != 0 && receivedBytes < expectedBytes); + + if (receivedBytes == 0) + { + break; + } + + int sentBytes = 0; + do + { + sentBytes += clientSocket.Send(transmissionBuffer, sentBytes, expectedBytes - sentBytes, SocketFlags.None); + } while (sentBytes != 0 && sentBytes < expectedBytes); + + if (sentBytes == 0) + { + break; + } + } + + server.Shutdown(SocketShutdown.Both); + server.Close(); + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public async Task RunAsync() + { + if (PacketCount > 10_000) + { + Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); + } + + EndPoint defaultRemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + rawSocket.Bind(ClientEndPoint); + + using StreamNetworkWriter writer = new StreamNetworkWriter(ref rawSocket, defaultRemoteEndPoint, PacketSize); + + using CancellationTokenSource serverCts = new CancellationTokenSource(); + Task serverTask = Task.Factory.StartNew(state => ServerTask((CancellationToken)state), serverCts.Token, TaskCreationOptions.LongRunning); + + ServerReadyEvent.Wait(); + rawSocket.Connect(ServerEndPoint); + + BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); + + byte[] sendBuffer = new byte[PacketSize]; + byte[] receiveBuffer = new byte[PacketSize]; + + for (int i = 0; i < PacketCount; i++) + { + byte[] packetBuffer = ServerEncoding.GetBytes($"[Client 0] Hello World! (Packet {i})"); + packetBuffer.CopyTo(sendBuffer, 0); + + benchmarkHelper.StartStopwatch(); + int sendResult = await writer.WriteAsync(ServerEndPoint, sendBuffer); + + int receiveResult = await writer.ReadAsync(ServerEndPoint, receiveBuffer); + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); + } + + benchmarkHelper.PrintBandwidthStats(0, PacketCount, PacketSize); + benchmarkHelper.PrintRttStats(0); + + serverCts.Cancel(); + try + { + serverTask.Dispose(); + } + catch (Exception) + { + // ignored + } + + rawSocket.Disconnect(false); + rawSocket.Shutdown(SocketShutdown.Both); + rawSocket.Close(); + rawSocket.Dispose(); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkWriterSyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkWriterSyncBenchmark.cs @@ -0,0 +1,134 @@ +using NetSharp; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks +{ + public class StreamNetworkWriterSyncBenchmark : INetSharpExample, INetSharpBenchmark + { + private const int PacketSize = 8192, PacketCount = 100_000; + + public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); + + public static readonly Encoding ServerEncoding = Encoding.UTF8; + public static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12374); + + public static readonly ManualResetEventSlim ServerReadyEvent = new ManualResetEventSlim(); + + /// <inheritdoc /> + public string Name { get; } = "Stream Network Writer Benchmark (Synchronous)"; + + private static Task ServerTask(CancellationToken cancellationToken) + { + using Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + server.Bind(ServerEndPoint); + ServerReadyEvent.Set(); + + byte[] transmissionBuffer = new byte[PacketSize]; + + server.Listen(1); + Socket clientSocket = server.Accept(); + + while (!cancellationToken.IsCancellationRequested) + { + int expectedBytes = transmissionBuffer.Length; + + int receivedBytes = 0; + do + { + receivedBytes += clientSocket.Receive(transmissionBuffer, receivedBytes, expectedBytes - receivedBytes, SocketFlags.None); + } while (receivedBytes != 0 && receivedBytes < expectedBytes); + + if (receivedBytes == 0) + { + break; + } + + int sentBytes = 0; + do + { + sentBytes += clientSocket.Send(transmissionBuffer, sentBytes, expectedBytes - sentBytes, SocketFlags.None); + } while (sentBytes != 0 && sentBytes < expectedBytes); + + if (sentBytes == 0) + { + break; + } + } + + server.Shutdown(SocketShutdown.Both); + server.Close(); + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public Task RunAsync() + { + if (PacketCount > 10_000) + { + Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); + } + + EndPoint defaultRemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + rawSocket.Bind(ClientEndPoint); + + using StreamNetworkWriter writer = new StreamNetworkWriter(ref rawSocket, defaultRemoteEndPoint, PacketSize); + + using CancellationTokenSource serverCts = new CancellationTokenSource(); + Task serverTask = Task.Factory.StartNew(state => ServerTask((CancellationToken)state), serverCts.Token, TaskCreationOptions.LongRunning); + + ServerReadyEvent.Wait(); + rawSocket.Connect(ServerEndPoint); + + BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); + + byte[] sendBuffer = new byte[PacketSize]; + byte[] receiveBuffer = new byte[PacketSize]; + + EndPoint remoteEndPoint = ServerEndPoint; + + for (int i = 0; i < PacketCount; i++) + { + byte[] packetBuffer = ServerEncoding.GetBytes($"[Client 0] Hello World! (Packet {i})"); + packetBuffer.CopyTo(sendBuffer, 0); + + benchmarkHelper.StartStopwatch(); + int sendResult = writer.Write(ServerEndPoint, sendBuffer); + + int receiveResult = writer.Read(ref remoteEndPoint, receiveBuffer); + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); + } + + benchmarkHelper.PrintBandwidthStats(0, PacketCount, PacketSize); + benchmarkHelper.PrintRttStats(0); + + serverCts.Cancel(); + try + { + serverTask.Dispose(); + } + catch (Exception) + { + // ignored + } + + rawSocket.Disconnect(false); + rawSocket.Shutdown(SocketShutdown.Both); + rawSocket.Close(); + rawSocket.Dispose(); + + return Task.CompletedTask; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketClientAsyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketClientAsyncBenchmark.cs @@ -0,0 +1,117 @@ +using NetSharp.Packets; +using NetSharp.Sockets.Stream; +using NetSharp.Utils; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks +{ + public class TcpSocketClientAsyncBenchmark : INetSharpExample + { + /// <summary> + /// Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. + /// </summary> + private const int PacketCount = 1_000_000; + + private static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12368); + + /// <inheritdoc /> + public string Name { get; } = "TCP Socket Client Benchmark (Asynchronous)"; + + private Task ServerTask(CancellationToken cancellationToken) + { + Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + server.Bind(ServerEndPoint); + + byte[] transmissionBuffer = new byte[NetworkPacket.TotalSize]; + + server.Listen(1); + Socket clientSocket = server.Accept(); + + while (!cancellationToken.IsCancellationRequested) + { + int expectedBytes = transmissionBuffer.Length; + + int receivedBytes = 0; + do + { + receivedBytes += clientSocket.Receive(transmissionBuffer, receivedBytes, expectedBytes - receivedBytes, SocketFlags.None); + } while (receivedBytes != 0 && receivedBytes < expectedBytes); + + if (receivedBytes == 0) + { + break; + } + + int sentBytes = 0; + do + { + sentBytes += clientSocket.Send(transmissionBuffer, sentBytes, expectedBytes - sentBytes, SocketFlags.None); + } while (sentBytes != 0 && sentBytes < expectedBytes); + + if (sentBytes == 0) + { + break; + } + } + + server.Shutdown(SocketShutdown.Both); + server.Close(); + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public async Task RunAsync() + { + if (PacketCount > 10_000) + { + Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); + } + + using CancellationTokenSource serverCts = new CancellationTokenSource(); + Task serverTask = Task.Factory.StartNew(state => ServerTask((CancellationToken)state), serverCts.Token, TaskCreationOptions.LongRunning); + + BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); + + StreamSocketClientOptions clientOptions = new StreamSocketClientOptions((ushort)2); + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + using StreamSocketClient client = new StreamSocketClient(ref rawSocket, clientOptions); + + await client.ConnectAsync(in ServerEndPoint); + + byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; + byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; + + for (int i = 0; i < PacketCount; i++) + { + byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client 0] Hello World! (Packet {i})"); + packetBuffer.CopyTo(sendBuffer, 0); + + benchmarkHelper.StartStopwatch(); + TransmissionResult sendResult = await client.SendAsync(in ServerEndPoint, sendBuffer); + + TransmissionResult receiveResult = await client.ReceiveAsync(in ServerEndPoint, receiveBuffer); + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); + } + + benchmarkHelper.PrintBandwidthStats(0, PacketCount, NetworkPacket.TotalSize); + benchmarkHelper.PrintRttStats(0); + + serverCts.Cancel(); + try + { + serverTask.Dispose(); + } + catch (Exception) { } + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketClientSyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketClientSyncBenchmark.cs @@ -0,0 +1,117 @@ +using NetSharp.Packets; +using NetSharp.Sockets.Stream; +using NetSharp.Utils; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks +{ + public class TcpSocketClientSyncBenchmark : INetSharpExample + { + /// <summary> + /// Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. + /// </summary> + private const int PacketCount = 1_000_000; + + private static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12358); + + /// <inheritdoc /> + public string Name { get; } = "TCP Socket Client Benchmark (Synchronous)"; + + private Task ServerTask(CancellationToken cancellationToken) + { + Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + server.Bind(ServerEndPoint); + + byte[] transmissionBuffer = new byte[NetworkPacket.TotalSize]; + + server.Listen(1); + Socket clientSocket = server.Accept(); + + while (!cancellationToken.IsCancellationRequested) + { + int expectedBytes = transmissionBuffer.Length; + + int receivedBytes = 0; + do + { + receivedBytes += clientSocket.Receive(transmissionBuffer, receivedBytes, expectedBytes - receivedBytes, SocketFlags.None); + } while (receivedBytes != 0 && receivedBytes < expectedBytes); + + if (receivedBytes == 0) + { + break; + } + + int sentBytes = 0; + do + { + sentBytes += clientSocket.Send(transmissionBuffer, sentBytes, expectedBytes - sentBytes, SocketFlags.None); + } while (sentBytes != 0 && sentBytes < expectedBytes); + + if (sentBytes == 0) + { + break; + } + } + + server.Shutdown(SocketShutdown.Both); + server.Close(); + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public async Task RunAsync() + { + if (PacketCount > 10_000) + { + Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); + } + + using CancellationTokenSource serverCts = new CancellationTokenSource(); + Task serverTask = Task.Factory.StartNew(state => ServerTask((CancellationToken)state), serverCts.Token, TaskCreationOptions.LongRunning); + + BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); + + StreamSocketClientOptions clientOptions = new StreamSocketClientOptions((ushort)2); + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + using StreamSocketClient client = new StreamSocketClient(ref rawSocket, clientOptions); + + client.Connect(in ServerEndPoint); + + byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; + byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; + + for (int i = 0; i < PacketCount; i++) + { + byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client 0] Hello World! (Packet {i})"); + packetBuffer.CopyTo(sendBuffer, 0); + + benchmarkHelper.StartStopwatch(); + TransmissionResult sendResult = client.Send(in ServerEndPoint, sendBuffer); + + TransmissionResult receiveResult = client.Receive(in ServerEndPoint, receiveBuffer); + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); + } + + benchmarkHelper.PrintBandwidthStats(0, PacketCount, NetworkPacket.TotalSize); + benchmarkHelper.PrintRttStats(0); + + serverCts.Cancel(); + try + { + serverTask.Dispose(); + } + catch (Exception) { } + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketServerBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketServerBenchmark.cs @@ -0,0 +1,136 @@ +using NetSharp.Packets; +using NetSharp.Sockets; +using NetSharp.Sockets.Stream; + +using System; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks +{ + public class TcpSocketServerBenchmark : INetSharpExample + { + /// <summary> + /// Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. + /// </summary> + private const int PacketCount = 1_000_000; + + private static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12348); + + private double[] ClientBandwidths; + + /// <inheritdoc /> + public string Name { get; } = "TCP Socket Server Benchmark"; + + private Task BenchmarkClientTask(object idObj) + { + int id = (int)idObj; + + BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); + + Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + clientSocket.Bind(new IPEndPoint(IPAddress.Any, 0)); + clientSocket.Connect(ServerEndPoint); + + byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; + byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; + + EndPoint remoteEndPoint = ServerEndPoint; + + lock (typeof(Console)) + { + Console.WriteLine($"[Client {id}] Starting client; sending messages to {remoteEndPoint}"); + } + + for (int i = 0; i < PacketCount; i++) + { + byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client {id}] Hello World! (Packet {i})"); + packetBuffer.CopyTo(sendBuffer, 0); + + benchmarkHelper.StartStopwatch(); + + int totalSent = 0; + do + { + totalSent += clientSocket.Send(sendBuffer, totalSent, sendBuffer.Length - totalSent, + SocketFlags.None); + } while (totalSent != 0 && totalSent != sendBuffer.Length); + + if (totalSent == 0) + { + break; + } + + int totalReceived = 0; + do + { + totalReceived += clientSocket.Receive(receiveBuffer, totalReceived, + receiveBuffer.Length - totalReceived, SocketFlags.None); + } while (totalReceived != 0 && totalReceived != sendBuffer.Length); + + if (totalReceived == 0) + { + break; + } + + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); + } + + clientSocket.Disconnect(true); + clientSocket.Close(); + + benchmarkHelper.PrintBandwidthStats(id, PacketCount, NetworkPacket.TotalSize); + benchmarkHelper.PrintRttStats(id); + + ClientBandwidths[id] = benchmarkHelper.CalcBandwidth(PacketCount, NetworkPacket.TotalSize); + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public async Task RunAsync() + { + CancellationTokenSource serverCts = new CancellationTokenSource(); + + int clientCount = Environment.ProcessorCount / 2; + + if (PacketCount > 10_000) + { + Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); + } + + StreamSocketServerOptions serverOptions = new StreamSocketServerOptions(clientCount, (ushort)clientCount); + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + StreamSocketServer server = new StreamSocketServer(ref rawSocket, RawSocketServer.DefaultRawPacketHandler, serverOptions); + + server.Bind(ServerEndPoint); + + Task serverTask = Task.Factory.StartNew(() => + { + server.RunAsync(serverCts.Token).GetAwaiter().GetResult(); + }, TaskCreationOptions.LongRunning); + + ClientBandwidths = new double[clientCount]; + Task[] clientTasks = new Task[clientCount]; + for (int i = 0; i < clientTasks.Length; i++) + { + clientTasks[i] = Task.Factory.StartNew(BenchmarkClientTask, i, TaskCreationOptions.LongRunning); + } + + await Task.WhenAll(clientTasks); + + Console.WriteLine($"Total estimated bandwidth: {ClientBandwidths.Sum():F5}"); + + serverCts.Cancel(); + + await serverTask; + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/TcpSocketClientAsyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/TcpSocketClientAsyncBenchmark.cs @@ -1,121 +0,0 @@ -using NetSharp.Packets; -using NetSharp.Sockets.Stream; -using NetSharp.Utils; - -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharpExamples.Benchmarks -{ - public class TcpSocketClientAsyncBenchmark : INetSharpExample - { - /// <summary> - /// Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. - /// </summary> - private const int PacketCount = 1_000_000; - - private static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12368); - - /// <inheritdoc /> - public string Name { get; } = "TCP Socket Client Benchmark (Asynchronous)"; - - private Task ServerTask(CancellationToken cancellationToken) - { - Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - - server.Bind(ServerEndPoint); - - byte[] transmissionBuffer = new byte[NetworkPacket.TotalSize]; - - server.Listen(1); - Socket clientSocket = server.Accept(); - - while (!cancellationToken.IsCancellationRequested) - { - int expectedBytes = transmissionBuffer.Length; - - int receivedBytes = 0; - do - { - receivedBytes += clientSocket.Receive(transmissionBuffer, receivedBytes, expectedBytes - receivedBytes, SocketFlags.None); - } while (receivedBytes != 0 && receivedBytes < expectedBytes); - - if (receivedBytes == 0) - { - break; - } - - int sentBytes = 0; - do - { - sentBytes += clientSocket.Send(transmissionBuffer, sentBytes, expectedBytes - sentBytes, SocketFlags.None); - } while (sentBytes != 0 && sentBytes < expectedBytes); - - if (sentBytes == 0) - { - break; - } - } - - server.Shutdown(SocketShutdown.Both); - server.Close(); - - return Task.CompletedTask; - } - - /// <inheritdoc /> - public async Task RunAsync() - { - Console.WriteLine($"TCP Client Benchmark started!"); - - if (PacketCount > 10_000) - { - Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); - } - - using CancellationTokenSource serverCts = new CancellationTokenSource(); - Task serverTask = Task.Factory.StartNew(state => ServerTask((CancellationToken)state), serverCts.Token, TaskCreationOptions.LongRunning); - - BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); - - StreamSocketClientOptions clientOptions = new StreamSocketClientOptions((ushort)2); - Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - using StreamSocketClient client = new StreamSocketClient(ref rawSocket, clientOptions); - - await client.ConnectAsync(in ServerEndPoint); - - byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; - byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; - - for (int i = 0; i < PacketCount; i++) - { - byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client 0] Hello World! (Packet {i})"); - packetBuffer.CopyTo(sendBuffer, 0); - - benchmarkHelper.StartStopwatch(); - TransmissionResult sendResult = await client.SendAsync(in ServerEndPoint, sendBuffer); - - TransmissionResult receiveResult = await client.ReceiveAsync(in ServerEndPoint, receiveBuffer); - benchmarkHelper.StopStopwatch(); - - benchmarkHelper.SnapshotRttStats(); - } - - benchmarkHelper.PrintBandwidthStats(0, PacketCount, NetworkPacket.TotalSize); - benchmarkHelper.PrintRttStats(0); - - serverCts.Cancel(); - try - { - serverTask.Dispose(); - } - catch (Exception) { } - - Console.WriteLine($"TCP Client Benchmark finished!"); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/TcpSocketClientSyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/TcpSocketClientSyncBenchmark.cs @@ -1,121 +0,0 @@ -using NetSharp.Packets; -using NetSharp.Sockets.Stream; -using NetSharp.Utils; - -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharpExamples.Benchmarks -{ - public class TcpSocketClientSyncBenchmark : INetSharpExample - { - /// <summary> - /// Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. - /// </summary> - private const int PacketCount = 1_000_000; - - private static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12358); - - /// <inheritdoc /> - public string Name { get; } = "TCP Socket Client Benchmark (Synchronous)"; - - private Task ServerTask(CancellationToken cancellationToken) - { - Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - - server.Bind(ServerEndPoint); - - byte[] transmissionBuffer = new byte[NetworkPacket.TotalSize]; - - server.Listen(1); - Socket clientSocket = server.Accept(); - - while (!cancellationToken.IsCancellationRequested) - { - int expectedBytes = transmissionBuffer.Length; - - int receivedBytes = 0; - do - { - receivedBytes += clientSocket.Receive(transmissionBuffer, receivedBytes, expectedBytes - receivedBytes, SocketFlags.None); - } while (receivedBytes != 0 && receivedBytes < expectedBytes); - - if (receivedBytes == 0) - { - break; - } - - int sentBytes = 0; - do - { - sentBytes += clientSocket.Send(transmissionBuffer, sentBytes, expectedBytes - sentBytes, SocketFlags.None); - } while (sentBytes != 0 && sentBytes < expectedBytes); - - if (sentBytes == 0) - { - break; - } - } - - server.Shutdown(SocketShutdown.Both); - server.Close(); - - return Task.CompletedTask; - } - - /// <inheritdoc /> - public async Task RunAsync() - { - Console.WriteLine($"TCP Client Benchmark started!"); - - if (PacketCount > 10_000) - { - Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); - } - - using CancellationTokenSource serverCts = new CancellationTokenSource(); - Task serverTask = Task.Factory.StartNew(state => ServerTask((CancellationToken)state), serverCts.Token, TaskCreationOptions.LongRunning); - - BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); - - StreamSocketClientOptions clientOptions = new StreamSocketClientOptions((ushort)2); - Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - using StreamSocketClient client = new StreamSocketClient(ref rawSocket, clientOptions); - - client.Connect(in ServerEndPoint); - - byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; - byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; - - for (int i = 0; i < PacketCount; i++) - { - byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client 0] Hello World! (Packet {i})"); - packetBuffer.CopyTo(sendBuffer, 0); - - benchmarkHelper.StartStopwatch(); - TransmissionResult sendResult = client.Send(in ServerEndPoint, sendBuffer); - - TransmissionResult receiveResult = client.Receive(in ServerEndPoint, receiveBuffer); - benchmarkHelper.StopStopwatch(); - - benchmarkHelper.SnapshotRttStats(); - } - - benchmarkHelper.PrintBandwidthStats(0, PacketCount, NetworkPacket.TotalSize); - benchmarkHelper.PrintRttStats(0); - - serverCts.Cancel(); - try - { - serverTask.Dispose(); - } - catch (Exception) { } - - Console.WriteLine($"TCP Client Benchmark finished!"); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/TcpSocketServerBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/TcpSocketServerBenchmark.cs @@ -1,140 +0,0 @@ -using NetSharp.Packets; -using NetSharp.Sockets; -using NetSharp.Sockets.Stream; - -using System; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharpExamples.Benchmarks -{ - public class TcpSocketServerBenchmark : INetSharpExample - { - /// <summary> - /// Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. - /// </summary> - private const int PacketCount = 1_000_000; - - private static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12348); - - private double[] ClientBandwidths; - - /// <inheritdoc /> - public string Name { get; } = "TCP Socket Server Benchmark"; - - private Task BenchmarkClientTask(object idObj) - { - int id = (int)idObj; - - BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); - - Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - - clientSocket.Bind(new IPEndPoint(IPAddress.Any, 0)); - clientSocket.Connect(ServerEndPoint); - - byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; - byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; - - EndPoint remoteEndPoint = ServerEndPoint; - - lock (typeof(Console)) - { - Console.WriteLine($"[Client {id}] Starting client; sending messages to {remoteEndPoint}"); - } - - for (int i = 0; i < PacketCount; i++) - { - byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client {id}] Hello World! (Packet {i})"); - packetBuffer.CopyTo(sendBuffer, 0); - - benchmarkHelper.StartStopwatch(); - - int totalSent = 0; - do - { - totalSent += clientSocket.Send(sendBuffer, totalSent, sendBuffer.Length - totalSent, - SocketFlags.None); - } while (totalSent != 0 && totalSent != sendBuffer.Length); - - if (totalSent == 0) - { - break; - } - - int totalReceived = 0; - do - { - totalReceived += clientSocket.Receive(receiveBuffer, totalReceived, - receiveBuffer.Length - totalReceived, SocketFlags.None); - } while (totalReceived != 0 && totalReceived != sendBuffer.Length); - - if (totalReceived == 0) - { - break; - } - - benchmarkHelper.StopStopwatch(); - - benchmarkHelper.SnapshotRttStats(); - } - - clientSocket.Disconnect(true); - clientSocket.Close(); - - benchmarkHelper.PrintBandwidthStats(id, PacketCount, NetworkPacket.TotalSize); - benchmarkHelper.PrintRttStats(id); - - ClientBandwidths[id] = benchmarkHelper.CalcBandwidth(PacketCount, NetworkPacket.TotalSize); - - return Task.CompletedTask; - } - - /// <inheritdoc /> - public async Task RunAsync() - { - CancellationTokenSource serverCts = new CancellationTokenSource(); - - int clientCount = Environment.ProcessorCount / 2; - - Console.WriteLine($"TCP Server Benchmark started!"); - - if (PacketCount > 10_000) - { - Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); - } - - StreamSocketServerOptions serverOptions = new StreamSocketServerOptions(clientCount, (ushort)clientCount); - Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - StreamSocketServer server = new StreamSocketServer(ref rawSocket, RawSocketServer.DefaultRawPacketHandler, serverOptions); - - server.Bind(ServerEndPoint); - - Task serverTask = Task.Factory.StartNew(() => - { - server.RunAsync(serverCts.Token).GetAwaiter().GetResult(); - }, TaskCreationOptions.LongRunning); - - ClientBandwidths = new double[clientCount]; - Task[] clientTasks = new Task[clientCount]; - for (int i = 0; i < clientTasks.Length; i++) - { - clientTasks[i] = Task.Factory.StartNew(BenchmarkClientTask, i, TaskCreationOptions.LongRunning); - } - - await Task.WhenAll(clientTasks); - - Console.WriteLine($"Total estimated bandwidth: {ClientBandwidths.Sum():F5}"); - - serverCts.Cancel(); - - await serverTask; - - Console.WriteLine($"TCP Server Benchmark finished!"); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/UDP Socket Connection Benchmarks/UdpSocketClientAsyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/UDP Socket Connection Benchmarks/UdpSocketClientAsyncBenchmark.cs @@ -0,0 +1,86 @@ +using NetSharp.Packets; +using NetSharp.Sockets.Datagram; +using NetSharp.Utils; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks +{ + public class UdpSocketClientAsyncBenchmark : INetSharpExample + { + /// <summary> + /// Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. + /// </summary> + private const int PacketCount = 1_000_000; + + private static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12367); + + /// <inheritdoc /> + public string Name { get; } = "UDP Socket Client Benchmark (Asynchronous)"; + + private Task ServerTask(CancellationToken cancellationToken) + { + Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + + server.Bind(ServerEndPoint); + + byte[] transmissionBuffer = new byte[NetworkPacket.TotalSize]; + + EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + + while (!cancellationToken.IsCancellationRequested) + { + int received = server.ReceiveFrom(transmissionBuffer, ref remoteEndPoint); + + int sent = server.SendTo(transmissionBuffer, remoteEndPoint); + } + + server.Close(); + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public async Task RunAsync() + { + if (PacketCount > 10_000) + { + Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); + } + + using CancellationTokenSource serverCts = new CancellationTokenSource(); + Task serverTask = Task.Factory.StartNew(state => ServerTask((CancellationToken)state), serverCts.Token, TaskCreationOptions.LongRunning); + + BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); + + DatagramSocketClientOptions clientOptions = new DatagramSocketClientOptions((ushort)2); + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + using DatagramSocketClient client = new DatagramSocketClient(ref rawSocket, clientOptions); + + byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; + byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; + + for (int i = 0; i < PacketCount; i++) + { + byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client 0] Hello World! (Packet {i})"); + packetBuffer.CopyTo(sendBuffer, 0); + + benchmarkHelper.StartStopwatch(); + TransmissionResult sendResult = await client.SendAsync(in ServerEndPoint, sendBuffer); + + TransmissionResult receiveResult = await client.ReceiveAsync(in ServerEndPoint, receiveBuffer); + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); + } + + benchmarkHelper.PrintBandwidthStats(0, PacketCount, NetworkPacket.TotalSize); + benchmarkHelper.PrintRttStats(0); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/UDP Socket Connection Benchmarks/UdpSocketClientSyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/UDP Socket Connection Benchmarks/UdpSocketClientSyncBenchmark.cs @@ -0,0 +1,95 @@ +using NetSharp.Packets; +using NetSharp.Sockets.Datagram; +using NetSharp.Utils; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks +{ + public class UdpSocketClientSyncBenchmark : INetSharpExample + { + /// <summary> + /// Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. + /// </summary> + private const int PacketCount = 1_000_000; + + private static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12357); + + /// <inheritdoc /> + public string Name { get; } = "UDP Socket Client Benchmark (Synchronous)"; + + private Task ServerTask(CancellationToken cancellationToken) + { + Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + + server.Bind(ServerEndPoint); + + byte[] transmissionBuffer = new byte[NetworkPacket.TotalSize]; + + EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + + while (!cancellationToken.IsCancellationRequested) + { + server.ReceiveFrom(transmissionBuffer, ref remoteEndPoint); + + server.SendTo(transmissionBuffer, remoteEndPoint); + } + + server.Close(); + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public async Task RunAsync() + { + if (PacketCount > 10_000) + { + Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); + } + + using CancellationTokenSource serverCts = new CancellationTokenSource(); + Task serverTask = Task.Factory.StartNew(state => ServerTask((CancellationToken)state), serverCts.Token, TaskCreationOptions.LongRunning); + + BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); + + DatagramSocketClientOptions clientOptions = new DatagramSocketClientOptions((ushort)2); + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + using DatagramSocketClient client = new DatagramSocketClient(ref rawSocket, clientOptions); + + byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; + byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; + + EndPoint remoteEndPoint = ServerEndPoint; + + for (int i = 0; i < PacketCount; i++) + { + byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client 0] Hello World! (Packet {i})"); + packetBuffer.CopyTo(sendBuffer, 0); + + benchmarkHelper.StartStopwatch(); + TransmissionResult sendResult = client.Send(in remoteEndPoint, sendBuffer); + + TransmissionResult receiveResult = client.Receive(in remoteEndPoint, receiveBuffer); + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); + } + + benchmarkHelper.PrintBandwidthStats(0, PacketCount, NetworkPacket.TotalSize); + benchmarkHelper.PrintRttStats(0); + + serverCts.Cancel(); + try + { + serverTask.Dispose(); + } + catch (Exception) { } + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/UDP Socket Connection Benchmarks/UdpSocketServerBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/UDP Socket Connection Benchmarks/UdpSocketServerBenchmark.cs @@ -0,0 +1,114 @@ +using NetSharp.Packets; +using NetSharp.Sockets; +using NetSharp.Sockets.Datagram; + +using System; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks +{ + public class UdpSocketServerBenchmark : INetSharpExample + { + /// <summary> + /// Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. + /// </summary> + private const int PacketCount = 1_000_000; + + private static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12347); + + private double[] ClientBandwidths; + + /// <inheritdoc /> + public string Name { get; } = "UDP Socket Server Benchmark"; + + private Task BenchmarkClientTask(object idObj) + { + int id = (int)idObj; + + BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); + + Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + + clientSocket.Bind(new IPEndPoint(IPAddress.Any, 0)); + + byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; + byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; + + EndPoint remoteEndPoint = ServerEndPoint; + + lock (typeof(Console)) + { + Console.WriteLine($"[Client {id}] Starting client; sending messages to {remoteEndPoint}"); + } + + for (int i = 0; i < PacketCount; i++) + { + byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client {id}] Hello World! (Packet {i})"); + packetBuffer.CopyTo(sendBuffer, 0); + + benchmarkHelper.StartStopwatch(); + int sentBytes = clientSocket.SendTo(sendBuffer, remoteEndPoint); + + int receivedBytes = clientSocket.ReceiveFrom(receiveBuffer, ref remoteEndPoint); + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); + } + + benchmarkHelper.PrintBandwidthStats(id, PacketCount, NetworkPacket.TotalSize); + benchmarkHelper.PrintRttStats(id); + + ClientBandwidths[id] = benchmarkHelper.CalcBandwidth(PacketCount, NetworkPacket.TotalSize); + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public async Task RunAsync() + { + CancellationTokenSource serverCts = new CancellationTokenSource(); + + int clientCount = Environment.ProcessorCount / 2; + + if (PacketCount > 10_000) + { + Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); + } + + DatagramSocketServerOptions serverOptions = new DatagramSocketServerOptions(clientCount, (ushort)clientCount); + + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + DatagramSocketServer server = new DatagramSocketServer(ref rawSocket, RawSocketServer.DefaultRawPacketHandler, serverOptions); + + server.Bind(ServerEndPoint); + + Task serverTask = Task.Factory.StartNew(() => + { + server.RunAsync(serverCts.Token).GetAwaiter().GetResult(); + }, TaskCreationOptions.LongRunning); + + ClientBandwidths = new double[clientCount]; + Task[] clientTasks = new Task[clientCount]; + for (int i = 0; i < clientTasks.Length; i++) + { + clientTasks[i] = Task.Factory.StartNew(BenchmarkClientTask, i, TaskCreationOptions.LongRunning); + } + + await Task.WhenAll(clientTasks); + + Console.WriteLine($"Total estimated bandwidth: {ClientBandwidths.Sum():F5}"); + + serverCts.Cancel(); + + await serverTask; + + rawSocket.Close(); + rawSocket.Dispose(); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/UdpSocketClientAsyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/UdpSocketClientAsyncBenchmark.cs @@ -1,88 +0,0 @@ -using NetSharp.Packets; -using NetSharp.Sockets.Datagram; -using NetSharp.Utils; - -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharpExamples.Benchmarks -{ - public class UdpSocketClientAsyncBenchmark : INetSharpExample - { - /// <summary> - /// Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. - /// </summary> - private const int PacketCount = 1_000_000; - - private static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12367); - - /// <inheritdoc /> - public string Name { get; } = "UDP Socket Client Benchmark (Asynchronous)"; - - private Task ServerTask(CancellationToken cancellationToken) - { - Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - - server.Bind(ServerEndPoint); - - byte[] transmissionBuffer = new byte[NetworkPacket.TotalSize]; - - EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); - - while (!cancellationToken.IsCancellationRequested) - { - int received = server.ReceiveFrom(transmissionBuffer, ref remoteEndPoint); - - int sent = server.SendTo(transmissionBuffer, remoteEndPoint); - } - - server.Close(); - - return Task.CompletedTask; - } - - /// <inheritdoc /> - public async Task RunAsync() - { - Console.WriteLine($"UDP Client Benchmark started!"); - - if (PacketCount > 10_000) - { - Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); - } - - using CancellationTokenSource serverCts = new CancellationTokenSource(); - Task serverTask = Task.Factory.StartNew(state => ServerTask((CancellationToken)state), serverCts.Token, TaskCreationOptions.LongRunning); - - BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); - - DatagramSocketClientOptions clientOptions = new DatagramSocketClientOptions((ushort)2); - Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - using DatagramSocketClient client = new DatagramSocketClient(ref rawSocket, clientOptions); - - byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; - byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; - - for (int i = 0; i < PacketCount; i++) - { - byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client 0] Hello World! (Packet {i})"); - packetBuffer.CopyTo(sendBuffer, 0); - - benchmarkHelper.StartStopwatch(); - TransmissionResult sendResult = await client.SendAsync(in ServerEndPoint, sendBuffer); - - TransmissionResult receiveResult = await client.ReceiveAsync(in ServerEndPoint, receiveBuffer); - benchmarkHelper.StopStopwatch(); - - benchmarkHelper.SnapshotRttStats(); - } - - benchmarkHelper.PrintBandwidthStats(0, PacketCount, NetworkPacket.TotalSize); - benchmarkHelper.PrintRttStats(0); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/UdpSocketClientSyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/UdpSocketClientSyncBenchmark.cs @@ -1,99 +0,0 @@ -using NetSharp.Packets; -using NetSharp.Sockets.Datagram; -using NetSharp.Utils; - -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharpExamples.Benchmarks -{ - public class UdpSocketClientSyncBenchmark : INetSharpExample - { - /// <summary> - /// Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. - /// </summary> - private const int PacketCount = 1_000_000; - - private static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12357); - - /// <inheritdoc /> - public string Name { get; } = "UDP Socket Client Benchmark (Synchronous)"; - - private Task ServerTask(CancellationToken cancellationToken) - { - Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - - server.Bind(ServerEndPoint); - - byte[] transmissionBuffer = new byte[NetworkPacket.TotalSize]; - - EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); - - while (!cancellationToken.IsCancellationRequested) - { - server.ReceiveFrom(transmissionBuffer, ref remoteEndPoint); - - server.SendTo(transmissionBuffer, remoteEndPoint); - } - - server.Close(); - - return Task.CompletedTask; - } - - /// <inheritdoc /> - public async Task RunAsync() - { - Console.WriteLine($"UDP Client Benchmark started!"); - - if (PacketCount > 10_000) - { - Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); - } - - using CancellationTokenSource serverCts = new CancellationTokenSource(); - Task serverTask = Task.Factory.StartNew(state => ServerTask((CancellationToken)state), serverCts.Token, TaskCreationOptions.LongRunning); - - BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); - - DatagramSocketClientOptions clientOptions = new DatagramSocketClientOptions((ushort)2); - Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - using DatagramSocketClient client = new DatagramSocketClient(ref rawSocket, clientOptions); - - byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; - byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; - - EndPoint remoteEndPoint = ServerEndPoint; - - for (int i = 0; i < PacketCount; i++) - { - byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client 0] Hello World! (Packet {i})"); - packetBuffer.CopyTo(sendBuffer, 0); - - benchmarkHelper.StartStopwatch(); - TransmissionResult sendResult = client.Send(in remoteEndPoint, sendBuffer); - - TransmissionResult receiveResult = client.Receive(in remoteEndPoint, receiveBuffer); - benchmarkHelper.StopStopwatch(); - - benchmarkHelper.SnapshotRttStats(); - } - - benchmarkHelper.PrintBandwidthStats(0, PacketCount, NetworkPacket.TotalSize); - benchmarkHelper.PrintRttStats(0); - - serverCts.Cancel(); - try - { - serverTask.Dispose(); - } - catch (Exception) { } - - Console.WriteLine($"UDP Client Benchmark finished!"); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/UdpSocketServerBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/UdpSocketServerBenchmark.cs @@ -1,118 +0,0 @@ -using NetSharp.Packets; -using NetSharp.Sockets; -using NetSharp.Sockets.Datagram; - -using System; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharpExamples.Benchmarks -{ - public class UdpSocketServerBenchmark : INetSharpExample - { - /// <summary> - /// Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. - /// </summary> - private const int PacketCount = 1_000_000; - - private static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12347); - - private double[] ClientBandwidths; - - /// <inheritdoc /> - public string Name { get; } = "UDP Socket Server Benchmark"; - - private Task BenchmarkClientTask(object idObj) - { - int id = (int)idObj; - - BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); - - Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - - clientSocket.Bind(new IPEndPoint(IPAddress.Any, 0)); - - byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; - byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; - - EndPoint remoteEndPoint = ServerEndPoint; - - lock (typeof(Console)) - { - Console.WriteLine($"[Client {id}] Starting client; sending messages to {remoteEndPoint}"); - } - - for (int i = 0; i < PacketCount; i++) - { - byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client {id}] Hello World! (Packet {i})"); - packetBuffer.CopyTo(sendBuffer, 0); - - benchmarkHelper.StartStopwatch(); - int sentBytes = clientSocket.SendTo(sendBuffer, remoteEndPoint); - - int receivedBytes = clientSocket.ReceiveFrom(receiveBuffer, ref remoteEndPoint); - benchmarkHelper.StopStopwatch(); - - benchmarkHelper.SnapshotRttStats(); - } - - benchmarkHelper.PrintBandwidthStats(id, PacketCount, NetworkPacket.TotalSize); - benchmarkHelper.PrintRttStats(id); - - ClientBandwidths[id] = benchmarkHelper.CalcBandwidth(PacketCount, NetworkPacket.TotalSize); - - return Task.CompletedTask; - } - - /// <inheritdoc /> - public async Task RunAsync() - { - CancellationTokenSource serverCts = new CancellationTokenSource(); - - int clientCount = Environment.ProcessorCount / 2; - - Console.WriteLine($"UDP Server Benchmark started!"); - - if (PacketCount > 10_000) - { - Console.WriteLine($"{PacketCount} packets will be sent per client. This could take a long time (maybe more than a minute)!"); - } - - DatagramSocketServerOptions serverOptions = new DatagramSocketServerOptions(clientCount, (ushort)clientCount); - - Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - DatagramSocketServer server = new DatagramSocketServer(ref rawSocket, RawSocketServer.DefaultRawPacketHandler, serverOptions); - - server.Bind(ServerEndPoint); - - Task serverTask = Task.Factory.StartNew(() => - { - server.RunAsync(serverCts.Token).GetAwaiter().GetResult(); - }, TaskCreationOptions.LongRunning); - - ClientBandwidths = new double[clientCount]; - Task[] clientTasks = new Task[clientCount]; - for (int i = 0; i < clientTasks.Length; i++) - { - clientTasks[i] = Task.Factory.StartNew(BenchmarkClientTask, i, TaskCreationOptions.LongRunning); - } - - await Task.WhenAll(clientTasks); - - Console.WriteLine($"Total estimated bandwidth: {ClientBandwidths.Sum():F5}"); - - serverCts.Cancel(); - - await serverTask; - - rawSocket.Close(); - rawSocket.Dispose(); - - Console.WriteLine($"UDP Server Benchmark finished!"); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Examples/TCP Socket Connection Examples/TcpSocketClientExample.cs b/NetSharp/NetSharpExamples/Examples/TCP Socket Connection Examples/TcpSocketClientExample.cs @@ -0,0 +1,81 @@ +using NetSharp.Packets; +using NetSharp.Sockets.Stream; +using NetSharp.Utils; + +using NetSharpExamples.Examples.UDP_Socket_Connection_Examples; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace NetSharpExamples.Examples.TCP_Socket_Connection_Examples +{ + public class TcpSocketClientExample : INetSharpExample + { + /// <inheritdoc /> + public string Name { get; } = "TCP Socket Client Example"; + + /// <inheritdoc /> + public async Task RunAsync() + { + StreamSocketClientOptions clientOptions = new StreamSocketClientOptions(2); + + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + using StreamSocketClient client = new StreamSocketClient(ref rawSocket, clientOptions); + + Encoding dataEncoding = UdpSocketServerExample.ServerEncoding; + byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; + byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; + + EndPoint remoteEndPoint = TcpSocketServerExample.ServerEndPoint; + + client.Connect(in remoteEndPoint); + + /* a cancellable asynchronous version also exists. + client.ConnectAsync(in remoteEndPoint, CancellationToken.None); + */ + + Console.WriteLine("Starting TCP Socket Client!"); + + for (int i = 0; i < 10; i++) + { + string data = $"Hello World from {client.LocalEndPoint}!"; + dataEncoding.GetBytes(data).CopyTo(sendBuffer, 0); + + TransmissionResult sendResult = + client.Send(in remoteEndPoint, sendBuffer, SocketFlags.None); + + /* a cancellable asynchronous version also exists. use only when necessary due to the inherent performance penalty of async operations + TransmissionResult sendResult = + await client.SendAsync(sendBuffer, SocketFlags.None, CancellationToken.None); + */ + + // lock is not necessary, but means that console output is clean and not interleaved + lock (typeof(Console)) + { + Console.WriteLine($"[Client] Sent request with contents \'{data}\' to {remoteEndPoint}"); + } + + TransmissionResult receiveResult = + client.Receive(in remoteEndPoint, receiveBuffer, SocketFlags.None); + + /* a cancellable asynchronous version also exists. use only when necessary due to the inherent performance penalty of async operations + TransmissionResult receiveResult = + await client.ReceiveAsync(receiveBuffer, SocketFlags.None, CancellationToken.None); + */ + + // lock is not necessary, but means that console output is clean and not interleaved + lock (typeof(Console)) + { + Console.WriteLine($"[Client] Received response with contents \'{dataEncoding.GetString(receiveBuffer).TrimEnd('\0', ' ')}\' from {remoteEndPoint}"); + } + } + + rawSocket.Shutdown(SocketShutdown.Both); + rawSocket.Close(); + rawSocket.Dispose(); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Examples/TCP Socket Connection Examples/TcpSocketServerExample.cs b/NetSharp/NetSharpExamples/Examples/TCP Socket Connection Examples/TcpSocketServerExample.cs @@ -0,0 +1,54 @@ +using NetSharp.Sockets.Stream; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharpExamples.Examples.TCP_Socket_Connection_Examples +{ + public class TcpSocketServerExample : INetSharpExample + { + public static readonly Encoding ServerEncoding = Encoding.UTF8; + public static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12348); + + /// <inheritdoc /> + public string Name { get; } = "TCP Socket Server Example"; + + public static bool ServerPacketHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> request, Memory<byte> response) + { + // lock is not necessary, but means that console output is clean and not interleaved + lock (typeof(Console)) + { + Console.WriteLine($"[Server] Received request with contents \'{ServerEncoding.GetString(request.Span).TrimEnd('\0', ' ')}\' from {remoteEndPoint}"); + Console.WriteLine($"[Server] Echoing back request to {remoteEndPoint}"); + } + + // we echo back the request, but we could just as easily send back a new packet. if we would not want to send back any response, we need + // to return false + + request.CopyTo(response); + + return true; + } + + /// <inheritdoc /> + public Task RunAsync() + { + StreamSocketServerOptions serverOptions = + new StreamSocketServerOptions(Environment.ProcessorCount, 2); + + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + using StreamSocketServer server = + new StreamSocketServer(ref rawSocket, ServerPacketHandler, serverOptions); + + server.Bind(in ServerEndPoint); + + Console.WriteLine("Starting TCP Socket Server!"); + + return server.RunAsync(CancellationToken.None); // we run forever. alternatively, pass in a cancellation token to ensure that the server terminates + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Examples/TcpSocketClientExample.cs b/NetSharp/NetSharpExamples/Examples/TcpSocketClientExample.cs @@ -1,79 +0,0 @@ -using NetSharp.Packets; -using NetSharp.Sockets.Stream; -using NetSharp.Utils; - -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading.Tasks; - -namespace NetSharpExamples.Examples -{ - public class TcpSocketClientExample : INetSharpExample - { - /// <inheritdoc /> - public string Name { get; } = "TCP Socket Client Example"; - - /// <inheritdoc /> - public async Task RunAsync() - { - StreamSocketClientOptions clientOptions = new StreamSocketClientOptions(2); - - Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - using StreamSocketClient client = new StreamSocketClient(ref rawSocket, clientOptions); - - Encoding dataEncoding = UdpSocketServerExample.ServerEncoding; - byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; - byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; - - EndPoint remoteEndPoint = TcpSocketServerExample.ServerEndPoint; - - client.Connect(in remoteEndPoint); - - /* a cancellable asynchronous version also exists. - client.ConnectAsync(in remoteEndPoint, CancellationToken.None); - */ - - Console.WriteLine("Starting TCP Socket Client!"); - - for (int i = 0; i < 10; i++) - { - string data = $"Hello World from {client.LocalEndPoint}!"; - dataEncoding.GetBytes(data).CopyTo(sendBuffer, 0); - - TransmissionResult sendResult = - client.Send(in remoteEndPoint, sendBuffer, SocketFlags.None); - - /* a cancellable asynchronous version also exists. use only when necessary due to the inherent performance penalty of async operations - TransmissionResult sendResult = - await client.SendAsync(sendBuffer, SocketFlags.None, CancellationToken.None); - */ - - // lock is not necessary, but means that console output is clean and not interleaved - lock (typeof(Console)) - { - Console.WriteLine($"[Client] Sent request with contents \'{data}\' to {remoteEndPoint}"); - } - - TransmissionResult receiveResult = - client.Receive(in remoteEndPoint, receiveBuffer, SocketFlags.None); - - /* a cancellable asynchronous version also exists. use only when necessary due to the inherent performance penalty of async operations - TransmissionResult receiveResult = - await client.ReceiveAsync(receiveBuffer, SocketFlags.None, CancellationToken.None); - */ - - // lock is not necessary, but means that console output is clean and not interleaved - lock (typeof(Console)) - { - Console.WriteLine($"[Client] Received response with contents \'{dataEncoding.GetString(receiveBuffer).TrimEnd('\0', ' ')}\' from {remoteEndPoint}"); - } - } - - rawSocket.Shutdown(SocketShutdown.Both); - rawSocket.Close(); - rawSocket.Dispose(); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Examples/TcpSocketServerExample.cs b/NetSharp/NetSharpExamples/Examples/TcpSocketServerExample.cs @@ -1,55 +0,0 @@ -using NetSharp.Packets; -using NetSharp.Sockets.Stream; - -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharpExamples.Examples -{ - public class TcpSocketServerExample : INetSharpExample - { - public static readonly Encoding ServerEncoding = Encoding.UTF8; - public static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12348); - - /// <inheritdoc /> - public string Name { get; } = "TCP Socket Server Example"; - - public static bool ServerPacketHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> request, Memory<byte> response) - { - // lock is not necessary, but means that console output is clean and not interleaved - lock (typeof(Console)) - { - Console.WriteLine($"[Server] Received request with contents \'{ServerEncoding.GetString(request.Span).TrimEnd('\0', ' ')}\' from {remoteEndPoint}"); - Console.WriteLine($"[Server] Echoing back request to {remoteEndPoint}"); - } - - // we echo back the request, but we could just as easily send back a new packet. if we would not want to send back any response, we need - // to return false - - request.CopyTo(response); - - return true; - } - - /// <inheritdoc /> - public Task RunAsync() - { - StreamSocketServerOptions serverOptions = - new StreamSocketServerOptions(Environment.ProcessorCount, 2); - - Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - using StreamSocketServer server = - new StreamSocketServer(ref rawSocket, ServerPacketHandler, serverOptions); - - server.Bind(in ServerEndPoint); - - Console.WriteLine("Starting TCP Socket Server!"); - - return server.RunAsync(CancellationToken.None); // we run forever. alternatively, pass in a cancellation token to ensure that the server terminates - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Examples/UDP Socket Connection Examples/UdpSocketClientExample.cs b/NetSharp/NetSharpExamples/Examples/UDP Socket Connection Examples/UdpSocketClientExample.cs @@ -0,0 +1,71 @@ +using NetSharp.Packets; +using NetSharp.Sockets.Datagram; +using NetSharp.Utils; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace NetSharpExamples.Examples.UDP_Socket_Connection_Examples +{ + public class UdpSocketClientExample : INetSharpExample + { + /// <inheritdoc /> + public string Name { get; } = "UDP Socket Client Example"; + + /// <inheritdoc /> + public async Task RunAsync() + { + DatagramSocketClientOptions clientOptions = new DatagramSocketClientOptions(2); + + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + using DatagramSocketClient client = new DatagramSocketClient(ref rawSocket, clientOptions); + + Encoding dataEncoding = UdpSocketServerExample.ServerEncoding; + byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; + byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; + + EndPoint remoteEndPoint = UdpSocketServerExample.ServerEndPoint; + + Console.WriteLine("Starting UDP Socket Client!"); + + for (int i = 0; i < 10; i++) + { + string data = $"Hello World from {client.LocalEndPoint}!"; + dataEncoding.GetBytes(data).CopyTo(sendBuffer, 0); + + TransmissionResult sendResult = client.Send(in remoteEndPoint, sendBuffer, SocketFlags.None); + + /* a cancellable asynchronous version also exists. use only when necessary due to the inherent performance penalty of async operations + TransmissionResult sendResult = + await client.SendAsync(in remoteEndPoint, sendBuffer, SocketFlags.None); + */ + + // lock is not necessary, but means that console output is clean and not interleaved + lock (typeof(Console)) + { + Console.WriteLine($"[Client] Sent request with contents \'{data}\' to {remoteEndPoint}"); + } + + TransmissionResult receiveResult = client.Receive(in remoteEndPoint, receiveBuffer, SocketFlags.None); + remoteEndPoint = receiveResult.RemoteEndPoint; + + /* a cancellable asynchronous version also exists. use only when necessary due to the inherent performance penalty of async operations + TransmissionResult receiveResult = + await client.ReceiveAsync(in remoteEndPoint, receiveBuffer, SocketFlags.None); + */ + + // lock is not necessary, but means that console output is clean and not interleaved + lock (typeof(Console)) + { + Console.WriteLine($"[Client] Received response with contents \'{dataEncoding.GetString(receiveBuffer).TrimEnd('\0', ' ')}\' from {remoteEndPoint}"); + } + } + + rawSocket.Close(); + rawSocket.Dispose(); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Examples/UDP Socket Connection Examples/UdpSocketServerExample.cs b/NetSharp/NetSharpExamples/Examples/UDP Socket Connection Examples/UdpSocketServerExample.cs @@ -0,0 +1,54 @@ +using NetSharp.Sockets.Datagram; + +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NetSharpExamples.Examples.UDP_Socket_Connection_Examples +{ + public class UdpSocketServerExample : INetSharpExample + { + public static readonly Encoding ServerEncoding = Encoding.UTF8; + public static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12347); + + /// <inheritdoc /> + public string Name { get; } = "UDP Socket Server Example"; + + public static bool ServerPacketHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> request, Memory<byte> response) + { + // lock is not necessary, but means that console output is clean and not interleaved + lock (typeof(Console)) + { + Console.WriteLine($"[Server] Received request with contents \'{ServerEncoding.GetString(request.Span).TrimEnd('\0', ' ')}\' from {remoteEndPoint}"); + Console.WriteLine($"[Server] Echoing back request to {remoteEndPoint}"); + } + + // we echo back the request, but we could just as easily send back a new packet. if we would not want to send back any response, we need + // to return false + + request.CopyTo(response); + + return true; + } + + /// <inheritdoc /> + public Task RunAsync() + { + DatagramSocketServerOptions serverOptions = + new DatagramSocketServerOptions(Environment.ProcessorCount, 2); + + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + using DatagramSocketServer server = + new DatagramSocketServer(ref rawSocket, ServerPacketHandler, serverOptions); + + server.Bind(in ServerEndPoint); + + Console.WriteLine("Starting UDP Socket Server!"); + + return server.RunAsync(CancellationToken.None); // we run forever. alternatively, pass in a cancellation token to ensure that the server terminates + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Examples/UdpSocketClientExample.cs b/NetSharp/NetSharpExamples/Examples/UdpSocketClientExample.cs @@ -1,71 +0,0 @@ -using NetSharp.Packets; -using NetSharp.Sockets.Datagram; -using NetSharp.Utils; - -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading.Tasks; - -namespace NetSharpExamples.Examples -{ - public class UdpSocketClientExample : INetSharpExample - { - /// <inheritdoc /> - public string Name { get; } = "UDP Socket Client Example"; - - /// <inheritdoc /> - public async Task RunAsync() - { - DatagramSocketClientOptions clientOptions = new DatagramSocketClientOptions(2); - - Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - using DatagramSocketClient client = new DatagramSocketClient(ref rawSocket, clientOptions); - - Encoding dataEncoding = UdpSocketServerExample.ServerEncoding; - byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; - byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; - - EndPoint remoteEndPoint = UdpSocketServerExample.ServerEndPoint; - - Console.WriteLine("Starting UDP Socket Client!"); - - for (int i = 0; i < 10; i++) - { - string data = $"Hello World from {client.LocalEndPoint}!"; - dataEncoding.GetBytes(data).CopyTo(sendBuffer, 0); - - TransmissionResult sendResult = client.Send(in remoteEndPoint, sendBuffer, SocketFlags.None); - - /* a cancellable asynchronous version also exists. use only when necessary due to the inherent performance penalty of async operations - TransmissionResult sendResult = - await client.SendAsync(in remoteEndPoint, sendBuffer, SocketFlags.None); - */ - - // lock is not necessary, but means that console output is clean and not interleaved - lock (typeof(Console)) - { - Console.WriteLine($"[Client] Sent request with contents \'{data}\' to {remoteEndPoint}"); - } - - TransmissionResult receiveResult = client.Receive(in remoteEndPoint, receiveBuffer, SocketFlags.None); - remoteEndPoint = receiveResult.RemoteEndPoint; - - /* a cancellable asynchronous version also exists. use only when necessary due to the inherent performance penalty of async operations - TransmissionResult receiveResult = - await client.ReceiveAsync(in remoteEndPoint, receiveBuffer, SocketFlags.None); - */ - - // lock is not necessary, but means that console output is clean and not interleaved - lock (typeof(Console)) - { - Console.WriteLine($"[Client] Received response with contents \'{dataEncoding.GetString(receiveBuffer).TrimEnd('\0', ' ')}\' from {remoteEndPoint}"); - } - } - - rawSocket.Close(); - rawSocket.Dispose(); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Examples/UdpSocketServerExample.cs b/NetSharp/NetSharpExamples/Examples/UdpSocketServerExample.cs @@ -1,55 +0,0 @@ -using NetSharp.Packets; -using NetSharp.Sockets.Datagram; - -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharpExamples.Examples -{ - public class UdpSocketServerExample : INetSharpExample - { - public static readonly Encoding ServerEncoding = Encoding.UTF8; - public static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12347); - - /// <inheritdoc /> - public string Name { get; } = "UDP Socket Server Example"; - - public static bool ServerPacketHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> request, Memory<byte> response) - { - // lock is not necessary, but means that console output is clean and not interleaved - lock (typeof(Console)) - { - Console.WriteLine($"[Server] Received request with contents \'{ServerEncoding.GetString(request.Span).TrimEnd('\0', ' ')}\' from {remoteEndPoint}"); - Console.WriteLine($"[Server] Echoing back request to {remoteEndPoint}"); - } - - // we echo back the request, but we could just as easily send back a new packet. if we would not want to send back any response, we need - // to return false - - request.CopyTo(response); - - return true; - } - - /// <inheritdoc /> - public Task RunAsync() - { - DatagramSocketServerOptions serverOptions = - new DatagramSocketServerOptions(Environment.ProcessorCount, 2); - - Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - using DatagramSocketServer server = - new DatagramSocketServer(ref rawSocket, ServerPacketHandler, serverOptions); - - server.Bind(in ServerEndPoint); - - Console.WriteLine("Starting UDP Socket Server!"); - - return server.RunAsync(CancellationToken.None); // we run forever. alternatively, pass in a cancellation token to ensure that the server terminates - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/INetSharpBenchmark.cs b/NetSharp/NetSharpExamples/INetSharpBenchmark.cs @@ -0,0 +1,6 @@ +namespace NetSharpExamples +{ + public interface INetSharpBenchmark : INetSharpExample + { + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/NetSharpExamples.csproj b/NetSharp/NetSharpExamples/NetSharpExamples.csproj @@ -16,4 +16,9 @@ <ItemGroup> <ProjectReference Include="..\NetSharp\NetSharp.csproj" /> </ItemGroup> + + <ItemGroup> + <Folder Include="Examples\Datagram Network Connection Examples\" /> + <Folder Include="Examples\Stream Network Connection Examples\" /> + </ItemGroup> </Project> \ No newline at end of file diff --git a/NetSharp/NetSharpExamples/NetSharpExamples.xml b/NetSharp/NetSharpExamples/NetSharpExamples.xml @@ -4,100 +4,130 @@ <name>NetSharpExamples</name> </assembly> <members> - <member name="P:NetSharpExamples.Benchmarks.DatagramNetworkReaderBenchmark.Name"> + <member name="P:NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks.DatagramNetworkReaderBenchmark.Name"> <inheritdoc /> </member> - <member name="M:NetSharpExamples.Benchmarks.DatagramNetworkReaderBenchmark.RunAsync"> + <member name="M:NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks.DatagramNetworkReaderBenchmark.RunAsync"> <inheritdoc /> </member> - <member name="F:NetSharpExamples.Benchmarks.TcpSocketClientAsyncBenchmark.PacketCount"> + <member name="P:NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks.DatagramNetworkWriterAsyncBenchmark.Name"> + <inheritdoc /> + </member> + <member name="M:NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks.DatagramNetworkWriterAsyncBenchmark.RunAsync"> + <inheritdoc /> + </member> + <member name="P:NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks.DatagramNetworkWriterSyncBenchmark.Name"> + <inheritdoc /> + </member> + <member name="M:NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks.DatagramNetworkWriterSyncBenchmark.RunAsync"> + <inheritdoc /> + </member> + <member name="P:NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks.StreamNetworkReaderBenchmark.Name"> + <inheritdoc /> + </member> + <member name="M:NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks.StreamNetworkReaderBenchmark.RunAsync"> + <inheritdoc /> + </member> + <member name="P:NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks.StreamNetworkWriterAsyncBenchmark.Name"> + <inheritdoc /> + </member> + <member name="M:NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks.StreamNetworkWriterAsyncBenchmark.RunAsync"> + <inheritdoc /> + </member> + <member name="P:NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks.StreamNetworkWriterSyncBenchmark.Name"> + <inheritdoc /> + </member> + <member name="M:NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks.StreamNetworkWriterSyncBenchmark.RunAsync"> + <inheritdoc /> + </member> + <member name="F:NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks.TcpSocketClientAsyncBenchmark.PacketCount"> <summary> Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. </summary> </member> - <member name="P:NetSharpExamples.Benchmarks.TcpSocketClientAsyncBenchmark.Name"> + <member name="P:NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks.TcpSocketClientAsyncBenchmark.Name"> <inheritdoc /> </member> - <member name="M:NetSharpExamples.Benchmarks.TcpSocketClientAsyncBenchmark.RunAsync"> + <member name="M:NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks.TcpSocketClientAsyncBenchmark.RunAsync"> <inheritdoc /> </member> - <member name="F:NetSharpExamples.Benchmarks.TcpSocketClientSyncBenchmark.PacketCount"> + <member name="F:NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks.TcpSocketClientSyncBenchmark.PacketCount"> <summary> Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. </summary> </member> - <member name="P:NetSharpExamples.Benchmarks.TcpSocketClientSyncBenchmark.Name"> + <member name="P:NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks.TcpSocketClientSyncBenchmark.Name"> <inheritdoc /> </member> - <member name="M:NetSharpExamples.Benchmarks.TcpSocketClientSyncBenchmark.RunAsync"> + <member name="M:NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks.TcpSocketClientSyncBenchmark.RunAsync"> <inheritdoc /> </member> - <member name="F:NetSharpExamples.Benchmarks.TcpSocketServerBenchmark.PacketCount"> + <member name="F:NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks.TcpSocketServerBenchmark.PacketCount"> <summary> Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. </summary> </member> - <member name="P:NetSharpExamples.Benchmarks.TcpSocketServerBenchmark.Name"> + <member name="P:NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks.TcpSocketServerBenchmark.Name"> <inheritdoc /> </member> - <member name="M:NetSharpExamples.Benchmarks.TcpSocketServerBenchmark.RunAsync"> + <member name="M:NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks.TcpSocketServerBenchmark.RunAsync"> <inheritdoc /> </member> - <member name="F:NetSharpExamples.Benchmarks.UdpSocketClientAsyncBenchmark.PacketCount"> + <member name="F:NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks.UdpSocketClientAsyncBenchmark.PacketCount"> <summary> Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. </summary> </member> - <member name="P:NetSharpExamples.Benchmarks.UdpSocketClientAsyncBenchmark.Name"> + <member name="P:NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks.UdpSocketClientAsyncBenchmark.Name"> <inheritdoc /> </member> - <member name="M:NetSharpExamples.Benchmarks.UdpSocketClientAsyncBenchmark.RunAsync"> + <member name="M:NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks.UdpSocketClientAsyncBenchmark.RunAsync"> <inheritdoc /> </member> - <member name="F:NetSharpExamples.Benchmarks.UdpSocketClientSyncBenchmark.PacketCount"> + <member name="F:NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks.UdpSocketClientSyncBenchmark.PacketCount"> <summary> Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. </summary> </member> - <member name="P:NetSharpExamples.Benchmarks.UdpSocketClientSyncBenchmark.Name"> + <member name="P:NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks.UdpSocketClientSyncBenchmark.Name"> <inheritdoc /> </member> - <member name="M:NetSharpExamples.Benchmarks.UdpSocketClientSyncBenchmark.RunAsync"> + <member name="M:NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks.UdpSocketClientSyncBenchmark.RunAsync"> <inheritdoc /> </member> - <member name="F:NetSharpExamples.Benchmarks.UdpSocketServerBenchmark.PacketCount"> + <member name="F:NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks.UdpSocketServerBenchmark.PacketCount"> <summary> Packets contain 8 KiB of data, so 1 000 000 packet = 8GiB. the more data the more accurate the benchmark, but the slower it will run. </summary> </member> - <member name="P:NetSharpExamples.Benchmarks.UdpSocketServerBenchmark.Name"> + <member name="P:NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks.UdpSocketServerBenchmark.Name"> <inheritdoc /> </member> - <member name="M:NetSharpExamples.Benchmarks.UdpSocketServerBenchmark.RunAsync"> + <member name="M:NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks.UdpSocketServerBenchmark.RunAsync"> <inheritdoc /> </member> - <member name="P:NetSharpExamples.Examples.TcpSocketClientExample.Name"> + <member name="P:NetSharpExamples.Examples.TCP_Socket_Connection_Examples.TcpSocketClientExample.Name"> <inheritdoc /> </member> - <member name="M:NetSharpExamples.Examples.TcpSocketClientExample.RunAsync"> + <member name="M:NetSharpExamples.Examples.TCP_Socket_Connection_Examples.TcpSocketClientExample.RunAsync"> <inheritdoc /> </member> - <member name="P:NetSharpExamples.Examples.TcpSocketServerExample.Name"> + <member name="P:NetSharpExamples.Examples.TCP_Socket_Connection_Examples.TcpSocketServerExample.Name"> <inheritdoc /> </member> - <member name="M:NetSharpExamples.Examples.TcpSocketServerExample.RunAsync"> + <member name="M:NetSharpExamples.Examples.TCP_Socket_Connection_Examples.TcpSocketServerExample.RunAsync"> <inheritdoc /> </member> - <member name="P:NetSharpExamples.Examples.UdpSocketClientExample.Name"> + <member name="P:NetSharpExamples.Examples.UDP_Socket_Connection_Examples.UdpSocketClientExample.Name"> <inheritdoc /> </member> - <member name="M:NetSharpExamples.Examples.UdpSocketClientExample.RunAsync"> + <member name="M:NetSharpExamples.Examples.UDP_Socket_Connection_Examples.UdpSocketClientExample.RunAsync"> <inheritdoc /> </member> - <member name="P:NetSharpExamples.Examples.UdpSocketServerExample.Name"> + <member name="P:NetSharpExamples.Examples.UDP_Socket_Connection_Examples.UdpSocketServerExample.Name"> <inheritdoc /> </member> - <member name="M:NetSharpExamples.Examples.UdpSocketServerExample.RunAsync"> + <member name="M:NetSharpExamples.Examples.UDP_Socket_Connection_Examples.UdpSocketServerExample.RunAsync"> <inheritdoc /> </member> <member name="T:NetSharpExamples.INetSharpExample"> diff --git a/NetSharp/NetSharpExamples/Program.cs b/NetSharp/NetSharpExamples/Program.cs @@ -1,37 +1,53 @@ -using NetSharpExamples.Benchmarks; - -using System; +using System; +using System.Collections.Generic; +using System.Reflection; using System.Threading.Tasks; -using NetSharpExamples.Examples; + +#nullable enable namespace NetSharpExamples { internal class Program { - private static readonly INetSharpExample[] Examples = + private static readonly List<INetSharpBenchmark> Benchmarks; + private static readonly List<INetSharpExample> Examples; + + static Program() { - // UDP socket server and client examples - new UdpSocketServerBenchmark(), - new UdpSocketServerExample(), - new UdpSocketClientSyncBenchmark(), - new UdpSocketClientAsyncBenchmark(), - new UdpSocketClientExample(), - - // TCP socket server and client examples - new TcpSocketServerBenchmark(), - new TcpSocketServerExample(), - new TcpSocketClientSyncBenchmark(), - new TcpSocketClientAsyncBenchmark(), - new TcpSocketClientExample(), - - // Restructured UDP server and client benchmarks - new DatagramNetworkReaderBenchmark(), - }; + Examples = new List<INetSharpExample>(); + + Benchmarks = new List<INetSharpBenchmark>(); + + foreach (Type type in Assembly.GetCallingAssembly().GetTypes()) + { + if (!type.IsClass) + { + continue; + } + + Type[] interfaces = type.GetInterfaces(); + object? instance = Activator.CreateInstance(type); + + switch (interfaces.Length) + { + case 1 when type.GetInterface(nameof(INetSharpExample)) == typeof(INetSharpExample): + Examples.Add((INetSharpExample)instance); + break; + + case 2 when type.GetInterface(nameof(INetSharpBenchmark)) == typeof(INetSharpBenchmark): + Examples.Add((INetSharpExample)instance); + Benchmarks.Add((INetSharpBenchmark)instance); + break; + } + } + } private static async Task Main() { Console.WriteLine("Hello World!"); + await RunAllBenchmarks(); + while (true) { GC.Collect(); @@ -43,7 +59,7 @@ namespace NetSharpExamples private static void PickExample() { Console.WriteLine("Available Examples:"); - for (int i = 0; i < Examples.Length; i++) + for (int i = 0; i < Examples.Count; i++) { Console.WriteLine($"\t{i} - {Examples[i].Name}"); } @@ -57,13 +73,15 @@ namespace NetSharpExamples string rawInput = Console.ReadLine(); int choice = int.Parse(rawInput ?? "x"); - if (0 > choice || choice >= Examples.Length) + if (0 > choice || choice >= Examples.Count) { Console.WriteLine("Option does not exist. Please try again!"); continue; } + Console.WriteLine($"Starting \'{Examples[choice].Name}\'..."); Examples[choice].RunAsync().GetAwaiter().GetResult(); + Console.WriteLine($"Finished \'{Examples[choice].Name}\'!"); break; } @@ -75,5 +93,23 @@ namespace NetSharpExamples Console.WriteLine(); } + + private static async Task RunAllBenchmarks() + { + Console.WriteLine("Run all benchmarks? (y/n)"); + string input = Console.ReadLine()?.ToLowerInvariant() ?? "n"; + + if (input == "y") + { + foreach (INetSharpBenchmark benchmark in Benchmarks) + { + Console.WriteLine($"Starting \'{benchmark.Name}\'..."); + await benchmark.RunAsync(); + Console.WriteLine($"Finished \'{benchmark.Name}\'!"); + + Console.WriteLine(); + } + } + } } } \ No newline at end of file