NetSharp

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

commit 2887f7fb4e6c4707d6042fce05501d4d4bfa6300
parent 1218c596fbe09b092349f98a4278e70eb9f7f9fc
Author: Mikolaj Lenczewski <mikolaj.lenczewski308@gmail.com>
Date:   Sun,  3 May 2020 14:00:54 +0100

Removed old API

Diffstat:
DNetSharp/NetSharp/DatagramNetworkConnection.cs | 412-------------------------------------------------------------------------------
MNetSharp/NetSharp/NetSharp.xml | 716+++++--------------------------------------------------------------------------
DNetSharp/NetSharp/NetworkConnectionBase.cs | 64----------------------------------------------------------------
DNetSharp/NetSharp/NetworkReaderBase.cs | 54------------------------------------------------------
DNetSharp/NetSharp/NetworkWriterBase.cs | 29-----------------------------
ANetSharp/NetSharp/Raw/Datagram/DatagramNetworkReader.cs | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Raw/Datagram/DatagramNetworkWriter.cs | 261+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Raw/NetworkConnectionBase.cs | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Raw/NetworkReaderBase.cs | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Raw/NetworkWriterBase.cs | 29+++++++++++++++++++++++++++++
ANetSharp/NetSharp/Raw/Stream/StreamNetworkReader.cs | 303+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Raw/Stream/StreamNetworkWriter.cs | 348+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DNetSharp/NetSharp/Sockets/Datagram/DatagramSocketClient.cs | 265-------------------------------------------------------------------------------
DNetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs | 265-------------------------------------------------------------------------------
DNetSharp/NetSharp/Sockets/RawSocketClient.cs | 308-------------------------------------------------------------------------------
DNetSharp/NetSharp/Sockets/RawSocketServer.cs | 88-------------------------------------------------------------------------------
DNetSharp/NetSharp/Sockets/SocketConnectionBase.cs | 175-------------------------------------------------------------------------------
DNetSharp/NetSharp/Sockets/Stream/StreamSocketClient.cs | 393-------------------------------------------------------------------------------
DNetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs | 334-------------------------------------------------------------------------------
DNetSharp/NetSharp/StreamNetworkConnection.cs | 646-------------------------------------------------------------------------------
MNetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkReaderBenchmark.cs | 12+++++++-----
MNetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkWriterAsyncBenchmark.cs | 4++--
MNetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkWriterSyncBenchmark.cs | 4++--
MNetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkReaderBenchmark.cs | 12+++++++-----
MNetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkWriterAsyncBenchmark.cs | 4++--
MNetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkWriterSyncBenchmark.cs | 4++--
DNetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketClientAsyncBenchmark.cs | 118-------------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketClientSyncBenchmark.cs | 118-------------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketServerBenchmark.cs | 137-------------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Benchmarks/UDP Socket Connection Benchmarks/UdpSocketClientAsyncBenchmark.cs | 87-------------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Benchmarks/UDP Socket Connection Benchmarks/UdpSocketClientSyncBenchmark.cs | 96-------------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Benchmarks/UDP Socket Connection Benchmarks/UdpSocketServerBenchmark.cs | 115-------------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Examples/TCP Socket Connection Examples/TcpSocketClientExample.cs | 82-------------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Examples/TCP Socket Connection Examples/TcpSocketServerExample.cs | 55-------------------------------------------------------
DNetSharp/NetSharpExamples/Examples/UDP Socket Connection Examples/UdpSocketClientExample.cs | 72------------------------------------------------------------------------
DNetSharp/NetSharpExamples/Examples/UDP Socket Connection Examples/UdpSocketServerExample.cs | 55-------------------------------------------------------
MNetSharp/NetSharpExamples/NetSharpExamples.xml | 90-------------------------------------------------------------------------------
37 files changed, 1275 insertions(+), 4755 deletions(-)

diff --git a/NetSharp/NetSharp/DatagramNetworkConnection.cs b/NetSharp/NetSharp/DatagramNetworkConnection.cs @@ -1,411 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading.Tasks; - -namespace NetSharp -{ - public sealed class DatagramNetworkReader : NetworkReaderBase<SocketAsyncEventArgs> - { - /// <inheritdoc /> - 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) - { - byte[] receiveBuffer = args.Buffer; - - switch (args.SocketError) - { - case SocketError.Success: - byte[] responseBuffer = BufferPool.Rent(BufferSize); - - bool responseExists = - RequestHandler(args.RemoteEndPoint, receiveBuffer, responseBuffer); - BufferPool.Return(receiveBuffer, true); - - if (responseExists) - { - args.SetBuffer(responseBuffer, 0, BufferSize); - - StartSendTo(args); - - return; - } - - BufferPool.Return(responseBuffer, true); - break; - - default: - BufferPool.Return(receiveBuffer, true); - StateObjectPool.Return(args); - break; - } - } - - private void CompleteSendTo(SocketAsyncEventArgs args) - { - byte[] sendBuffer = args.Buffer; - - BufferPool.Return(sendBuffer, true); - StateObjectPool.Return(args); - } - - private void HandleIoCompleted(object sender, SocketAsyncEventArgs args) - { - switch (args.LastOperation) - { - case SocketAsyncOperation.ReceiveFrom: - StartDefaultReceiveFrom(); - - CompleteReceiveFrom(args); - break; - - case SocketAsyncOperation.SendTo: - CompleteSendTo(args); - break; - } - } - - private void StartDefaultReceiveFrom() - { - if (ShutdownToken.IsCancellationRequested) - { - return; - } - - SocketAsyncEventArgs args = StateObjectPool.Rent(); - StartReceiveFrom(args); - } - - private void StartReceiveFrom(SocketAsyncEventArgs args) - { - byte[] receiveBuffer = BufferPool.Rent(BufferSize); - - if (ShutdownToken.IsCancellationRequested) - { - BufferPool.Return(receiveBuffer, true); - StateObjectPool.Return(args); - - return; - } - - args.SetBuffer(receiveBuffer, 0, BufferSize); - - if (Connection.ReceiveFromAsync(args)) return; - - StartDefaultReceiveFrom(); - CompleteReceiveFrom(args); - } - - private void StartSendTo(SocketAsyncEventArgs args) - { - byte[] sendBuffer = args.Buffer; - - if (ShutdownToken.IsCancellationRequested) - { - BufferPool.Return(sendBuffer, true); - StateObjectPool.Return(args); - - return; - } - - if (Connection.SendToAsync(args)) return; - - CompleteSendTo(args); - } - - /// <inheritdoc /> - protected override bool CanReuseStateObject(ref SocketAsyncEventArgs instance) - { - return true; - } - - /// <inheritdoc /> - protected override SocketAsyncEventArgs CreateStateObject() - { - SocketAsyncEventArgs instance = new SocketAsyncEventArgs { RemoteEndPoint = DefaultEndPoint }; - instance.Completed += HandleIoCompleted; - - return instance; - } - - /// <inheritdoc /> - protected override void DestroyStateObject(SocketAsyncEventArgs instance) - { - instance.Completed -= HandleIoCompleted; - instance.Dispose(); - } - - /// <inheritdoc /> - protected override void ResetStateObject(ref SocketAsyncEventArgs instance) - { - instance.RemoteEndPoint = DefaultEndPoint; - } - - /// <inheritdoc /> - public override void Start(ushort concurrentReadTasks) - { - for (ushort i = 0; i < concurrentReadTasks; i++) - { - StartDefaultReceiveFrom(); - } - } - } - - public sealed class DatagramNetworkWriter : NetworkWriterBase<SocketAsyncEventArgs> - { - /// <inheritdoc /> - 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(ref SocketAsyncEventArgs instance) - { - return true; - } - - /// <inheritdoc /> - protected override SocketAsyncEventArgs CreateStateObject() - { - SocketAsyncEventArgs instance = new SocketAsyncEventArgs(); - instance.Completed += HandleIoCompleted; - - return instance; - } - - /// <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) - ); - } - - 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) - { - 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) - { - 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,68 +4,6 @@ <name>NetSharp</name> </assembly> <members> - <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@)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.DatagramNetworkReader.CreateStateObject"> - <inheritdoc /> - </member> - <member name="M:NetSharp.DatagramNetworkReader.DestroyStateObject(System.Net.Sockets.SocketAsyncEventArgs)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.DatagramNetworkReader.ResetStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.DatagramNetworkReader.Start(System.UInt16)"> - <inheritdoc /> - </member> - <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@)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.DatagramNetworkWriter.CreateStateObject"> - <inheritdoc /> - </member> - <member name="M:NetSharp.DatagramNetworkWriter.DestroyStateObject(System.Net.Sockets.SocketAsyncEventArgs)"> - <inheritdoc /> - </member> - <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> - <member name="M:NetSharp.DatagramNetworkWriter.WriteAsync(System.Net.EndPoint,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.NetworkConnectionBase`1.Dispose(System.Boolean)"> - <summary> - Allows for inheritors to dispose of their own resources. - </summary> - </member> - <member name="M:NetSharp.NetworkConnectionBase`1.Dispose"> - <inheritdoc /> - </member> - <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.Net.EndPoint,System.Int32,System.Int32,System.UInt32)"> - <inheritdoc /> - </member> <member name="T:NetSharp.Packets.NetworkPacket"> <summary> Represents a raw packet sent across the network. @@ -218,690 +156,111 @@ The memory buffer to write the serialised packet header instance to. </param> </member> - <member name="T:NetSharp.Sockets.Datagram.DatagramSocketClientOptions"> - <summary> - Provides additional configuration options for a <see cref="T:NetSharp.Sockets.Datagram.DatagramSocketClient" /> instance. - </summary> - </member> - <member name="F:NetSharp.Sockets.Datagram.DatagramSocketClientOptions.Defaults"> - <summary> - The default configuration. - </summary> - </member> - <member name="F:NetSharp.Sockets.Datagram.DatagramSocketClientOptions.PreallocatedTransmissionArgs"> - <summary> - The number of <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances that should be preallocated for use in the - <see cref="!:DatagramSocketClient.SendToAsyncInternal" /> and <see cref="!:DatagramSocketClient.ReceiveFromAsyncInternal" /> methods. - </summary> - </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketClientOptions.#ctor(System.UInt16)"> - <summary> - Constructs a new instance of the <see cref="T:NetSharp.Sockets.Datagram.DatagramSocketClientOptions" /> struct. - </summary> - <param name="preallocatedTransmissionArgs"> - The number of <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances to preallocate. - </param> - </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketClient.CanTransmissionArgsBeReused(System.Net.Sockets.SocketAsyncEventArgs@)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketClient.CreateTransmissionArgs"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketClient.DestroyTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketClient.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketClient.ResetTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs@)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketClient.Receive(System.Net.EndPoint@,System.Byte[],System.Net.Sockets.SocketFlags)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketClient.ReceiveAsync(System.Net.EndPoint@,System.Memory{System.Byte},System.Net.Sockets.SocketFlags)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketClient.Send(System.Net.EndPoint@,System.Byte[],System.Net.Sockets.SocketFlags)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketClient.SendAsync(System.Net.EndPoint@,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)"> - <inheritdoc /> - </member> - <member name="T:NetSharp.Sockets.Datagram.DatagramSocketServerOptions"> - <summary> - Provides additional configuration options for a <see cref="T:NetSharp.Sockets.Datagram.DatagramSocketServer" /> instance. - </summary> - </member> - <member name="F:NetSharp.Sockets.Datagram.DatagramSocketServerOptions.Defaults"> - <summary> - The default configuration. - </summary> - </member> - <member name="F:NetSharp.Sockets.Datagram.DatagramSocketServerOptions.ConcurrentReceiveFromCalls"> - <summary> - The number of <see cref="M:System.Net.Sockets.Socket.ReceiveFromAsync(System.Net.Sockets.SocketAsyncEventArgs)" /> calls that will be 'in-flight' at any one time, and ready to service incoming client - packets. This should be set to the number of client which will be connected at once. - </summary> - </member> - <member name="F:NetSharp.Sockets.Datagram.DatagramSocketServerOptions.PreallocatedTransmissionArgs"> - <summary> - The number of <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances that should be preallocated for use in the <see cref="M:System.Net.Sockets.Socket.SendToAsync(System.Net.Sockets.SocketAsyncEventArgs)" /> - and <see cref="M:System.Net.Sockets.Socket.ReceiveFromAsync(System.Net.Sockets.SocketAsyncEventArgs)" /> methods. - </summary> - </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketServerOptions.#ctor(System.Int32,System.UInt16)"> - <summary> - Constructs a new instance of the <see cref="T:NetSharp.Sockets.Datagram.DatagramSocketServerOptions" /> struct. - </summary> - <param name="concurrentReceiveFromCalls"> - The number of <see cref="M:System.Net.Sockets.Socket.ReceiveFromAsync(System.Net.Sockets.SocketAsyncEventArgs)" /> calls which should be 'in-flight' at any one time. - </param> - <param name="preallocatedTransmissionArgs"> - The number of <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances to preallocate. - </param> - </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketServer.#ctor(System.Net.Sockets.Socket@,NetSharp.Sockets.RawRequestPacketHandler@,System.Nullable{NetSharp.Sockets.Datagram.DatagramSocketServerOptions}@)"> - <summary> - Constructs a new instance of the <see cref="T:NetSharp.Sockets.Datagram.DatagramSocketServer" /> class. - </summary> - <param name="serverOptions"> - Additional options to configure the server. - </param> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkReader.#ctor(System.Net.Sockets.Socket@,NetSharp.Raw.NetworkRequestHandler,System.Net.EndPoint,System.Int32,System.Int32,System.UInt32)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketServer.CanTransmissionArgsBeReused(System.Net.Sockets.SocketAsyncEventArgs@)"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkReader.CanReuseStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketServer.CreateTransmissionArgs"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkReader.CreateStateObject"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketServer.DestroyTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs)"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkReader.DestroyStateObject(System.Net.Sockets.SocketAsyncEventArgs)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketServer.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkReader.ResetStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketServer.ResetTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs@)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketServer.RunAsync(System.Threading.CancellationToken)"> - <inheritdoc /> - </member> - <member name="T:NetSharp.Sockets.RawSocketClient"> - <summary> - Abstract base class for clients. - </summary> - TODO implement proper memory leak-free cancellation of network IO operations - </member> - <member name="M:NetSharp.Sockets.RawSocketClient.#ctor(System.Net.Sockets.Socket@,System.Int32,System.UInt16)"> - <summary> - 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. - </param> - <param name="pooledBufferMaxSize"> - The maximum size in bytes of buffers held in the buffer pool. - </param> - <param name="preallocatedTransmissionArgs"> - The number of transmission args to preallocate. - </param> - </member> - <member name="M:NetSharp.Sockets.RawSocketClient.CancelAsyncOperationCallback(System.Object)"> - <summary> - Callback for the cancellation of an asynchronous network operation. - </summary> - <param name="state"> - The <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> state object for the operation. - </param> - </member> - <member name="M:NetSharp.Sockets.RawSocketClient.CancelAsyncReceiveCallback(System.Object)"> - <summary> - Callback for the cancellation of an asynchronous network receive operation. - </summary> - <param name="state"> - The <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> state object for the operation. - </param> - </member> - <member name="M:NetSharp.Sockets.RawSocketClient.CancelAsyncSendCallback(System.Object)"> - <summary> - Callback for the cancellation of an asynchronous network send operation. - </summary> - <param name="state"> - The <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> state object for the operation. - </param> - </member> - <member name="M:NetSharp.Sockets.RawSocketClient.Connect(System.Net.EndPoint@)"> - <summary> - Connects the client to the specified end point. If called on a <see cref="F:System.Net.Sockets.SocketType.Dgram" />-based client, this method configures the - default remote host, and the client will ignore any packets not coming from this default host (i.e the given <paramref name="remoteEndPoint" />). - </summary> - <param name="remoteEndPoint"> - The remote end point which to which to connect the client. - </param> - </member> - <member name="M:NetSharp.Sockets.RawSocketClient.ConnectAsync(System.Net.EndPoint@)"> - <summary> - Asynchronously connects the client to the specified end point. If called on a <see cref="F:System.Net.Sockets.SocketType.Dgram" />-based client, this method - configures the default remote host, and the client will ignore any packets not coming from this default host (i.e the given <paramref name="remoteEndPoint" />). - </summary> - <param name="remoteEndPoint"> - The remote end point which to which to connect the client. - </param> - <returns> - A <see cref="T:System.Threading.Tasks.ValueTask" /> representing the connection attempt. - </returns> - </member> - <member name="M:NetSharp.Sockets.RawSocketClient.Receive(System.Net.EndPoint@,System.Byte[],System.Net.Sockets.SocketFlags)"> - <summary> - Listens for data from the specified endpoint, placing the data in the given buffer. On connection-oriented protocols, the given endpoint - is ignored in favour of the default remote host set up by a call to <see cref="M:NetSharp.Sockets.RawSocketClient.Connect(System.Net.EndPoint@)" /> or <see cref="M:NetSharp.Sockets.RawSocketClient.ConnectAsync(System.Net.EndPoint@)" />. - </summary> - <param name="remoteEndPoint"> - The remote endpoint from which data should be received. Ignored on connection-oriented protocols. - </param> - <param name="receiveBuffer"> - The buffer into which data is to be received. - </param> - <param name="flags"> - The socket flags associated with the send operation. - </param> - <returns> - The result of the receive operation. - </returns> - </member> - <member name="M:NetSharp.Sockets.RawSocketClient.ReceiveAsync(System.Net.EndPoint@,System.Memory{System.Byte},System.Net.Sockets.SocketFlags)"> - <summary> - Asynchronously listens for data from the specified endpoint, placing the data in the given buffer. On connection-oriented protocols, the - given endpoint is ignored in favour of the default remote host set up by a call to <see cref="M:NetSharp.Sockets.RawSocketClient.Connect(System.Net.EndPoint@)" /> or <see cref="M:NetSharp.Sockets.RawSocketClient.ConnectAsync(System.Net.EndPoint@)" />. - </summary> - <param name="remoteEndPoint"> - The remote endpoint from which data should be received. Ignored on connection-oriented protocols. - </param> - <param name="receiveBuffer"> - The buffer into which data is to be received. - </param> - <param name="flags"> - The socket flags associated with the send operation. - </param> - <returns> - The result of the asynchronous receive operation. - </returns> - </member> - <member name="M:NetSharp.Sockets.RawSocketClient.Send(System.Net.EndPoint@,System.Byte[],System.Net.Sockets.SocketFlags)"> - <summary> - Sends the data in the given buffer to the specified endpoint. On connection-oriented protocols, the given endpoint is ignored in favour of - the default remote host set up by a call to <see cref="M:NetSharp.Sockets.RawSocketClient.Connect(System.Net.EndPoint@)" /> or <see cref="M:NetSharp.Sockets.RawSocketClient.ConnectAsync(System.Net.EndPoint@)" />. - </summary> - <param name="remoteEndPoint"> - The remote endpoint to which data should be sent. Ignored on connection-oriented protocols. - </param> - <param name="sendBuffer"> - The buffer containing the outgoing data to be sent. - </param> - <param name="flags"> - The socket flags associated with the send operation. - </param> - <returns> - The result of the send operation. - </returns> - </member> - <member name="M:NetSharp.Sockets.RawSocketClient.SendAsync(System.Net.EndPoint@,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)"> - <summary> - Asynchronously sends the data in the given buffer to the specified endpoint. On connection-oriented protocols, the given endpoint is - ignored in favour of the default remote host set up by a call to <see cref="M:NetSharp.Sockets.RawSocketClient.Connect(System.Net.EndPoint@)" /> or <see cref="M:NetSharp.Sockets.RawSocketClient.ConnectAsync(System.Net.EndPoint@)" />. - </summary> - <param name="remoteEndPoint"> - The remote endpoint to which data should be sent. Ignored on connection-oriented protocols. - </param> - <param name="sendBuffer"> - The buffer containing the outgoing data to be sent. The contents of the buffer are copied to an internally maintained buffer when the call - is made. - </param> - <param name="flags"> - The socket flags associated with the send operation. - </param> - <returns> - The result of the asynchronous send operation. - </returns> - </member> - <member name="T:NetSharp.Sockets.RawSocketClient.AsyncOperationToken"> - <summary> - A state token for asynchronous socket operations. - </summary> - </member> - <member name="F:NetSharp.Sockets.RawSocketClient.AsyncOperationToken.CancellationToken"> - <summary> - The <see cref="T:System.Threading.CancellationToken" /> associated with the socket operation. - </summary> - </member> - <member name="F:NetSharp.Sockets.RawSocketClient.AsyncOperationToken.CompletionSource"> - <summary> - The completion source which wraps the event-based APM, and provides an awaitable <see cref="T:System.Threading.Tasks.Task" />. - </summary> - </member> - <member name="M:NetSharp.Sockets.RawSocketClient.AsyncOperationToken.#ctor(System.Threading.Tasks.TaskCompletionSource{System.Boolean}@,System.Threading.CancellationToken@)"> - <summary> - Constructs a new instance of the <see cref="T:NetSharp.Sockets.RawSocketClient.AsyncOperationToken" /> struct. - </summary> - <param name="completionSource"> - The completion source to trigger when the socket operation completes. - </param> - <param name="cancellationToken"> - The cancellation token to observe during the operation. - </param> - </member> - <member name="T:NetSharp.Sockets.RawSocketClient.AsyncReceiveToken"> - <summary> - A state token for asynchronous incoming network IO operations. - </summary> - </member> - <member name="F:NetSharp.Sockets.RawSocketClient.AsyncReceiveToken.CancellationToken"> - <summary> - The <see cref="T:System.Threading.CancellationToken" /> associated with the network IO operation. - </summary> - </member> - <member name="F:NetSharp.Sockets.RawSocketClient.AsyncReceiveToken.CompletionSource"> - <summary> - The completion source which wraps the event-based APM, and provides an awaitable <see cref="T:System.Threading.Tasks.Task" />. - </summary> - </member> - <member name="M:NetSharp.Sockets.RawSocketClient.AsyncReceiveToken.#ctor(System.Threading.Tasks.TaskCompletionSource{NetSharp.Utils.TransmissionResult}@,System.Threading.CancellationToken@)"> - <summary> - Constructs a new instance of the <see cref="T:NetSharp.Sockets.RawSocketClient.AsyncReceiveToken" /> struct. - </summary> - <param name="completionSource"> - The completion source to trigger when the IO operation completes. - </param> - <param name="cancellationToken"> - The cancellation token to observe during the operation. - </param> - </member> - <member name="T:NetSharp.Sockets.RawSocketClient.AsyncSendToken"> - <summary> - A state token for asynchronous outgoing network IO operations. - </summary> - </member> - <member name="F:NetSharp.Sockets.RawSocketClient.AsyncSendToken.CancellationToken"> - <summary> - The <see cref="T:System.Threading.CancellationToken" /> associated with the network IO operation. - </summary> - </member> - <member name="F:NetSharp.Sockets.RawSocketClient.AsyncSendToken.CompletionSource"> - <summary> - The completion source which wraps the event-based APM, and provides an awaitable <see cref="T:System.Threading.Tasks.Task" />. - </summary> - </member> - <member name="F:NetSharp.Sockets.RawSocketClient.AsyncSendToken.RentedBuffer"> - <summary> - The rented buffer which holds the user's data. - </summary> - </member> - <member name="M:NetSharp.Sockets.RawSocketClient.AsyncSendToken.#ctor(System.Threading.Tasks.TaskCompletionSource{NetSharp.Utils.TransmissionResult}@,System.Byte[]@,System.Threading.CancellationToken@)"> - <summary> - Constructs a new instance of the <see cref="T:NetSharp.Sockets.RawSocketClient.AsyncSendToken" /> struct. - </summary> - <param name="completionSource"> - The completion source to trigger when the IO operation completes. - </param> - <param name="bufferHandle"> - The buffer holding the user's data. - </param> - <param name="cancellationToken"> - The cancellation token to observe during the operation. - </param> - </member> - <member name="T:NetSharp.Sockets.RawRequestPacketHandler"> - <summary> - Represents a method for serving request packets. This method should not throw any errors. - </summary> - <param name="clientEndPoint"> - The client from which the packet was received. - </param> - <param name="requestBuffer"> - The buffer holding the data received from the client. - </param> - <param name="responseBuffer"> - The buffer into which the optional response should be written. - </param> - <returns> - Whether a response was generated which should be sent back to the client. - </returns> - </member> - <member name="T:NetSharp.Sockets.RawSocketServer"> - <summary> - Abstract base class for servers. - </summary> - </member> - <member name="F:NetSharp.Sockets.RawSocketServer.PacketHandler"> - <summary> - The packet handler delegate to use to respond to incoming requests. - </summary> - </member> - <member name="M:NetSharp.Sockets.RawSocketServer.#ctor(System.Net.Sockets.Socket@,System.Int32,System.UInt16,NetSharp.Sockets.RawRequestPacketHandler)"> - <summary> - 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. - </param> - <param name="packetHandler"> - The packet handler delegate to use to respond to incoming requests. - </param> - <param name="pooledBufferMaxSize"> - The maximum size in bytes of buffers held in the buffer pool. - </param> - <param name="preallocatedTransmissionArgs"> - The number of transmission args to preallocate. - </param> - </member> - <member name="M:NetSharp.Sockets.RawSocketServer.DefaultRawPacketHandler(System.Net.EndPoint@,System.ReadOnlyMemory{System.Byte},System.Memory{System.Byte})"> - <summary> - The default request handler for servers. Simply echoes back any received data. - </summary> - <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> - <returns> - Whether to send back a response. - </returns> - </member> - <member name="M:NetSharp.Sockets.RawSocketServer.RunAsync(System.Threading.CancellationToken)"> - <summary> - Runs the server, handling requests from clients, until the <paramref name="cancellationToken" /> has its cancellation requested. - </summary> - <param name="cancellationToken"> - The <see cref="T:System.Threading.CancellationToken" /> upon whose cancellation the server should shut down. - </param> - <returns> - A <see cref="T:System.Threading.Tasks.Task" /> representing the server's execution. - </returns> - </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. - </summary> - </member> - <member name="F:NetSharp.Sockets.SocketConnectionBase.ArgsPool"> - <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. - </summary> - </member> - <member name="F:NetSharp.Sockets.SocketConnectionBase.BufferPool"> - <summary> - Pools arrays to function as temporary buffers during network read/write operations. - </summary> - </member> - <member name="F:NetSharp.Sockets.SocketConnectionBase.MaxBufferSize"> - <summary> - The maximum size of buffer that can be rented from the pool. - </summary> - </member> - <member name="F:NetSharp.Sockets.SocketConnectionBase.Connection"> - <summary> - The underlying <see cref="T:System.Net.Sockets.Socket" /> which provides access to network operations. - </summary> - </member> - <member name="M:NetSharp.Sockets.SocketConnectionBase.#ctor(System.Net.Sockets.Socket@,System.Int32,System.UInt16)"> - <summary> - 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. - </param> - <param name="pooledBufferMaxSize"> - The maximum size in bytes of buffers held in the buffer pool. - </param> - <param name="preallocatedTransmissionArgs"> - The number of <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> objects to initially preallocate. - </param> - </member> - <member name="P:NetSharp.Sockets.SocketConnectionBase.LocalEndPoint"> - <summary> - The local endpoint to which the underlying <see cref="T:System.Net.Sockets.Socket" /> is bound. - </summary> - </member> - <member name="M:NetSharp.Sockets.SocketConnectionBase.CanTransmissionArgsBeReused(System.Net.Sockets.SocketAsyncEventArgs@)"> - <summary> - Delegate method used to check whether the given used <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instance can be reused by the - <see cref="F:NetSharp.Sockets.SocketConnectionBase.ArgsPool" />. If this method returns <c>true</c>, <see cref="M:NetSharp.Sockets.SocketConnectionBase.ResetTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs@)" /> is called on the given - <paramref name="args" />. Otherwise, <see cref="M:NetSharp.Sockets.SocketConnectionBase.DestroyTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs)" /> is called. - </summary> - <param name="args"> - The <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instance to check. - </param> - <returns> - Whether the given <paramref name="args" /> should be reset and reused, or should be destroyed. - </returns> - </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 - <see cref="E:System.Net.Sockets.SocketAsyncEventArgs.Completed" /> event. - </summary> - <returns> - The configured <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instance. - </returns> - </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. - </summary> - <param name="remoteConnectionArgs"> - The <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> which should be destroyed. - </param> - </member> - <member name="M:NetSharp.Sockets.SocketConnectionBase.Dispose(System.Boolean)"> - <summary> - Disposes of managed and unmanaged resources used by the <see cref="T:NetSharp.Sockets.SocketConnectionBase" /> class. - </summary> - <param name="disposing"> - Whether this call was made by a call to <see cref="M:NetSharp.Sockets.SocketConnectionBase.Dispose" />. - </param> - </member> - <member name="M:NetSharp.Sockets.SocketConnectionBase.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)"> - <summary> - Delegate method to handle asynchronous network IO completion via the <see cref="E:System.Net.Sockets.SocketAsyncEventArgs.Completed" /> event. - </summary> - <param name="sender"> - The object which raised the event. - </param> - <param name="args"> - The <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instance associated with the asynchronous network IO. - </param> - </member> - <member name="M:NetSharp.Sockets.SocketConnectionBase.ResetTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs@)"> - <summary> - Delegate method used to reset used <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances for later reuse by the <see cref="F:NetSharp.Sockets.SocketConnectionBase.ArgsPool" />. - </summary> - <param name="args"> - The <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instance that should be reset. - </param> - </member> - <member name="M:NetSharp.Sockets.SocketConnectionBase.Bind(System.Net.EndPoint@)"> - <summary> - Binds the underlying socket. - </summary> - <param name="localEndPoint"> - The end point to which the socket should be bound. - </param> - </member> - <member name="M:NetSharp.Sockets.SocketConnectionBase.Dispose"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkReader.Start(System.UInt16)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.SocketConnectionBase.Shutdown(System.Net.Sockets.SocketShutdown)"> - <summary> - Shuts down the underlying socket. - </summary> - <param name="how"> - Which socket transmission functions should be shut down on the socket. - </param> - </member> - <member name="T:NetSharp.Sockets.Stream.StreamSocketClientOptions"> - <summary> - Provides additional configuration options for a <see cref="T:NetSharp.Sockets.Stream.StreamSocketClient" /> instance. - </summary> - </member> - <member name="F:NetSharp.Sockets.Stream.StreamSocketClientOptions.Defaults"> - <summary> - The default configuration. - </summary> - </member> - <member name="F:NetSharp.Sockets.Stream.StreamSocketClientOptions.PreallocatedTransmissionArgs"> - <summary> - The number of <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances that should be preallocated for use in the - <see cref="M:NetSharp.Sockets.Stream.StreamSocketClient.SendAsync(System.Net.EndPoint@,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)" /> and <see cref="M:NetSharp.Sockets.Stream.StreamSocketClient.ReceiveAsync(System.Net.EndPoint@,System.Memory{System.Byte},System.Net.Sockets.SocketFlags)" /> methods. - </summary> - </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketClientOptions.#ctor(System.UInt16)"> - <summary> - Constructs a new instance of the <see cref="T:NetSharp.Sockets.Stream.StreamSocketClientOptions" /> struct. - </summary> - <param name="preallocatedTransmissionArgs"> - The number of <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances to preallocate. - </param> - </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.CanTransmissionArgsBeReused(System.Net.Sockets.SocketAsyncEventArgs@)"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkWriter.#ctor(System.Net.Sockets.Socket@,System.Net.EndPoint,System.Int32,System.Int32,System.UInt32)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.CreateTransmissionArgs"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkWriter.CanReuseStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.DestroyTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs)"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkWriter.CreateStateObject"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkWriter.DestroyStateObject(System.Net.Sockets.SocketAsyncEventArgs)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.ResetTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs@)"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkWriter.ResetStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.Receive(System.Net.EndPoint@,System.Byte[],System.Net.Sockets.SocketFlags)"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkWriter.Read(System.Net.EndPoint@,System.Memory{System.Byte},System.Net.Sockets.SocketFlags)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.ReceiveAsync(System.Net.EndPoint@,System.Memory{System.Byte},System.Net.Sockets.SocketFlags)"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkWriter.ReadAsync(System.Net.EndPoint,System.Memory{System.Byte},System.Net.Sockets.SocketFlags)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.Send(System.Net.EndPoint@,System.Byte[],System.Net.Sockets.SocketFlags)"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkWriter.Write(System.Net.EndPoint,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.SendAsync(System.Net.EndPoint@,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)"> + <member name="M:NetSharp.Raw.Datagram.DatagramNetworkWriter.WriteAsync(System.Net.EndPoint,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)"> <inheritdoc /> </member> - <member name="T:NetSharp.Sockets.Stream.StreamSocketServerOptions"> - <summary> - Provides additional configuration options for a <see cref="T:NetSharp.Sockets.Stream.StreamSocketServer" /> instance. - </summary> - </member> - <member name="F:NetSharp.Sockets.Stream.StreamSocketServerOptions.Defaults"> + <member name="M:NetSharp.Raw.NetworkConnectionBase`1.Dispose(System.Boolean)"> <summary> - The default configuration. - </summary> - </member> - <member name="F:NetSharp.Sockets.Stream.StreamSocketServerOptions.ConcurrentAcceptCalls"> - <summary> - The number of <see cref="M:System.Net.Sockets.Socket.AcceptAsync(System.Net.Sockets.SocketAsyncEventArgs)" /> calls that will be 'in-flight' at any one time, and ready to service incoming client - connection requests. This should be set according to the number of client which will be attempting to connect at once. - </summary> - </member> - <member name="F:NetSharp.Sockets.Stream.StreamSocketServerOptions.PreallocatedTransmissionArgs"> - <summary> - The number of <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances that should be preallocated for use in the <see cref="M:System.Net.Sockets.Socket.SendAsync(System.Net.Sockets.SocketAsyncEventArgs)" /> and - <see cref="M:System.Net.Sockets.Socket.ReceiveAsync(System.Net.Sockets.SocketAsyncEventArgs)" /> methods. - </summary> - </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketServerOptions.#ctor(System.Int32,System.UInt16)"> - <summary> - Constructs a new instance of the <see cref="T:NetSharp.Sockets.Stream.StreamSocketServerOptions" /> struct. - </summary> - <param name="concurrentAcceptCalls"> - The number of <see cref="M:System.Net.Sockets.Socket.AcceptAsync(System.Net.Sockets.SocketAsyncEventArgs)" /> calls which should be 'in-flight' at any one time. - </param> - <param name="preallocatedTransmissionArgs"> - The number of <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances to preallocate. - </param> - </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketServer.#ctor(System.Net.Sockets.Socket@,NetSharp.Sockets.RawRequestPacketHandler@,System.Nullable{NetSharp.Sockets.Stream.StreamSocketServerOptions}@)"> - <summary> - Constructs a new instance of the <see cref="T:NetSharp.Sockets.Stream.StreamSocketServer" /> class. + Allows for inheritors to dispose of their own resources. </summary> - <param name="serverOptions"> - Additional options to configure the server. - </param> - <inheritdoc /> - </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketServer.CanTransmissionArgsBeReused(System.Net.Sockets.SocketAsyncEventArgs@)"> - <inheritdoc /> - </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketServer.CreateTransmissionArgs"> - <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketServer.DestroyTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs)"> + <member name="M:NetSharp.Raw.NetworkConnectionBase`1.Dispose"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketServer.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)"> + <member name="M:NetSharp.Raw.NetworkReaderBase`1.#ctor(System.Net.Sockets.Socket@,System.Net.EndPoint,NetSharp.Raw.NetworkRequestHandler,System.Int32,System.Int32,System.UInt32)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketServer.ResetTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs@)"> + <member name="M:NetSharp.Raw.NetworkReaderBase`1.Dispose(System.Boolean)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketServer.RunAsync(System.Threading.CancellationToken)"> + <member name="M:NetSharp.Raw.NetworkWriterBase`1.#ctor(System.Net.Sockets.Socket@,System.Net.EndPoint,System.Int32,System.Int32,System.UInt32)"> <inheritdoc /> </member> - <member name="M:NetSharp.StreamNetworkReader.#ctor(System.Net.Sockets.Socket@,NetSharp.NetworkRequestHandler,System.Net.EndPoint,System.Int32,System.Int32,System.UInt32)"> + <member name="M:NetSharp.Raw.Stream.StreamNetworkReader.#ctor(System.Net.Sockets.Socket@,NetSharp.Raw.NetworkRequestHandler,System.Net.EndPoint,System.Int32,System.Int32,System.UInt32)"> <inheritdoc /> </member> - <member name="M:NetSharp.StreamNetworkReader.CanReuseStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> + <member name="M:NetSharp.Raw.Stream.StreamNetworkReader.CanReuseStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> <inheritdoc /> </member> - <member name="M:NetSharp.StreamNetworkReader.CreateStateObject"> + <member name="M:NetSharp.Raw.Stream.StreamNetworkReader.CreateStateObject"> <inheritdoc /> </member> - <member name="M:NetSharp.StreamNetworkReader.DestroyStateObject(System.Net.Sockets.SocketAsyncEventArgs)"> + <member name="M:NetSharp.Raw.Stream.StreamNetworkReader.DestroyStateObject(System.Net.Sockets.SocketAsyncEventArgs)"> <inheritdoc /> </member> - <member name="M:NetSharp.StreamNetworkReader.ResetStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> + <member name="M:NetSharp.Raw.Stream.StreamNetworkReader.ResetStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> <inheritdoc /> </member> - <member name="M:NetSharp.StreamNetworkReader.Start(System.UInt16)"> + <member name="M:NetSharp.Raw.Stream.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)"> + <member name="M:NetSharp.Raw.Stream.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@)"> + <member name="M:NetSharp.Raw.Stream.StreamNetworkWriter.CanReuseStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> <inheritdoc /> </member> - <member name="M:NetSharp.StreamNetworkWriter.CreateStateObject"> + <member name="M:NetSharp.Raw.Stream.StreamNetworkWriter.CreateStateObject"> <inheritdoc /> </member> - <member name="M:NetSharp.StreamNetworkWriter.DestroyStateObject(System.Net.Sockets.SocketAsyncEventArgs)"> + <member name="M:NetSharp.Raw.Stream.StreamNetworkWriter.DestroyStateObject(System.Net.Sockets.SocketAsyncEventArgs)"> <inheritdoc /> </member> - <member name="M:NetSharp.StreamNetworkWriter.ResetStateObject(System.Net.Sockets.SocketAsyncEventArgs@)"> + <member name="M:NetSharp.Raw.Stream.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)"> + <member name="M:NetSharp.Raw.Stream.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)"> + <member name="M:NetSharp.Raw.Stream.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)"> + <member name="M:NetSharp.Raw.Stream.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)"> + <member name="M:NetSharp.Raw.Stream.StreamNetworkWriter.WriteAsync(System.Net.EndPoint,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)"> <inheritdoc /> </member> <member name="T:NetSharp.Utils.BiDictionary`2"> @@ -1317,4 +676,4 @@ </returns> </member> </members> -</doc> -\ No newline at end of file +</doc> diff --git a/NetSharp/NetSharp/NetworkConnectionBase.cs b/NetSharp/NetSharp/NetworkConnectionBase.cs @@ -1,63 +0,0 @@ -using NetSharp.Utils; - -using System; -using System.Buffers; -using System.Net; -using System.Net.Sockets; - -namespace NetSharp -{ - public abstract class NetworkConnectionBase<TState> : IDisposable where TState : class - { - 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, EndPoint defaultEndPoint, int maxPooledBufferSize, - int maxPooledBuffersPerBucket = 1000, uint preallocatedStateObjects = 0) - { - Connection = rawConnection; - - BufferSize = maxPooledBufferSize; - 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(ref TState instance); - - protected abstract TState CreateStateObject(); - - protected abstract void DestroyStateObject(TState instance); - - /// <summary> - /// Allows for inheritors to dispose of their own resources. - /// </summary> - protected virtual void Dispose(bool disposing) - { - if (!disposing) return; - - StateObjectPool.Dispose(); - } - - protected abstract void ResetStateObject(ref TState instance); - - /// <inheritdoc /> - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/NetworkReaderBase.cs b/NetSharp/NetSharp/NetworkReaderBase.cs @@ -1,53 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading; - -namespace NetSharp -{ - public delegate bool NetworkRequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, - Memory<byte> responseBuffer); - - public abstract class NetworkReaderBase<TState> : NetworkConnectionBase<TState> where TState : class - { - private readonly CancellationTokenSource shutdownTokenSource; - - protected readonly NetworkRequestHandler RequestHandler; - protected readonly CancellationToken ShutdownToken; - - /// <inheritdoc /> - 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; - - RequestHandler = requestHandler ?? DefaultRequestHandler; - } - - /// <inheritdoc /> - protected override void Dispose(bool disposing) - { - if (!disposing) return; - - shutdownTokenSource.Cancel(); - shutdownTokenSource.Dispose(); - - 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() - { - shutdownTokenSource.Cancel(); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/NetworkWriterBase.cs b/NetSharp/NetSharp/NetworkWriterBase.cs @@ -1,28 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading.Tasks; - -namespace NetSharp -{ - public abstract class NetworkWriterBase<TState> : NetworkConnectionBase<TState> where TState : class - { - /// <inheritdoc /> - 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 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); - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Raw/Datagram/DatagramNetworkReader.cs b/NetSharp/NetSharp/Raw/Datagram/DatagramNetworkReader.cs @@ -0,0 +1,156 @@ +using System.Net; +using System.Net.Sockets; + +namespace NetSharp.Raw.Datagram +{ + public sealed class DatagramNetworkReader : NetworkReaderBase<SocketAsyncEventArgs> + { + /// <inheritdoc /> + 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) + { + byte[] receiveBuffer = args.Buffer; + + switch (args.SocketError) + { + case SocketError.Success: + byte[] responseBuffer = BufferPool.Rent(BufferSize); + + bool responseExists = + RequestHandler(args.RemoteEndPoint, receiveBuffer, args.BytesTransferred, responseBuffer); + BufferPool.Return(receiveBuffer, true); + + if (responseExists) + { + args.SetBuffer(responseBuffer, 0, BufferSize); + + StartSendTo(args); + + return; + } + + BufferPool.Return(responseBuffer, true); + break; + + default: + BufferPool.Return(receiveBuffer, true); + StateObjectPool.Return(args); + break; + } + } + + private void CompleteSendTo(SocketAsyncEventArgs args) + { + byte[] sendBuffer = args.Buffer; + + BufferPool.Return(sendBuffer, true); + StateObjectPool.Return(args); + } + + private void HandleIoCompleted(object sender, SocketAsyncEventArgs args) + { + switch (args.LastOperation) + { + case SocketAsyncOperation.ReceiveFrom: + StartDefaultReceiveFrom(); + + CompleteReceiveFrom(args); + break; + + case SocketAsyncOperation.SendTo: + CompleteSendTo(args); + break; + } + } + + private void StartDefaultReceiveFrom() + { + if (ShutdownToken.IsCancellationRequested) + { + return; + } + + SocketAsyncEventArgs args = StateObjectPool.Rent(); + StartReceiveFrom(args); + } + + private void StartReceiveFrom(SocketAsyncEventArgs args) + { + byte[] receiveBuffer = BufferPool.Rent(BufferSize); + + if (ShutdownToken.IsCancellationRequested) + { + BufferPool.Return(receiveBuffer, true); + StateObjectPool.Return(args); + + return; + } + + args.SetBuffer(receiveBuffer, 0, BufferSize); + + if (Connection.ReceiveFromAsync(args)) return; + + StartDefaultReceiveFrom(); + CompleteReceiveFrom(args); + } + + private void StartSendTo(SocketAsyncEventArgs args) + { + byte[] sendBuffer = args.Buffer; + + if (ShutdownToken.IsCancellationRequested) + { + BufferPool.Return(sendBuffer, true); + StateObjectPool.Return(args); + + return; + } + + if (Connection.SendToAsync(args)) return; + + CompleteSendTo(args); + } + + /// <inheritdoc /> + protected override bool CanReuseStateObject(ref SocketAsyncEventArgs instance) + { + return true; + } + + /// <inheritdoc /> + protected override SocketAsyncEventArgs CreateStateObject() + { + SocketAsyncEventArgs instance = new SocketAsyncEventArgs { RemoteEndPoint = DefaultEndPoint }; + instance.Completed += HandleIoCompleted; + + return instance; + } + + /// <inheritdoc /> + protected override void DestroyStateObject(SocketAsyncEventArgs instance) + { + instance.Completed -= HandleIoCompleted; + instance.Dispose(); + } + + /// <inheritdoc /> + protected override void ResetStateObject(ref SocketAsyncEventArgs instance) + { + instance.RemoteEndPoint = DefaultEndPoint; + } + + /// <inheritdoc /> + public override void Start(ushort concurrentReadTasks) + { + for (ushort i = 0; i < concurrentReadTasks; i++) + { + StartDefaultReceiveFrom(); + } + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Raw/Datagram/DatagramNetworkWriter.cs b/NetSharp/NetSharp/Raw/Datagram/DatagramNetworkWriter.cs @@ -0,0 +1,260 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; + +namespace NetSharp.Raw.Datagram +{ + public sealed class DatagramNetworkWriter : NetworkWriterBase<SocketAsyncEventArgs> + { + /// <inheritdoc /> + 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(ref SocketAsyncEventArgs instance) + { + return true; + } + + /// <inheritdoc /> + protected override SocketAsyncEventArgs CreateStateObject() + { + SocketAsyncEventArgs instance = new SocketAsyncEventArgs(); + instance.Completed += HandleIoCompleted; + + return instance; + } + + /// <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) + ); + } + + 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) + { + 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) + { + 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/Raw/NetworkConnectionBase.cs b/NetSharp/NetSharp/Raw/NetworkConnectionBase.cs @@ -0,0 +1,63 @@ +using NetSharp.Utils; + +using System; +using System.Buffers; +using System.Net; +using System.Net.Sockets; + +namespace NetSharp.Raw +{ + public abstract class NetworkConnectionBase<TState> : IDisposable where TState : class + { + 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, EndPoint defaultEndPoint, int maxPooledBufferSize, + int maxPooledBuffersPerBucket = 1000, uint preallocatedStateObjects = 0) + { + Connection = rawConnection; + + BufferSize = maxPooledBufferSize; + 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(ref TState instance); + + protected abstract TState CreateStateObject(); + + protected abstract void DestroyStateObject(TState instance); + + /// <summary> + /// Allows for inheritors to dispose of their own resources. + /// </summary> + protected virtual void Dispose(bool disposing) + { + if (!disposing) return; + + StateObjectPool.Dispose(); + } + + protected abstract void ResetStateObject(ref TState instance); + + /// <inheritdoc /> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Raw/NetworkReaderBase.cs b/NetSharp/NetSharp/Raw/NetworkReaderBase.cs @@ -0,0 +1,53 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +namespace NetSharp.Raw +{ + public delegate bool NetworkRequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, int receivedRequestBytes, + Memory<byte> responseBuffer); + + public abstract class NetworkReaderBase<TState> : NetworkConnectionBase<TState> where TState : class + { + private readonly CancellationTokenSource shutdownTokenSource; + + protected readonly NetworkRequestHandler RequestHandler; + protected readonly CancellationToken ShutdownToken; + + /// <inheritdoc /> + 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; + + RequestHandler = requestHandler ?? DefaultRequestHandler; + } + + /// <inheritdoc /> + protected override void Dispose(bool disposing) + { + if (!disposing) return; + + shutdownTokenSource.Cancel(); + shutdownTokenSource.Dispose(); + + base.Dispose(disposing); + } + + public static bool DefaultRequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, int receivedRequestBytes, + Memory<byte> responseBuffer) + { + return requestBuffer.TryCopyTo(responseBuffer); + } + + public abstract void Start(ushort concurrentReadTasks); + + public void Stop() + { + shutdownTokenSource.Cancel(); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Raw/NetworkWriterBase.cs b/NetSharp/NetSharp/Raw/NetworkWriterBase.cs @@ -0,0 +1,28 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; + +namespace NetSharp.Raw +{ + public abstract class NetworkWriterBase<TState> : NetworkConnectionBase<TState> where TState : class + { + /// <inheritdoc /> + 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 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); + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Raw/Stream/StreamNetworkReader.cs b/NetSharp/NetSharp/Raw/Stream/StreamNetworkReader.cs @@ -0,0 +1,302 @@ +using System.Net; +using System.Net.Sockets; +using System.Runtime.CompilerServices; + +namespace NetSharp.Raw.Stream +{ + 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) + { + 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, totalReceivedBytes + receivedBytes, 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; + } + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/Raw/Stream/StreamNetworkWriter.cs b/NetSharp/NetSharp/Raw/Stream/StreamNetworkWriter.cs @@ -0,0 +1,347 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace NetSharp.Raw.Stream +{ + 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/Sockets/Datagram/DatagramSocketClient.cs b/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketClient.cs @@ -1,264 +0,0 @@ -using NetSharp.Packets; -using NetSharp.Utils; - -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharp.Sockets.Datagram -{ - /// <summary> - /// Provides additional configuration options for a <see cref="DatagramSocketClient" /> instance. - /// </summary> - public readonly struct DatagramSocketClientOptions - { - /// <summary> - /// The default configuration. - /// </summary> - public static readonly DatagramSocketClientOptions Defaults = - new DatagramSocketClientOptions(0); - - /// <summary> - /// The number of <see cref="SocketAsyncEventArgs" /> instances that should be preallocated for use in the - /// <see cref="DatagramSocketClient.SendToAsyncInternal" /> and <see cref="DatagramSocketClient.ReceiveFromAsyncInternal" /> methods. - /// </summary> - public readonly ushort PreallocatedTransmissionArgs; - - /// <summary> - /// Constructs a new instance of the <see cref="DatagramSocketClientOptions" /> struct. - /// </summary> - /// <param name="preallocatedTransmissionArgs"> - /// The number of <see cref="SocketAsyncEventArgs" /> instances to preallocate. - /// </param> - public DatagramSocketClientOptions(ushort preallocatedTransmissionArgs) - { - PreallocatedTransmissionArgs = preallocatedTransmissionArgs; - } - } - - //TODO address the need to handle series of network packets, not just single packets - //TODO document class - public sealed class DatagramSocketClient : RawSocketClient - { - private readonly DatagramSocketClientOptions clientOptions; - - public DatagramSocketClient(ref Socket rawConnection, in DatagramSocketClientOptions? clientOptions = null) - : base(ref rawConnection, - NetworkPacket.TotalSize, - clientOptions?.PreallocatedTransmissionArgs ?? DatagramSocketClientOptions.Defaults.PreallocatedTransmissionArgs) - { - if (rawConnection.SocketType != SocketType.Dgram) - { - throw new ArgumentException($"Only {SocketType.Dgram} is supported!", nameof(rawConnection)); - } - - this.clientOptions = clientOptions ?? DatagramSocketClientOptions.Defaults; - } - - public ref readonly DatagramSocketClientOptions ClientOptions - { - get { return ref clientOptions; } - } - - private void CompleteConnect(SocketAsyncEventArgs args) - { - AsyncOperationToken connectToken = (AsyncOperationToken)args.UserToken; - - if (connectToken.CancellationToken.IsCancellationRequested) return; - - switch (args.SocketError) - { - case SocketError.Success: - connectToken.CompletionSource.SetResult(true); - - break; - - case SocketError.OperationAborted: - break; - - default: - connectToken.CompletionSource.SetException(new SocketException((int)args.SocketError)); - - break; - } - - ArgsPool.Return(args); - } - - private void CompleteReceiveFrom(SocketAsyncEventArgs args) - { - AsyncReceiveToken receiveToken = (AsyncReceiveToken)args.UserToken; - - if (receiveToken.CancellationToken.IsCancellationRequested) return; - - switch (args.SocketError) - { - case SocketError.Success: - TransmissionResult result = new TransmissionResult(in args); - - receiveToken.CompletionSource.SetResult(result); - - break; - - case SocketError.OperationAborted: - break; - - default: - receiveToken.CompletionSource.SetException(new SocketException((int)args.SocketError)); - - break; - } - - ArgsPool.Return(args); - } - - private void CompleteSendTo(SocketAsyncEventArgs args) - { - AsyncSendToken sendToken = (AsyncSendToken)args.UserToken; - - if (sendToken.CancellationToken.IsCancellationRequested) return; - - switch (args.SocketError) - { - case SocketError.Success: - TransmissionResult result = new TransmissionResult(in args); - - sendToken.CompletionSource.SetResult(result); - - break; - - case SocketError.OperationAborted: - break; - - default: - sendToken.CompletionSource.SetException(new SocketException((int)args.SocketError)); - - break; - } - - BufferPool.Return(sendToken.RentedBuffer, true); - ArgsPool.Return(args); - } - - /// <inheritdoc /> - protected override bool CanTransmissionArgsBeReused(ref SocketAsyncEventArgs args) - { - return true; - } - - /// <inheritdoc /> - protected override SocketAsyncEventArgs CreateTransmissionArgs() - { - SocketAsyncEventArgs connectionArgs = new SocketAsyncEventArgs(); - - connectionArgs.Completed += HandleIoCompleted; - - return connectionArgs; - } - - /// <inheritdoc /> - protected override void DestroyTransmissionArgs(SocketAsyncEventArgs remoteConnectionArgs) - { - remoteConnectionArgs.Completed -= HandleIoCompleted; - - remoteConnectionArgs.Dispose(); - } - - /// <inheritdoc /> - protected override void HandleIoCompleted(object sender, SocketAsyncEventArgs args) - { - switch (args.LastOperation) - { - case SocketAsyncOperation.Connect: - CompleteConnect(args); - - break; - - case SocketAsyncOperation.ReceiveFrom: - CompleteReceiveFrom(args); - - break; - - case SocketAsyncOperation.SendTo: - CompleteSendTo(args); - - break; - - default: - throw new NotSupportedException($"{nameof(HandleIoCompleted)} doesn't support {args.LastOperation}"); - } - } - - /// <inheritdoc /> - protected override void ResetTransmissionArgs(ref SocketAsyncEventArgs args) - { - } - - /// <inheritdoc /> - public override TransmissionResult Receive(in EndPoint remoteEndPoint, byte[] receiveBuffer, SocketFlags flags = SocketFlags.None) - { - EndPoint actualEndPoint = remoteEndPoint; - int receivedBytes = Connection.ReceiveFrom(receiveBuffer, flags, ref actualEndPoint); - - return new TransmissionResult(in receiveBuffer, in receivedBytes, in actualEndPoint); - } - - /// <inheritdoc /> - public override ValueTask<TransmissionResult> ReceiveAsync(in EndPoint remoteEndPoint, Memory<byte> receiveBuffer, SocketFlags flags = SocketFlags.None) - { - TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); - - SocketAsyncEventArgs args = ArgsPool.Rent(); - - args.SetBuffer(receiveBuffer); - - args.RemoteEndPoint = remoteEndPoint; - args.SocketFlags = flags; - args.UserToken = new AsyncReceiveToken(in tcs, CancellationToken.None); - - if (Connection.ReceiveFromAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); - - TransmissionResult result = new TransmissionResult(in args); - - ArgsPool.Return(args); - - return new ValueTask<TransmissionResult>(result); - } - - /// <inheritdoc /> - public override TransmissionResult Send(in EndPoint remoteEndPoint, byte[] sendBuffer, SocketFlags flags = SocketFlags.None) - { - int sentBytes = Connection.SendTo(sendBuffer, flags, remoteEndPoint); - - return new TransmissionResult(in sendBuffer, in sentBytes, in remoteEndPoint); - } - - /// <inheritdoc /> - public override ValueTask<TransmissionResult> SendAsync(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> sendBuffer, SocketFlags flags = SocketFlags.None) - { - TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); - - SocketAsyncEventArgs args = ArgsPool.Rent(); - byte[] transmissionBuffer = BufferPool.Rent(sendBuffer.Length); - - sendBuffer.CopyTo(transmissionBuffer); - - args.SetBuffer(transmissionBuffer); - - args.RemoteEndPoint = remoteEndPoint; - args.SocketFlags = flags; - args.UserToken = new AsyncSendToken(in tcs, ref transmissionBuffer, CancellationToken.None); - - if (Connection.SendToAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); - - TransmissionResult result = new TransmissionResult(in args); - - BufferPool.Return(transmissionBuffer, true); - ArgsPool.Return(args); - - return new ValueTask<TransmissionResult>(result); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs b/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs @@ -1,264 +0,0 @@ -using NetSharp.Packets; - -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharp.Sockets.Datagram -{ - /// <summary> - /// Provides additional configuration options for a <see cref="DatagramSocketServer" /> instance. - /// </summary> - public readonly struct DatagramSocketServerOptions - { - /// <summary> - /// The default configuration. - /// </summary> - public static readonly DatagramSocketServerOptions Defaults = - new DatagramSocketServerOptions(Environment.ProcessorCount, 0); - - /// <summary> - /// The number of <see cref="Socket.ReceiveFromAsync" /> calls that will be 'in-flight' at any one time, and ready to service incoming client - /// packets. This should be set to the number of client which will be connected at once. - /// </summary> - public readonly int ConcurrentReceiveFromCalls; - - /// <summary> - /// The number of <see cref="SocketAsyncEventArgs" /> instances that should be preallocated for use in the <see cref="Socket.SendToAsync" /> - /// and <see cref="Socket.ReceiveFromAsync" /> methods. - /// </summary> - public readonly ushort PreallocatedTransmissionArgs; - - /// <summary> - /// Constructs a new instance of the <see cref="DatagramSocketServerOptions" /> struct. - /// </summary> - /// <param name="concurrentReceiveFromCalls"> - /// The number of <see cref="Socket.ReceiveFromAsync" /> calls which should be 'in-flight' at any one time. - /// </param> - /// <param name="preallocatedTransmissionArgs"> - /// The number of <see cref="SocketAsyncEventArgs" /> instances to preallocate. - /// </param> - public DatagramSocketServerOptions(int concurrentReceiveFromCalls, ushort preallocatedTransmissionArgs) - { - ConcurrentReceiveFromCalls = concurrentReceiveFromCalls; - - PreallocatedTransmissionArgs = preallocatedTransmissionArgs; - } - } - - //TODO address the need to handle series of network packets, not just single packets - //TODO document class - public sealed class DatagramSocketServer : RawSocketServer - { - private static readonly EndPoint AnyRemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); - - private readonly DatagramSocketServerOptions serverOptions; - - private CancellationToken serverShutdownToken; - - /// <summary> - /// Constructs a new instance of the <see cref="DatagramSocketServer" /> class. - /// </summary> - /// <param name="serverOptions"> - /// Additional options to configure the server. - /// </param> - /// <inheritdoc /> - public DatagramSocketServer(ref Socket rawConnection, in RawRequestPacketHandler packetHandler, in DatagramSocketServerOptions? serverOptions = null) - : base(ref rawConnection, - NetworkPacket.TotalSize, - serverOptions?.PreallocatedTransmissionArgs ?? DatagramSocketServerOptions.Defaults.PreallocatedTransmissionArgs, - packetHandler) - { - if (rawConnection.SocketType != SocketType.Dgram) - { - throw new ArgumentException($"Only {SocketType.Dgram} is supported!", nameof(rawConnection)); - } - - this.serverOptions = serverOptions ?? DatagramSocketServerOptions.Defaults; - } - - public ref readonly DatagramSocketServerOptions ServerOptions - { - get { return ref serverOptions; } - } - - private void CompleteReceiveFrom(SocketAsyncEventArgs receiveArgs) - { - SocketOperationToken receiveToken = (SocketOperationToken)receiveArgs.UserToken; - - if (receiveArgs.SocketError == SocketError.Success) - { - byte[] responseBuffer = BufferPool.Rent(MaxBufferSize); - - bool responseExists = PacketHandler(receiveArgs.RemoteEndPoint, receiveToken.RentedBuffer, responseBuffer); - BufferPool.Return(receiveToken.RentedBuffer, true); - - //NetworkPacket.Deserialise(receiveArgs.MemoryBuffer, out NetworkPacket request); - //NetworkPacket response = PacketHandler(in request, receiveArgs.RemoteEndPoint); - - if (responseExists) - { - //byte[] sendBuffer = BufferPool.Rent(NetworkPacket.TotalSize); - //Memory<byte> sendBufferMemory = new Memory<byte>(sendBuffer); - //NetworkPacket.Serialise(response, sendBufferMemory); - - receiveArgs.SetBuffer(responseBuffer); - receiveArgs.UserToken = new SocketOperationToken(ref responseBuffer); - - SendTo(receiveArgs); - } - else - { - BufferPool.Return(responseBuffer, true); - } - } - else - { - BufferPool.Return(receiveToken.RentedBuffer, true); - - ArgsPool.Return(receiveArgs); - } - } - - private void CompleteSendTo(SocketAsyncEventArgs sendArgs) - { - SocketOperationToken sendToken = (SocketOperationToken)sendArgs.UserToken; - - BufferPool.Return(sendToken.RentedBuffer, true); - - ArgsPool.Return(sendArgs); - } - - private void ReceiveFrom(SocketAsyncEventArgs receiveArgs) - { - if (serverShutdownToken.IsCancellationRequested) - { - ArgsPool.Return(receiveArgs); - - return; - } - - byte[] receiveBuffer = BufferPool.Rent(NetworkPacket.TotalSize); - Memory<byte> receiveBufferMemory = new Memory<byte>(receiveBuffer); - - receiveArgs.SetBuffer(receiveBufferMemory); - receiveArgs.UserToken = new SocketOperationToken(ref receiveBuffer); - - bool operationPending = Connection.ReceiveFromAsync(receiveArgs); - - if (operationPending) return; - - SocketAsyncEventArgs newReceiveArgs = ArgsPool.Rent(); - newReceiveArgs.RemoteEndPoint = AnyRemoteEndPoint; - - ReceiveFrom(newReceiveArgs); // start a new receive from operation immediately, to not drop any packets - - CompleteReceiveFrom(receiveArgs); - } - - private void SendTo(SocketAsyncEventArgs sendArgs) - { - if (serverShutdownToken.IsCancellationRequested) - { - SocketOperationToken sendToken = (SocketOperationToken)sendArgs.UserToken; - - BufferPool.Return(sendToken.RentedBuffer, true); - - ArgsPool.Return(sendArgs); - - return; - } - - bool operationPending = Connection.SendToAsync(sendArgs); - - if (!operationPending) - { - CompleteSendTo(sendArgs); - } - } - - /// <inheritdoc /> - protected override bool CanTransmissionArgsBeReused(ref SocketAsyncEventArgs args) - { - return true; - } - - /// <inheritdoc /> - protected override SocketAsyncEventArgs CreateTransmissionArgs() - { - SocketAsyncEventArgs connectionArgs = new SocketAsyncEventArgs(); - - connectionArgs.Completed += HandleIoCompleted; - - return connectionArgs; - } - - /// <inheritdoc /> - protected override void DestroyTransmissionArgs(SocketAsyncEventArgs remoteConnectionArgs) - { - remoteConnectionArgs.Completed -= HandleIoCompleted; - - remoteConnectionArgs.Dispose(); - } - - /// <inheritdoc /> - protected override void HandleIoCompleted(object sender, SocketAsyncEventArgs args) - { - switch (args.LastOperation) - { - case SocketAsyncOperation.ReceiveFrom: - SocketAsyncEventArgs newReceiveArgs = ArgsPool.Rent(); - newReceiveArgs.RemoteEndPoint = AnyRemoteEndPoint; - - ReceiveFrom(newReceiveArgs); // start a new receive from operation immediately, to not drop any packets - - CompleteReceiveFrom(args); - - break; - - case SocketAsyncOperation.SendTo: - CompleteSendTo(args); - - break; - - default: - throw new NotSupportedException($"{nameof(HandleIoCompleted)} doesn't support {args.LastOperation}"); - } - } - - /// <inheritdoc /> - protected override void ResetTransmissionArgs(ref SocketAsyncEventArgs args) - { - } - - /// <inheritdoc /> - public override Task RunAsync(CancellationToken cancellationToken = default) - { - serverShutdownToken = cancellationToken; - - for (int i = 0; i < serverOptions.ConcurrentReceiveFromCalls; i++) - { - SocketAsyncEventArgs newReceiveArgs = ArgsPool.Rent(); - newReceiveArgs.RemoteEndPoint = AnyRemoteEndPoint; - - ReceiveFrom(newReceiveArgs); - } - - serverShutdownToken.WaitHandle.WaitOne(); - - return Task.CompletedTask; - } - - private readonly struct SocketOperationToken - { - public readonly byte[] RentedBuffer; - - public SocketOperationToken(ref byte[] rentedBuffer) - { - RentedBuffer = rentedBuffer; - } - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/RawSocketClient.cs b/NetSharp/NetSharp/Sockets/RawSocketClient.cs @@ -1,307 +0,0 @@ -using NetSharp.Utils; - -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharp.Sockets -{ - /// <summary> - /// Abstract base class for clients. - /// </summary> - /// TODO implement proper memory leak-free cancellation of network IO operations - public abstract class RawSocketClient : SocketConnectionBase - { - /// <summary> - /// 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. - /// </param> - /// <param name="pooledBufferMaxSize"> - /// The maximum size in bytes of buffers held in the buffer pool. - /// </param> - /// <param name="preallocatedTransmissionArgs"> - /// The number of transmission args to preallocate. - /// </param> - protected RawSocketClient(ref Socket rawConnection, int pooledBufferMaxSize, ushort preallocatedTransmissionArgs) - : base(ref rawConnection, pooledBufferMaxSize, preallocatedTransmissionArgs) - { - } - - /// <summary> - /// Callback for the cancellation of an asynchronous network operation. - /// </summary> - /// <param name="state"> - /// The <see cref="SocketAsyncEventArgs" /> state object for the operation. - /// </param> - protected void CancelAsyncOperationCallback(object state) - { - SocketAsyncEventArgs args = (SocketAsyncEventArgs)state; - - AsyncOperationToken token = (AsyncOperationToken)args.UserToken; - - token.CompletionSource.SetResult(false); - - DestroyTransmissionArgs(args); - } - - /// <summary> - /// Callback for the cancellation of an asynchronous network receive operation. - /// </summary> - /// <param name="state"> - /// The <see cref="SocketAsyncEventArgs" /> state object for the operation. - /// </param> - protected void CancelAsyncReceiveCallback(object state) - { - SocketAsyncEventArgs args = (SocketAsyncEventArgs)state; - - AsyncReceiveToken token = (AsyncReceiveToken)args.UserToken; - - token.CompletionSource.SetResult(TransmissionResult.Timeout); - - DestroyTransmissionArgs(args); - } - - /// <summary> - /// Callback for the cancellation of an asynchronous network send operation. - /// </summary> - /// <param name="state"> - /// The <see cref="SocketAsyncEventArgs" /> state object for the operation. - /// </param> - protected void CancelAsyncSendCallback(object state) - { - SocketAsyncEventArgs args = (SocketAsyncEventArgs)state; - - AsyncSendToken token = (AsyncSendToken)args.UserToken; - - token.CompletionSource.SetResult(TransmissionResult.Timeout); - - BufferPool.Return(token.RentedBuffer, true); - DestroyTransmissionArgs(args); - } - - /// <summary> - /// Connects the client to the specified end point. If called on a <see cref="SocketType.Dgram" />-based client, this method configures the - /// default remote host, and the client will ignore any packets not coming from this default host (i.e the given <paramref name="remoteEndPoint" />). - /// </summary> - /// <param name="remoteEndPoint"> - /// The remote end point which to which to connect the client. - /// </param> - public void Connect(in EndPoint remoteEndPoint) - { - Connection.Connect(remoteEndPoint); - } - - /// <summary> - /// Asynchronously connects the client to the specified end point. If called on a <see cref="SocketType.Dgram" />-based client, this method - /// configures the default remote host, and the client will ignore any packets not coming from this default host (i.e the given <paramref name="remoteEndPoint" />). - /// </summary> - /// <param name="remoteEndPoint"> - /// The remote end point which to which to connect the client. - /// </param> - /// <returns> - /// A <see cref="ValueTask" /> representing the connection attempt. - /// </returns> - public ValueTask ConnectAsync(in EndPoint remoteEndPoint) - { - TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); - - SocketAsyncEventArgs args = ArgsPool.Rent(); - - args.RemoteEndPoint = remoteEndPoint; - args.UserToken = new AsyncOperationToken(in tcs, CancellationToken.None); - - if (Connection.ConnectAsync(args)) return new ValueTask(tcs.Task); - - ArgsPool.Return(args); - - return new ValueTask(); - } - - /// <summary> - /// Listens for data from the specified endpoint, placing the data in the given buffer. On connection-oriented protocols, the given endpoint - /// is ignored in favour of the default remote host set up by a call to <see cref="Connect" /> or <see cref="ConnectAsync" />. - /// </summary> - /// <param name="remoteEndPoint"> - /// The remote endpoint from which data should be received. Ignored on connection-oriented protocols. - /// </param> - /// <param name="receiveBuffer"> - /// The buffer into which data is to be received. - /// </param> - /// <param name="flags"> - /// The socket flags associated with the send operation. - /// </param> - /// <returns> - /// The result of the receive operation. - /// </returns> - public abstract TransmissionResult Receive(in EndPoint remoteEndPoint, byte[] receiveBuffer, - SocketFlags flags = SocketFlags.None); - - /// <summary> - /// Asynchronously listens for data from the specified endpoint, placing the data in the given buffer. On connection-oriented protocols, the - /// given endpoint is ignored in favour of the default remote host set up by a call to <see cref="Connect" /> or <see cref="ConnectAsync" />. - /// </summary> - /// <param name="remoteEndPoint"> - /// The remote endpoint from which data should be received. Ignored on connection-oriented protocols. - /// </param> - /// <param name="receiveBuffer"> - /// The buffer into which data is to be received. - /// </param> - /// <param name="flags"> - /// The socket flags associated with the send operation. - /// </param> - /// <returns> - /// The result of the asynchronous receive operation. - /// </returns> - public abstract ValueTask<TransmissionResult> ReceiveAsync(in EndPoint remoteEndPoint, Memory<byte> receiveBuffer, - SocketFlags flags = SocketFlags.None); - - /// <summary> - /// Sends the data in the given buffer to the specified endpoint. On connection-oriented protocols, the given endpoint is ignored in favour of - /// the default remote host set up by a call to <see cref="Connect" /> or <see cref="ConnectAsync" />. - /// </summary> - /// <param name="remoteEndPoint"> - /// The remote endpoint to which data should be sent. Ignored on connection-oriented protocols. - /// </param> - /// <param name="sendBuffer"> - /// The buffer containing the outgoing data to be sent. - /// </param> - /// <param name="flags"> - /// The socket flags associated with the send operation. - /// </param> - /// <returns> - /// The result of the send operation. - /// </returns> - public abstract TransmissionResult Send(in EndPoint remoteEndPoint, byte[] sendBuffer, - SocketFlags flags = SocketFlags.None); - - /// <summary> - /// Asynchronously sends the data in the given buffer to the specified endpoint. On connection-oriented protocols, the given endpoint is - /// ignored in favour of the default remote host set up by a call to <see cref="Connect" /> or <see cref="ConnectAsync" />. - /// </summary> - /// <param name="remoteEndPoint"> - /// The remote endpoint to which data should be sent. Ignored on connection-oriented protocols. - /// </param> - /// <param name="sendBuffer"> - /// The buffer containing the outgoing data to be sent. The contents of the buffer are copied to an internally maintained buffer when the call - /// is made. - /// </param> - /// <param name="flags"> - /// The socket flags associated with the send operation. - /// </param> - /// <returns> - /// The result of the asynchronous send operation. - /// </returns> - public abstract ValueTask<TransmissionResult> SendAsync(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> sendBuffer, - SocketFlags flags = SocketFlags.None); - - /// <summary> - /// A state token for asynchronous socket operations. - /// </summary> - protected readonly struct AsyncOperationToken - { - /// <summary> - /// The <see cref="System.Threading.CancellationToken" /> associated with the socket operation. - /// </summary> - public readonly CancellationToken CancellationToken; - - /// <summary> - /// The completion source which wraps the event-based APM, and provides an awaitable <see cref="Task" />. - /// </summary> - public readonly TaskCompletionSource<bool> CompletionSource; - - /// <summary> - /// Constructs a new instance of the <see cref="AsyncOperationToken" /> struct. - /// </summary> - /// <param name="completionSource"> - /// The completion source to trigger when the socket operation completes. - /// </param> - /// <param name="cancellationToken"> - /// The cancellation token to observe during the operation. - /// </param> - public AsyncOperationToken(in TaskCompletionSource<bool> completionSource, in CancellationToken cancellationToken) - { - CompletionSource = completionSource; - - CancellationToken = cancellationToken; - } - } - - /// <summary> - /// A state token for asynchronous incoming network IO operations. - /// </summary> - protected readonly struct AsyncReceiveToken - { - /// <summary> - /// The <see cref="System.Threading.CancellationToken" /> associated with the network IO operation. - /// </summary> - public readonly CancellationToken CancellationToken; - - /// <summary> - /// The completion source which wraps the event-based APM, and provides an awaitable <see cref="Task" />. - /// </summary> - public readonly TaskCompletionSource<TransmissionResult> CompletionSource; - - /// <summary> - /// Constructs a new instance of the <see cref="AsyncReceiveToken" /> struct. - /// </summary> - /// <param name="completionSource"> - /// The completion source to trigger when the IO operation completes. - /// </param> - /// <param name="cancellationToken"> - /// The cancellation token to observe during the operation. - /// </param> - public AsyncReceiveToken(in TaskCompletionSource<TransmissionResult> completionSource, in CancellationToken cancellationToken) - { - CompletionSource = completionSource; - - CancellationToken = cancellationToken; - } - } - - /// <summary> - /// A state token for asynchronous outgoing network IO operations. - /// </summary> - protected readonly struct AsyncSendToken - { - /// <summary> - /// The <see cref="System.Threading.CancellationToken" /> associated with the network IO operation. - /// </summary> - public readonly CancellationToken CancellationToken; - - /// <summary> - /// The completion source which wraps the event-based APM, and provides an awaitable <see cref="Task" />. - /// </summary> - public readonly TaskCompletionSource<TransmissionResult> CompletionSource; - - /// <summary> - /// The rented buffer which holds the user's data. - /// </summary> - public readonly byte[] RentedBuffer; - - /// <summary> - /// Constructs a new instance of the <see cref="AsyncSendToken" /> struct. - /// </summary> - /// <param name="completionSource"> - /// The completion source to trigger when the IO operation completes. - /// </param> - /// <param name="bufferHandle"> - /// The buffer holding the user's data. - /// </param> - /// <param name="cancellationToken"> - /// The cancellation token to observe during the operation. - /// </param> - public AsyncSendToken(in TaskCompletionSource<TransmissionResult> completionSource, ref byte[] bufferHandle, in CancellationToken cancellationToken) - { - CompletionSource = completionSource; - - RentedBuffer = bufferHandle; - - CancellationToken = cancellationToken; - } - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/RawSocketServer.cs b/NetSharp/NetSharp/Sockets/RawSocketServer.cs @@ -1,87 +0,0 @@ -using System; - -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharp.Sockets -{ - /// <summary> - /// Represents a method for serving request packets. This method should not throw any errors. - /// </summary> - /// <param name="clientEndPoint"> - /// The client from which the packet was received. - /// </param> - /// <param name="requestBuffer"> - /// The buffer holding the data received from the client. - /// </param> - /// <param name="responseBuffer"> - /// The buffer into which the optional response should be written. - /// </param> - /// <returns> - /// Whether a response was generated which should be sent back to the client. - /// </returns> - public delegate bool RawRequestPacketHandler(in EndPoint clientEndPoint, ReadOnlyMemory<byte> requestBuffer, Memory<byte> responseBuffer); - - /// <summary> - /// Abstract base class for servers. - /// </summary> - public abstract class RawSocketServer : SocketConnectionBase - { - /// <summary> - /// The packet handler delegate to use to respond to incoming requests. - /// </summary> - protected readonly RawRequestPacketHandler PacketHandler; - - /// <summary> - /// 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. - /// </param> - /// <param name="packetHandler"> - /// The packet handler delegate to use to respond to incoming requests. - /// </param> - /// <param name="pooledBufferMaxSize"> - /// The maximum size in bytes of buffers held in the buffer pool. - /// </param> - /// <param name="preallocatedTransmissionArgs"> - /// The number of transmission args to preallocate. - /// </param> - protected RawSocketServer(ref Socket rawConnection, int pooledBufferMaxSize, ushort preallocatedTransmissionArgs, RawRequestPacketHandler? packetHandler) - : base(ref rawConnection, pooledBufferMaxSize, preallocatedTransmissionArgs) - { - PacketHandler = packetHandler ?? DefaultRawPacketHandler; - } - - /// <summary> - /// The default request handler for servers. Simply echoes back any received data. - /// </summary> - /// <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> - /// <returns> - /// Whether to send back a response. - /// </returns> - public static bool DefaultRawPacketHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, - Memory<byte> responseBuffer) => requestBuffer.TryCopyTo(responseBuffer); - - /// <summary> - /// Runs the server, handling requests from clients, until the <paramref name="cancellationToken" /> has its cancellation requested. - /// </summary> - /// <param name="cancellationToken"> - /// The <see cref="CancellationToken" /> upon whose cancellation the server should shut down. - /// </param> - /// <returns> - /// A <see cref="Task" /> representing the server's execution. - /// </returns> - public abstract Task RunAsync(CancellationToken cancellationToken = default); - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/SocketConnectionBase.cs b/NetSharp/NetSharp/Sockets/SocketConnectionBase.cs @@ -1,174 +0,0 @@ -using NetSharp.Utils; - -using System; -using System.Buffers; -using System.Net; -using System.Net.Sockets; - -namespace NetSharp.Sockets -{ - /// <summary> - /// Abstract base class for client and server wrappers around existing <see cref="Socket" /> objects. - /// </summary> - public abstract class SocketConnectionBase : IDisposable - { - /// <summary> - /// 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 SlimObjectPool<SocketAsyncEventArgs> ArgsPool; - - /// <summary> - /// Pools arrays to function as temporary buffers during network read/write operations. - /// </summary> - protected readonly ArrayPool<byte> BufferPool; - - /// <summary> - /// The maximum size of buffer that can be rented from the pool. - /// </summary> - protected readonly int MaxBufferSize; - - /// <summary> - /// The underlying <see cref="Socket" /> which provides access to network operations. - /// </summary> - protected Socket Connection; - - /// <summary> - /// 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. - /// </param> - /// <param name="pooledBufferMaxSize"> - /// The maximum size in bytes of buffers held in the buffer pool. - /// </param> - /// <param name="preallocatedTransmissionArgs"> - /// The number of <see cref="SocketAsyncEventArgs" /> objects to initially preallocate. - /// </param> - private protected SocketConnectionBase(ref Socket rawConnection, int pooledBufferMaxSize, ushort preallocatedTransmissionArgs) - { - Connection = rawConnection; - - MaxBufferSize = pooledBufferMaxSize; - BufferPool = ArrayPool<byte>.Create(MaxBufferSize, 1000); - - ArgsPool = new SlimObjectPool<SocketAsyncEventArgs>(CreateTransmissionArgs, - ResetTransmissionArgs, DestroyTransmissionArgs, CanTransmissionArgsBeReused); - - // TODO refactor into a cleaner structure, with a better method of seeding the object pool - for (ushort i = 0; i < preallocatedTransmissionArgs; i++) - { - SocketAsyncEventArgs args = CreateTransmissionArgs(); - - ArgsPool.Return(args); - } - } - - /// <summary> - /// The local endpoint to which the underlying <see cref="Socket" /> is bound. - /// </summary> - public EndPoint LocalEndPoint - { - get { return Connection.LocalEndPoint; } - } - - /// <summary> - /// Delegate method used to check whether the given used <see cref="SocketAsyncEventArgs" /> instance can be reused by the - /// <see cref="ArgsPool" />. If this method returns <c>true</c>, <see cref="ResetTransmissionArgs" /> is called on the given - /// <paramref name="args" />. Otherwise, <see cref="DestroyTransmissionArgs" /> is called. - /// </summary> - /// <param name="args"> - /// The <see cref="SocketAsyncEventArgs" /> instance to check. - /// </param> - /// <returns> - /// Whether the given <paramref name="args" /> should be reset and reused, or should be destroyed. - /// </returns> - 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 - /// <see cref="SocketAsyncEventArgs.Completed" /> event. - /// </summary> - /// <returns> - /// The configured <see cref="SocketAsyncEventArgs" /> instance. - /// </returns> - 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. - /// </summary> - /// <param name="remoteConnectionArgs"> - /// The <see cref="SocketAsyncEventArgs" /> which should be destroyed. - /// </param> - protected abstract void DestroyTransmissionArgs(SocketAsyncEventArgs remoteConnectionArgs); - - /// <summary> - /// Disposes of managed and unmanaged resources used by the <see cref="SocketConnectionBase" /> class. - /// </summary> - /// <param name="disposing"> - /// Whether this call was made by a call to <see cref="Dispose()" />. - /// </param> - protected virtual void Dispose(bool disposing) - { - if (!disposing) return; - - ArgsPool.Dispose(); - } - - /// <summary> - /// Delegate method to handle asynchronous network IO completion via the <see cref="SocketAsyncEventArgs.Completed" /> event. - /// </summary> - /// <param name="sender"> - /// The object which raised the event. - /// </param> - /// <param name="args"> - /// The <see cref="SocketAsyncEventArgs" /> instance associated with the asynchronous network IO. - /// </param> - protected abstract void HandleIoCompleted(object sender, SocketAsyncEventArgs args); - - /// <summary> - /// Delegate method used to reset used <see cref="SocketAsyncEventArgs" /> instances for later reuse by the <see cref="ArgsPool" />. - /// </summary> - /// <param name="args"> - /// The <see cref="SocketAsyncEventArgs" /> instance that should be reset. - /// </param> - protected abstract void ResetTransmissionArgs(ref SocketAsyncEventArgs args); - - /// <summary> - /// Binds the underlying socket. - /// </summary> - /// <param name="localEndPoint"> - /// The end point to which the socket should be bound. - /// </param> - public void Bind(in EndPoint localEndPoint) - { - Connection.Bind(localEndPoint); - } - - /// <inheritdoc /> - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// <summary> - /// Shuts down the underlying socket. - /// </summary> - /// <param name="how"> - /// Which socket transmission functions should be shut down on the socket. - /// </param> - public void Shutdown(SocketShutdown how) - { - try - { - Connection.Shutdown(how); - } - catch (SocketException) { } - - Connection.Close(); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/Stream/StreamSocketClient.cs b/NetSharp/NetSharp/Sockets/Stream/StreamSocketClient.cs @@ -1,392 +0,0 @@ -using NetSharp.Packets; -using NetSharp.Utils; - -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharp.Sockets.Stream -{ - /// <summary> - /// Provides additional configuration options for a <see cref="StreamSocketClient" /> instance. - /// </summary> - public readonly struct StreamSocketClientOptions - { - /// <summary> - /// The default configuration. - /// </summary> - public static readonly StreamSocketClientOptions Defaults = - new StreamSocketClientOptions(0); - - /// <summary> - /// The number of <see cref="SocketAsyncEventArgs" /> instances that should be preallocated for use in the - /// <see cref="StreamSocketClient.SendAsync" /> and <see cref="StreamSocketClient.ReceiveAsync" /> methods. - /// </summary> - public readonly ushort PreallocatedTransmissionArgs; - - /// <summary> - /// Constructs a new instance of the <see cref="StreamSocketClientOptions" /> struct. - /// </summary> - /// <param name="preallocatedTransmissionArgs"> - /// The number of <see cref="SocketAsyncEventArgs" /> instances to preallocate. - /// </param> - public StreamSocketClientOptions(ushort preallocatedTransmissionArgs) - { - PreallocatedTransmissionArgs = preallocatedTransmissionArgs; - } - } - - //TODO address the need to handle series of network packets, not just single packets - //TODO document class - public sealed class StreamSocketClient : RawSocketClient - { - private readonly StreamSocketClientOptions clientOptions; - - public StreamSocketClient(ref Socket rawConnection, in StreamSocketClientOptions? clientOptions = null) - : base(ref rawConnection, - NetworkPacket.TotalSize, - clientOptions?.PreallocatedTransmissionArgs ?? StreamSocketClientOptions.Defaults.PreallocatedTransmissionArgs) - { - if (rawConnection.SocketType != SocketType.Stream) - { - throw new ArgumentException($"Only {SocketType.Stream} is supported!", nameof(rawConnection)); - } - - this.clientOptions = clientOptions ?? StreamSocketClientOptions.Defaults; - } - - public ref readonly StreamSocketClientOptions ClientOptions - { - get { return ref clientOptions; } - } - - private void CompleteConnect(SocketAsyncEventArgs args) - { - AsyncOperationToken connectToken = (AsyncOperationToken)args.UserToken; - - if (connectToken.CancellationToken.IsCancellationRequested) return; - - switch (args.SocketError) - { - case SocketError.Success: - connectToken.CompletionSource.SetResult(true); - - break; - - case SocketError.OperationAborted: - break; - - default: - connectToken.CompletionSource.SetException(new SocketException((int)args.SocketError)); - - break; - } - - ArgsPool.Return(args); - } - - private void CompleteDisconnect(SocketAsyncEventArgs args) - { - AsyncOperationToken disconnectToken = (AsyncOperationToken)args.UserToken; - - if (disconnectToken.CancellationToken.IsCancellationRequested) return; - - switch (args.SocketError) - { - case SocketError.Success: - disconnectToken.CompletionSource.SetResult(true); - - break; - - case SocketError.OperationAborted: - break; - - default: - disconnectToken.CompletionSource.SetException(new SocketException((int)args.SocketError)); - - break; - } - - ArgsPool.Return(args); - } - - private void CompleteReceive(SocketAsyncEventArgs args) - { - AsyncReceiveToken receiveToken = (AsyncReceiveToken)args.UserToken; - - if (receiveToken.CancellationToken.IsCancellationRequested) return; - - switch (args.SocketError) - { - case SocketError.Success: - Memory<byte> transmissionBuffer = args.MemoryBuffer; - int expectedBytes = transmissionBuffer.Length; - - if (args.BytesTransferred == expectedBytes) - { - // buffer was fully received - - TransmissionResult result = new TransmissionResult(in args); - - receiveToken.CompletionSource.SetResult(result); - } - else if (expectedBytes > args.BytesTransferred && args.BytesTransferred > 0) - { - // receive the remaining parts of the buffer - - int receivedBytes = args.BytesTransferred; - - args.SetBuffer(receivedBytes, expectedBytes - receivedBytes); - - Connection.ReceiveAsync(args); - return; - } - else - { - // no bytes were received, remote socket is dead - - receiveToken.CompletionSource.SetException(new SocketException((int)SocketError.HostDown)); - } - - break; - - case SocketError.OperationAborted: - break; - - default: - receiveToken.CompletionSource.SetException(new SocketException((int)args.SocketError)); - break; - } - - ArgsPool.Return(args); - } - - private void CompleteSend(SocketAsyncEventArgs args) - { - AsyncSendToken sendToken = (AsyncSendToken)args.UserToken; - - if (sendToken.CancellationToken.IsCancellationRequested) return; - - switch (args.SocketError) - { - case SocketError.Success: - Memory<byte> transmissionBuffer = args.MemoryBuffer; - int remainingBytes = transmissionBuffer.Length; - - if (args.BytesTransferred == remainingBytes) - { - // buffer was fully sent - - TransmissionResult result = new TransmissionResult(in args); - - sendToken.CompletionSource.SetResult(result); - } - else if (remainingBytes > args.BytesTransferred && args.BytesTransferred > 0) - { - // send the remaining parts of the buffer - - int sentBytes = args.BytesTransferred; - - args.SetBuffer(sentBytes, remainingBytes - sentBytes); - - Connection.SendAsync(args); - return; - } - else - { - // no bytes were sent, remote socket is dead - - sendToken.CompletionSource.SetException(new SocketException((int)SocketError.HostDown)); - } - - break; - - case SocketError.OperationAborted: - break; - - default: - sendToken.CompletionSource.SetException(new SocketException((int)args.SocketError)); - break; - } - - BufferPool.Return(sendToken.RentedBuffer, true); - ArgsPool.Return(args); - } - - /// <inheritdoc /> - protected override bool CanTransmissionArgsBeReused(ref SocketAsyncEventArgs args) - { - return true; - } - - /// <inheritdoc /> - protected override SocketAsyncEventArgs CreateTransmissionArgs() - { - SocketAsyncEventArgs connectionArgs = new SocketAsyncEventArgs(); - - connectionArgs.Completed += HandleIoCompleted; - - return connectionArgs; - } - - /// <inheritdoc /> - protected override void DestroyTransmissionArgs(SocketAsyncEventArgs remoteConnectionArgs) - { - remoteConnectionArgs.Completed -= HandleIoCompleted; - - remoteConnectionArgs.Dispose(); - } - - /// <inheritdoc /> - protected override 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; - - default: - throw new NotSupportedException($"{nameof(HandleIoCompleted)} doesn't support {args.LastOperation}"); - } - } - - /// <inheritdoc /> - protected override void ResetTransmissionArgs(ref SocketAsyncEventArgs args) - { - } - - public void Disconnect(bool allowSocketReuse) - { - Connection.Disconnect(allowSocketReuse); - } - - public ValueTask DisconnectAsync(bool allowSocketReuse, CancellationToken cancellationToken = default) - { - TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); - - SocketAsyncEventArgs args = ArgsPool.Rent(); - - if (cancellationToken == default) - { - if (Connection.DisconnectAsync(args)) return new ValueTask(tcs.Task); - } - else - { - args.DisconnectReuseSocket = allowSocketReuse; - args.UserToken = new AsyncOperationToken(in tcs, in cancellationToken); - - // TODO find out why the fricc we leak memory - CancellationTokenRegistration cancellationRegistration = - cancellationToken.Register(CancelAsyncOperationCallback, args); - - if (Connection.DisconnectAsync(args)) - return new ValueTask( - tcs.Task.ContinueWith((task, state) => - { - ((CancellationTokenRegistration)state).Dispose(); - - return task.Result; - }, cancellationRegistration, CancellationToken.None) - ); - - cancellationRegistration.Dispose(); - } - - ArgsPool.Return(args); - - return new ValueTask(); - } - - /// <inheritdoc /> - public override TransmissionResult Receive(in EndPoint remoteEndPoint, byte[] receiveBuffer, SocketFlags flags = SocketFlags.None) - { - int expectedBytes = receiveBuffer.Length; - int receivedBytes = 0; - - do - { - receivedBytes += Connection.Receive(receiveBuffer, receivedBytes, expectedBytes - receivedBytes, flags); - } while (receivedBytes != 0 && receivedBytes < expectedBytes); - - return new TransmissionResult(in receiveBuffer, in receivedBytes, Connection.RemoteEndPoint); - } - - /// <inheritdoc /> - public override ValueTask<TransmissionResult> ReceiveAsync(in EndPoint remoteEndPoint, Memory<byte> receiveBuffer, SocketFlags flags = SocketFlags.None) - { - TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); - - SocketAsyncEventArgs args = ArgsPool.Rent(); - - args.SetBuffer(receiveBuffer); - - args.SocketFlags = flags; - args.UserToken = new AsyncReceiveToken(in tcs, CancellationToken.None); - - if (Connection.ReceiveAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); - - TransmissionResult result = new TransmissionResult(in args); - - ArgsPool.Return(args); - - return new ValueTask<TransmissionResult>(result); - } - - /// <inheritdoc /> - public override TransmissionResult Send(in EndPoint remoteEndPoint, byte[] sendBuffer, SocketFlags flags = SocketFlags.None) - { - int expectedBytes = sendBuffer.Length; - int sentBytes = 0; - - do - { - sentBytes += Connection.Send(sendBuffer, sentBytes, expectedBytes - sentBytes, flags); - } while (sentBytes != 0 && sentBytes < expectedBytes); - - return new TransmissionResult(in sendBuffer, in sentBytes, Connection.RemoteEndPoint); - } - - /// <inheritdoc /> - public override ValueTask<TransmissionResult> SendAsync(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> sendBuffer, SocketFlags flags = SocketFlags.None) - { - TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); - - SocketAsyncEventArgs args = ArgsPool.Rent(); - byte[] transmissionBuffer = BufferPool.Rent(sendBuffer.Length); - - sendBuffer.CopyTo(transmissionBuffer); - - args.SetBuffer(transmissionBuffer); - - args.SocketFlags = flags; - args.UserToken = new AsyncSendToken(in tcs, ref transmissionBuffer, CancellationToken.None); - - if (Connection.SendAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); - - TransmissionResult result = new TransmissionResult(in args); - - ArgsPool.Return(args); - - return new ValueTask<TransmissionResult>(result); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs b/NetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs @@ -1,333 +0,0 @@ -using NetSharp.Packets; - -using System; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharp.Sockets.Stream -{ - /// <summary> - /// Provides additional configuration options for a <see cref="StreamSocketServer" /> instance. - /// </summary> - public readonly struct StreamSocketServerOptions - { - /// <summary> - /// The default configuration. - /// </summary> - public static readonly StreamSocketServerOptions Defaults = - new StreamSocketServerOptions(Environment.ProcessorCount, 0); - - /// <summary> - /// The number of <see cref="Socket.AcceptAsync" /> calls that will be 'in-flight' at any one time, and ready to service incoming client - /// connection requests. This should be set according to the number of client which will be attempting to connect at once. - /// </summary> - public readonly int ConcurrentAcceptCalls; - - /// <summary> - /// The number of <see cref="SocketAsyncEventArgs" /> instances that should be preallocated for use in the <see cref="Socket.SendAsync" /> and - /// <see cref="Socket.ReceiveAsync" /> methods. - /// </summary> - public readonly ushort PreallocatedTransmissionArgs; - - /// <summary> - /// Constructs a new instance of the <see cref="StreamSocketServerOptions" /> struct. - /// </summary> - /// <param name="concurrentAcceptCalls"> - /// The number of <see cref="Socket.AcceptAsync" /> calls which should be 'in-flight' at any one time. - /// </param> - /// <param name="preallocatedTransmissionArgs"> - /// The number of <see cref="SocketAsyncEventArgs" /> instances to preallocate. - /// </param> - public StreamSocketServerOptions(int concurrentAcceptCalls, ushort preallocatedTransmissionArgs) - { - ConcurrentAcceptCalls = concurrentAcceptCalls; - - PreallocatedTransmissionArgs = preallocatedTransmissionArgs; - } - } - - //TODO address the need to handle series of network packets, not just single packets - //TODO document class - public sealed class StreamSocketServer : RawSocketServer - { - private readonly StreamSocketServerOptions serverOptions; - - /// <summary> - /// Constructs a new instance of the <see cref="StreamSocketServer" /> class. - /// </summary> - /// <param name="serverOptions"> - /// Additional options to configure the server. - /// </param> - /// <inheritdoc /> - public StreamSocketServer(ref Socket rawConnection, in RawRequestPacketHandler packetHandler, in StreamSocketServerOptions? serverOptions = null) - : base(ref rawConnection, - NetworkPacket.TotalSize, - serverOptions?.PreallocatedTransmissionArgs ?? StreamSocketServerOptions.Defaults.PreallocatedTransmissionArgs, - packetHandler) - { - if (rawConnection.SocketType != SocketType.Stream) - { - throw new ArgumentException($"Only {SocketType.Stream} is supported!", nameof(rawConnection)); - } - - this.serverOptions = serverOptions ?? StreamSocketServerOptions.Defaults; - } - - public ref readonly StreamSocketServerOptions ServerOptions - { - get { return ref serverOptions; } - } - - private void Accept(SocketAsyncEventArgs acceptArgs) - { - bool operationPending = Connection.AcceptAsync(acceptArgs); - - if (operationPending) return; - - SocketAsyncEventArgs newAcceptArgs = ArgsPool.Rent(); - - Accept(newAcceptArgs); // start a new accept operation to not miss any clients - - CompleteAccept(acceptArgs); - } - - private void CloseClientSocket(SocketAsyncEventArgs clientArgs) - { - RemoteStreamClientToken clientToken = (RemoteStreamClientToken)clientArgs.UserToken; - clientToken.Dispose(); - - ArgsPool.Return(clientArgs); - } - - private void CompleteAccept(SocketAsyncEventArgs connectedClientArgs) - { - Socket clientSocket = connectedClientArgs.AcceptSocket; - - RemoteStreamClientToken clientToken = new RemoteStreamClientToken(in clientSocket); - - connectedClientArgs.UserToken = clientToken; - - Receive(connectedClientArgs); - } - - private void CompleteReceive(SocketAsyncEventArgs clientArgs) - { - RemoteStreamClientToken receiveToken = (RemoteStreamClientToken)clientArgs.UserToken; - - if (clientArgs.SocketError == SocketError.Success) - { - if (clientArgs.BytesTransferred == NetworkPacket.TotalSize) - { - // buffer was fully received - byte[] responseBuffer = BufferPool.Rent(MaxBufferSize); - - bool responseExists = PacketHandler(clientArgs.RemoteEndPoint, receiveToken.RentedBuffer, responseBuffer); - BufferPool.Return(receiveToken.RentedBuffer, true); // at this point the request buffer can be returned - - //NetworkPacket.Deserialise(receiveToken.RentedBuffer, out NetworkPacket request); - //NetworkPacket response = PacketHandler(in request, clientArgs.RemoteEndPoint); - - if (responseExists) - { - //NetworkPacket.Serialise(response, responseBufferMemory); - - receiveToken.RentedBuffer = responseBuffer; - clientArgs.SetBuffer(responseBuffer, 0, NetworkPacket.TotalSize); - - Send(clientArgs); - } - else - { - BufferPool.Return(responseBuffer, true); - } - - Receive(clientArgs); - } - else if (NetworkPacket.TotalSize > clientArgs.BytesTransferred && clientArgs.BytesTransferred > 0) - { - // receive the remaining parts of the buffer - - int receivedBytes = clientArgs.BytesTransferred; - - clientArgs.SetBuffer(receivedBytes, NetworkPacket.TotalSize - receivedBytes); - - Receive(clientArgs); - } - else - { - // no bytes were received, remote socket is dead - - CloseClientSocket(clientArgs); - } - } - else - { - CloseClientSocket(clientArgs); - } - } - - private void CompleteSend(SocketAsyncEventArgs clientArgs) - { - RemoteStreamClientToken sendToken = (RemoteStreamClientToken)clientArgs.UserToken; - - if (clientArgs.SocketError == SocketError.Success) - { - if (clientArgs.BytesTransferred == NetworkPacket.TotalSize) - { - // buffer was fully sent - - BufferPool.Return(sendToken.RentedBuffer, true); - - sendToken.RentedBuffer = null; - - Receive(clientArgs); - } - else if (NetworkPacket.TotalSize > clientArgs.BytesTransferred && clientArgs.BytesTransferred > 0) - { - // send the remaining parts of the buffer - - int sentBytes = clientArgs.BytesTransferred; - - clientArgs.SetBuffer(sentBytes, NetworkPacket.TotalSize - sentBytes); - - Send(clientArgs); - } - else - { - // no bytes were sent, remote socket is dead - - CloseClientSocket(clientArgs); - } - } - else - { - CloseClientSocket(clientArgs); - } - } - - private void Receive(SocketAsyncEventArgs clientArgs) - { - RemoteStreamClientToken clientToken = (RemoteStreamClientToken)clientArgs.UserToken; - - byte[] requestBuffer = BufferPool.Rent(NetworkPacket.TotalSize); - Memory<byte> requestBufferMemory = new Memory<byte>(requestBuffer); - - clientToken.RentedBuffer = requestBuffer; - clientArgs.SetBuffer(clientToken.RentedBuffer, 0, NetworkPacket.TotalSize); - - bool operationPending = clientToken.ClientSocket.ReceiveAsync(clientArgs); - - if (!operationPending) - { - CompleteReceive(clientArgs); - } - } - - private void Send(SocketAsyncEventArgs clientArgs) - { - RemoteStreamClientToken clientToken = (RemoteStreamClientToken)clientArgs.UserToken; - - bool operationPending = clientToken.ClientSocket.SendAsync(clientArgs); - - if (!operationPending) - { - CompleteSend(clientArgs); - } - } - - /// <inheritdoc /> - protected override bool CanTransmissionArgsBeReused(ref SocketAsyncEventArgs args) - { - return true; - } - - /// <inheritdoc /> - protected override SocketAsyncEventArgs CreateTransmissionArgs() - { - SocketAsyncEventArgs connectionArgs = new SocketAsyncEventArgs(); - - connectionArgs.Completed += HandleIoCompleted; - - return connectionArgs; - } - - /// <inheritdoc /> - protected override void DestroyTransmissionArgs(SocketAsyncEventArgs remoteConnectionArgs) - { - remoteConnectionArgs.Completed -= HandleIoCompleted; - - remoteConnectionArgs.Dispose(); - } - - /// <inheritdoc /> - protected override void HandleIoCompleted(object sender, SocketAsyncEventArgs args) - { - switch (args.LastOperation) - { - case SocketAsyncOperation.Accept: - SocketAsyncEventArgs newAcceptArgs = ArgsPool.Rent(); - - Accept(newAcceptArgs); // start a new accept operation to not miss any clients - - CompleteAccept(args); - - break; - - case SocketAsyncOperation.Receive: - CompleteReceive(args); - - break; - - case SocketAsyncOperation.Send: - CompleteSend(args); - - break; - - default: - throw new NotSupportedException($"{nameof(HandleIoCompleted)} doesn't support {args.LastOperation}"); - } - } - - /// <inheritdoc /> - protected override void ResetTransmissionArgs(ref SocketAsyncEventArgs args) - { - } - - /// <inheritdoc /> - public override Task RunAsync(CancellationToken cancellationToken = default) - { - Connection.Listen(100); - - for (int i = 0; i < serverOptions.ConcurrentAcceptCalls; i++) - { - SocketAsyncEventArgs acceptArgs = ArgsPool.Rent(); - - Accept(acceptArgs); - } - - cancellationToken.WaitHandle.WaitOne(); - - return Task.CompletedTask; - } - - private class RemoteStreamClientToken : IDisposable - { - public readonly Socket ClientSocket; - - public byte[]? RentedBuffer; - - public RemoteStreamClientToken(in Socket clientSocket) - { - ClientSocket = clientSocket; - } - - public void Dispose() - { - ClientSocket.Shutdown(SocketShutdown.Both); - ClientSocket.Close(); - ClientSocket.Dispose(); - } - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/StreamNetworkConnection.cs b/NetSharp/NetSharp/StreamNetworkConnection.cs @@ -1,645 +0,0 @@ -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/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkReaderBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkReaderBenchmark.cs @@ -1,4 +1,4 @@ -using NetSharp; +using NetSharp.Raw.Datagram; using System; using System.Linq; @@ -11,7 +11,7 @@ namespace NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks { public class DatagramNetworkReaderBenchmark : INetSharpExample, INetSharpBenchmark { - private const int PacketSize = 8192, PacketCount = 100_000, ClientCount = 12; + private const int PacketSize = 8192, PacketCount = 1_000_000, ClientCount = 12; private double[] ClientBandwidths; public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); @@ -22,9 +22,11 @@ namespace NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks /// <inheritdoc /> public string Name { get; } = "Datagram Network Reader Benchmark"; - private static bool RequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, Memory<byte> responseBuffer) + private static bool RequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, int receivedRequestBytes, Memory<byte> responseBuffer) { - return requestBuffer.TryCopyTo(responseBuffer); + requestBuffer.CopyTo(responseBuffer); + + return true; } private Task BenchmarkClientTask(object idObj) @@ -100,7 +102,7 @@ namespace NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks await Task.WhenAll(clientTasks); - Console.WriteLine($"Total estimated bandwidth: {ClientBandwidths.Sum():F5}"); + Console.WriteLine($"Total estimated bandwidth: {ClientBandwidths.Sum():F3}"); reader.Stop(); diff --git a/NetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkWriterAsyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkWriterAsyncBenchmark.cs @@ -1,4 +1,4 @@ -using NetSharp; +using NetSharp.Raw.Datagram; using System; using System.Net; @@ -11,7 +11,7 @@ namespace NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks { public class DatagramNetworkWriterAsyncBenchmark : INetSharpExample, INetSharpBenchmark { - private const int PacketSize = 8192, PacketCount = 100_000; + private const int PacketSize = 8192, PacketCount = 1_000_000; public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); diff --git a/NetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkWriterSyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/Datagram Network Connection Benchmarks/DatagramNetworkWriterSyncBenchmark.cs @@ -1,4 +1,4 @@ -using NetSharp; +using NetSharp.Raw.Datagram; using System; using System.Net; @@ -11,7 +11,7 @@ namespace NetSharpExamples.Benchmarks.Datagram_Network_Connection_Benchmarks { public class DatagramNetworkWriterSyncBenchmark : INetSharpExample, INetSharpBenchmark { - private const int PacketSize = 8192, PacketCount = 100_000; + private const int PacketSize = 8192, PacketCount = 1_000_000; public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); diff --git a/NetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkReaderBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkReaderBenchmark.cs @@ -1,4 +1,4 @@ -using NetSharp; +using NetSharp.Raw.Stream; using System; using System.Linq; @@ -11,7 +11,7 @@ namespace NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks { public class StreamNetworkReaderBenchmark : INetSharpExample, INetSharpBenchmark { - private const int PacketSize = 8192, PacketCount = 100_000, ClientCount = 12; + private const int PacketSize = 8192, PacketCount = 1_000_000, ClientCount = 12; private double[] ClientBandwidths; public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); @@ -22,9 +22,11 @@ namespace NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks /// <inheritdoc /> public string Name { get; } = "Stream Network Reader Benchmark"; - private static bool RequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, Memory<byte> responseBuffer) + private static bool RequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, int receivedRequestBytes, Memory<byte> responseBuffer) { - return requestBuffer.TryCopyTo(responseBuffer); + requestBuffer.CopyTo(responseBuffer); + + return true; } private Task BenchmarkClientTask(object idObj) @@ -121,7 +123,7 @@ namespace NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks await Task.WhenAll(clientTasks); - Console.WriteLine($"Total estimated bandwidth: {ClientBandwidths.Sum():F5}"); + Console.WriteLine($"Total estimated bandwidth: {ClientBandwidths.Sum():F3}"); reader.Stop(); diff --git a/NetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkWriterAsyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkWriterAsyncBenchmark.cs @@ -1,4 +1,4 @@ -using NetSharp; +using NetSharp.Raw.Stream; using System; using System.Net; @@ -11,7 +11,7 @@ namespace NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks { public class StreamNetworkWriterAsyncBenchmark : INetSharpExample, INetSharpBenchmark { - private const int PacketSize = 8192, PacketCount = 100_000; + private const int PacketSize = 8192, PacketCount = 1_000_000; public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); diff --git a/NetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkWriterSyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/Stream Network Connection Benchmarks/StreamNetworkWriterSyncBenchmark.cs @@ -1,4 +1,4 @@ -using NetSharp; +using NetSharp.Raw.Stream; using System; using System.Net; @@ -11,7 +11,7 @@ namespace NetSharpExamples.Benchmarks.Stream_Network_Connection_Benchmarks { public class StreamNetworkWriterSyncBenchmark : INetSharpExample, INetSharpBenchmark { - private const int PacketSize = 8192, PacketCount = 100_000; + private const int PacketSize = 8192, PacketCount = 1_000_000; public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); diff --git a/NetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketClientAsyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/TCP Socket Connection Benchmarks/TcpSocketClientAsyncBenchmark.cs @@ -1,117 +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.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 @@ -1,117 +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.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 @@ -1,136 +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.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/UDP Socket Connection Benchmarks/UdpSocketClientAsyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/UDP Socket Connection Benchmarks/UdpSocketClientAsyncBenchmark.cs @@ -1,86 +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.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 @@ -1,95 +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.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 @@ -1,114 +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.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/Examples/TCP Socket Connection Examples/TcpSocketClientExample.cs b/NetSharp/NetSharpExamples/Examples/TCP Socket Connection Examples/TcpSocketClientExample.cs @@ -1,81 +0,0 @@ -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 @@ -1,54 +0,0 @@ -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/UDP Socket Connection Examples/UdpSocketClientExample.cs b/NetSharp/NetSharpExamples/Examples/UDP Socket Connection 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.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 @@ -1,54 +0,0 @@ -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/NetSharpExamples.xml b/NetSharp/NetSharpExamples/NetSharpExamples.xml @@ -40,96 +40,6 @@ <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.TCP_Socket_Connection_Benchmarks.TcpSocketClientAsyncBenchmark.Name"> - <inheritdoc /> - </member> - <member name="M:NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks.TcpSocketClientAsyncBenchmark.RunAsync"> - <inheritdoc /> - </member> - <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.TCP_Socket_Connection_Benchmarks.TcpSocketClientSyncBenchmark.Name"> - <inheritdoc /> - </member> - <member name="M:NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks.TcpSocketClientSyncBenchmark.RunAsync"> - <inheritdoc /> - </member> - <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.TCP_Socket_Connection_Benchmarks.TcpSocketServerBenchmark.Name"> - <inheritdoc /> - </member> - <member name="M:NetSharpExamples.Benchmarks.TCP_Socket_Connection_Benchmarks.TcpSocketServerBenchmark.RunAsync"> - <inheritdoc /> - </member> - <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.UDP_Socket_Connection_Benchmarks.UdpSocketClientAsyncBenchmark.Name"> - <inheritdoc /> - </member> - <member name="M:NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks.UdpSocketClientAsyncBenchmark.RunAsync"> - <inheritdoc /> - </member> - <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.UDP_Socket_Connection_Benchmarks.UdpSocketClientSyncBenchmark.Name"> - <inheritdoc /> - </member> - <member name="M:NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks.UdpSocketClientSyncBenchmark.RunAsync"> - <inheritdoc /> - </member> - <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.UDP_Socket_Connection_Benchmarks.UdpSocketServerBenchmark.Name"> - <inheritdoc /> - </member> - <member name="M:NetSharpExamples.Benchmarks.UDP_Socket_Connection_Benchmarks.UdpSocketServerBenchmark.RunAsync"> - <inheritdoc /> - </member> - <member name="P:NetSharpExamples.Examples.TCP_Socket_Connection_Examples.TcpSocketClientExample.Name"> - <inheritdoc /> - </member> - <member name="M:NetSharpExamples.Examples.TCP_Socket_Connection_Examples.TcpSocketClientExample.RunAsync"> - <inheritdoc /> - </member> - <member name="P:NetSharpExamples.Examples.TCP_Socket_Connection_Examples.TcpSocketServerExample.Name"> - <inheritdoc /> - </member> - <member name="M:NetSharpExamples.Examples.TCP_Socket_Connection_Examples.TcpSocketServerExample.RunAsync"> - <inheritdoc /> - </member> - <member name="P:NetSharpExamples.Examples.UDP_Socket_Connection_Examples.UdpSocketClientExample.Name"> - <inheritdoc /> - </member> - <member name="M:NetSharpExamples.Examples.UDP_Socket_Connection_Examples.UdpSocketClientExample.RunAsync"> - <inheritdoc /> - </member> - <member name="P:NetSharpExamples.Examples.UDP_Socket_Connection_Examples.UdpSocketServerExample.Name"> - <inheritdoc /> - </member> - <member name="M:NetSharpExamples.Examples.UDP_Socket_Connection_Examples.UdpSocketServerExample.RunAsync"> - <inheritdoc /> - </member> <member name="T:NetSharpExamples.INetSharpExample"> <summary> Defines an example program.