NetSharp

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

commit 7545617da2caa6168b44bf53b5e4873dc01ae6c4
parent 99fba2a642114c8a22cfe50f8a7d097cd885f47a
Author: Mikolaj Lenczewski <mikolaj.lenczewski308@gmail.com>
Date:   Thu, 30 Apr 2020 17:10:05 +0100

Restructuring again because im a masochist! (maybe stuff wont be as hard this time!)

Diffstat:
ANetSharp/NetSharp/DatagramNetworkConnection.cs | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MNetSharp/NetSharp/NetSharp.xml | 368+++++++++++++++++++++++++++++++++++++++++++------------------------------------
ANetSharp/NetSharp/NetworkConnectionBase.cs | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/NetworkReaderBase.cs | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/NetworkWriterBase.cs | 23+++++++++++++++++++++++
MNetSharp/NetSharp/Sockets/Datagram/DatagramSocketClient.cs | 86++++++++++++++++++++++---------------------------------------------------------
MNetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs | 63+++++++++++++++++++++++++++++++++++++--------------------------
ANetSharp/NetSharp/Sockets/RawSocketClient.cs | 308+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ANetSharp/NetSharp/Sockets/RawSocketServer.cs | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DNetSharp/NetSharp/Sockets/SocketClient.cs | 345-------------------------------------------------------------------------------
DNetSharp/NetSharp/Sockets/SocketConnection.cs | 178-------------------------------------------------------------------------------
ANetSharp/NetSharp/Sockets/SocketConnectionBase.cs | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DNetSharp/NetSharp/Sockets/SocketServer.cs | 91-------------------------------------------------------------------------------
MNetSharp/NetSharp/Sockets/Stream/StreamSocketClient.cs | 92+++++++++++++++++++++++--------------------------------------------------------
MNetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs | 48++++++++++++++++++++++++++----------------------
MNetSharp/NetSharp/Utils/SlimObjectPool.cs | 4++--
MNetSharp/NetSharpExamples/BenchmarkHelper.cs | 73+++++++++++++++++++++++++++++++++----------------------------------------
ANetSharp/NetSharpExamples/Benchmarks/DatagramNetworkReaderBenchmark.cs | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MNetSharp/NetSharpExamples/Benchmarks/TcpSocketClientAsyncBenchmark.cs | 12+++++-------
MNetSharp/NetSharpExamples/Benchmarks/TcpSocketClientSyncBenchmark.cs | 14++++++--------
MNetSharp/NetSharpExamples/Benchmarks/TcpSocketServerBenchmark.cs | 14+++++---------
MNetSharp/NetSharpExamples/Benchmarks/UdpSocketClientAsyncBenchmark.cs | 12+++++-------
MNetSharp/NetSharpExamples/Benchmarks/UdpSocketClientSyncBenchmark.cs | 12+++++-------
MNetSharp/NetSharpExamples/Benchmarks/UdpSocketServerBenchmark.cs | 16++++++++--------
MNetSharp/NetSharpExamples/Examples/TcpSocketClientExample.cs | 7++++++-
MNetSharp/NetSharpExamples/Examples/TcpSocketServerExample.cs | 14+++++++++-----
DNetSharp/NetSharpExamples/Examples/UdpSocketClientCancellationExample.cs | 102-------------------------------------------------------------------------------
MNetSharp/NetSharpExamples/Examples/UdpSocketClientExample.cs | 10+++++++---
MNetSharp/NetSharpExamples/Examples/UdpSocketServerExample.cs | 14+++++++++-----
MNetSharp/NetSharpExamples/NetSharpExamples.xml | 17++++++-----------
MNetSharp/NetSharpExamples/Program.cs | 4+++-
31 files changed, 1467 insertions(+), 1171 deletions(-)

diff --git a/NetSharp/NetSharp/DatagramNetworkConnection.cs b/NetSharp/NetSharp/DatagramNetworkConnection.cs @@ -0,0 +1,204 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +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 preallocatedStateObjects = 0) + : base(ref rawConnection, requestHandler, defaultEndPoint, maxPooledBufferSize, preallocatedStateObjects) + { + } + + private void CompleteReceiveFrom(SocketAsyncEventArgs args) + { + RentedBufferHandle receiveBufferHandle = (RentedBufferHandle) args.UserToken; + + switch (args.SocketError) + { + case SocketError.Success: + RentedBufferHandle responseBufferHandle = RentBuffer(BufferSize); + + bool responseExists = + RequestHandler(args.RemoteEndPoint, receiveBufferHandle.RentedBuffer, responseBufferHandle.RentedBuffer); + ReturnBuffer(receiveBufferHandle); + + if (responseExists) + { + args.SetBuffer(responseBufferHandle.RentedBuffer); + args.UserToken = responseBufferHandle; + + StartSendTo(args); + + return; + } + + ReturnBuffer(responseBufferHandle); + break; + + default: + ReturnBuffer(receiveBufferHandle); + StateObjectPool.Return(args); + break; + } + } + + private void CompleteSendTo(SocketAsyncEventArgs args) + { + RentedBufferHandle sendBufferHandle = (RentedBufferHandle)args.UserToken; + + ReturnBuffer(sendBufferHandle); + 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) + { + RentedBufferHandle receiveBufferHandle = RentBuffer(BufferSize); + + args.SetBuffer(receiveBufferHandle.RentedBuffer, 0, BufferSize); + args.UserToken = receiveBufferHandle; + + if (ShutdownToken.IsCancellationRequested) + { + ReturnBuffer(receiveBufferHandle); + StateObjectPool.Return(args); + + return; + } + + if (Connection.ReceiveFromAsync(args)) return; + + StartDefaultReceiveFrom(); + CompleteReceiveFrom(args); + } + + private void StartSendTo(SocketAsyncEventArgs args) + { + RentedBufferHandle sendBufferHandle = (RentedBufferHandle) args.UserToken; + + if (ShutdownToken.IsCancellationRequested) + { + ReturnBuffer(sendBufferHandle); + StateObjectPool.Return(args); + + return; + } + + if (Connection.SendToAsync(args)) return; + + CompleteSendTo(args); + } + + /// <inheritdoc /> + protected override bool CanReuseStateObject(in 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, int maxPooledBufferSize, int preallocatedStateObjects = 0) : base(ref rawConnection, maxPooledBufferSize, preallocatedStateObjects) + { + } + + /// <inheritdoc /> + protected override bool CanReuseStateObject(in SocketAsyncEventArgs instance) + { + throw new NotImplementedException(); + } + + /// <inheritdoc /> + protected override SocketAsyncEventArgs CreateStateObject() + { + throw new NotImplementedException(); + } + + /// <inheritdoc /> + protected override void DestroyStateObject(SocketAsyncEventArgs instance) + { + throw new NotImplementedException(); + } + + /// <inheritdoc /> + protected override void ResetStateObject(ref SocketAsyncEventArgs instance) + { + throw new NotImplementedException(); + } + + /// <inheritdoc /> + public override int Write(EndPoint remoteEndPoint, ReadOnlyMemory<byte> writeBuffer, SocketFlags flags = SocketFlags.None) + { + throw new NotImplementedException(); + } + + /// <inheritdoc /> + public override ValueTask<int> WriteAsync(EndPoint remoteEndPoint, ReadOnlyMemory<byte> writeBuffer, SocketFlags flags = SocketFlags.None) + { + throw new NotImplementedException(); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/NetSharp.xml b/NetSharp/NetSharp/NetSharp.xml @@ -4,6 +4,62 @@ <name>NetSharp</name> </assembly> <members> + <member name="M:NetSharp.DatagramNetworkReader.#ctor(System.Net.Sockets.Socket@,NetSharp.NetworkRequestHandler,System.Net.EndPoint,System.Int32,System.Int32)"> + <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.Int32,System.Int32)"> + <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.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@,NetSharp.NetworkRequestHandler,System.Net.EndPoint,System.Int32,System.Int32)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.NetworkReaderBase`1.Dispose(System.Boolean)"> + <inheritdoc /> + </member> + <member name="M:NetSharp.NetworkWriterBase`1.#ctor(System.Net.Sockets.Socket@,System.Int32,System.Int32)"> + <inheritdoc /> + </member> <member name="T:NetSharp.Packets.NetworkPacket"> <summary> Represents a raw packet sent across the network. @@ -192,19 +248,19 @@ <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)"> + <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,System.Threading.CancellationToken)"> + <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,System.Threading.CancellationToken)"> + <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"> @@ -240,7 +296,7 @@ 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.AddressFamily@,System.Net.Sockets.ProtocolType@,NetSharp.Sockets.SocketServerPacketHandler@,System.Nullable{NetSharp.Sockets.Datagram.DatagramSocketServerOptions}@)"> + <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> @@ -261,30 +317,24 @@ <member name="M:NetSharp.Sockets.Datagram.DatagramSocketServer.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Datagram.DatagramSocketServer.ResetTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs)"> + <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.SocketClient"> + <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.SocketClient.#ctor(System.Net.Sockets.AddressFamily@,System.Net.Sockets.SocketType@,System.Net.Sockets.ProtocolType@,System.Int32@,System.UInt16@)"> + <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.SocketClient" /> class. + Constructs a new instance of the <see cref="T:NetSharp.Sockets.RawSocketClient" /> class. </summary> - <param name="connectionAddressFamily"> - The address family that the underlying connection should use. - </param> - <param name="connectionSocketType"> - The socket type that the underlying connection should use. - </param> - <param name="connectionProtocolType"> - The protocol type that the underlying connection should use. + <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. @@ -293,7 +343,7 @@ The number of transmission args to preallocate. </param> </member> - <member name="M:NetSharp.Sockets.SocketClient.CancelAsyncOperationCallback(System.Object)"> + <member name="M:NetSharp.Sockets.RawSocketClient.CancelAsyncOperationCallback(System.Object)"> <summary> Callback for the cancellation of an asynchronous network operation. </summary> @@ -301,7 +351,7 @@ The <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> state object for the operation. </param> </member> - <member name="M:NetSharp.Sockets.SocketClient.CancelAsyncReceiveCallback(System.Object)"> + <member name="M:NetSharp.Sockets.RawSocketClient.CancelAsyncReceiveCallback(System.Object)"> <summary> Callback for the cancellation of an asynchronous network receive operation. </summary> @@ -309,7 +359,7 @@ The <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> state object for the operation. </param> </member> - <member name="M:NetSharp.Sockets.SocketClient.CancelAsyncSendCallback(System.Object)"> + <member name="M:NetSharp.Sockets.RawSocketClient.CancelAsyncSendCallback(System.Object)"> <summary> Callback for the cancellation of an asynchronous network send operation. </summary> @@ -317,7 +367,7 @@ The <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> state object for the operation. </param> </member> - <member name="M:NetSharp.Sockets.SocketClient.Connect(System.Net.EndPoint@)"> + <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" />). @@ -326,7 +376,7 @@ The remote end point which to which to connect the client. </param> </member> - <member name="M:NetSharp.Sockets.SocketClient.ConnectAsync(System.Net.EndPoint@,System.Threading.CancellationToken)"> + <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" />). @@ -334,17 +384,14 @@ <param name="remoteEndPoint"> The remote end point which to which to connect the client. </param> - <param name="cancellationToken"> - The cancellation token to observe during the asynchronous operation. - </param> <returns> A <see cref="T:System.Threading.Tasks.ValueTask" /> representing the connection attempt. </returns> </member> - <member name="M:NetSharp.Sockets.SocketClient.Receive(System.Net.EndPoint@,System.Byte[],System.Net.Sockets.SocketFlags)"> + <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.SocketClient.Connect(System.Net.EndPoint@)" /> or <see cref="M:NetSharp.Sockets.SocketClient.ConnectAsync(System.Net.EndPoint@,System.Threading.CancellationToken)" />. + 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. @@ -359,10 +406,10 @@ The result of the receive operation. </returns> </member> - <member name="M:NetSharp.Sockets.SocketClient.ReceiveAsync(System.Net.EndPoint@,System.Memory{System.Byte},System.Net.Sockets.SocketFlags,System.Threading.CancellationToken)"> + <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.SocketClient.Connect(System.Net.EndPoint@)" /> or <see cref="M:NetSharp.Sockets.SocketClient.ConnectAsync(System.Net.EndPoint@,System.Threading.CancellationToken)" />. + 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. @@ -373,17 +420,14 @@ <param name="flags"> The socket flags associated with the send operation. </param> - <param name="cancellationToken"> - The cancellation token to observe during the asynchronous operation. - </param> <returns> The result of the asynchronous receive operation. </returns> </member> - <member name="M:NetSharp.Sockets.SocketClient.Send(System.Net.EndPoint@,System.Byte[],System.Net.Sockets.SocketFlags)"> + <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.SocketClient.Connect(System.Net.EndPoint@)" /> or <see cref="M:NetSharp.Sockets.SocketClient.ConnectAsync(System.Net.EndPoint@,System.Threading.CancellationToken)" />. + 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. @@ -398,10 +442,10 @@ The result of the send operation. </returns> </member> - <member name="M:NetSharp.Sockets.SocketClient.SendAsync(System.Net.EndPoint@,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags,System.Threading.CancellationToken)"> + <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.SocketClient.Connect(System.Net.EndPoint@)" /> or <see cref="M:NetSharp.Sockets.SocketClient.ConnectAsync(System.Net.EndPoint@,System.Threading.CancellationToken)" />. + 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. @@ -413,31 +457,28 @@ <param name="flags"> The socket flags associated with the send operation. </param> - <param name="cancellationToken"> - The cancellation token to observe during the asynchronous operation. - </param> <returns> The result of the asynchronous send operation. </returns> </member> - <member name="T:NetSharp.Sockets.SocketClient.AsyncOperationToken"> + <member name="T:NetSharp.Sockets.RawSocketClient.AsyncOperationToken"> <summary> A state token for asynchronous socket operations. </summary> </member> - <member name="F:NetSharp.Sockets.SocketClient.AsyncOperationToken.CancellationToken"> + <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.SocketClient.AsyncOperationToken.CompletionSource"> + <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.SocketClient.AsyncOperationToken.#ctor(System.Threading.Tasks.TaskCompletionSource{System.Boolean}@,System.Threading.CancellationToken@)"> + <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.SocketClient.AsyncOperationToken" /> struct. + 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. @@ -446,24 +487,24 @@ The cancellation token to observe during the operation. </param> </member> - <member name="T:NetSharp.Sockets.SocketClient.AsyncReceiveToken"> + <member name="T:NetSharp.Sockets.RawSocketClient.AsyncReceiveToken"> <summary> A state token for asynchronous incoming network IO operations. </summary> </member> - <member name="F:NetSharp.Sockets.SocketClient.AsyncReceiveToken.CancellationToken"> + <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.SocketClient.AsyncReceiveToken.CompletionSource"> + <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.SocketClient.AsyncReceiveToken.#ctor(System.Threading.Tasks.TaskCompletionSource{NetSharp.Utils.TransmissionResult}@,System.Threading.CancellationToken@)"> + <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.SocketClient.AsyncReceiveToken" /> struct. + 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. @@ -472,73 +513,140 @@ The cancellation token to observe during the operation. </param> </member> - <member name="T:NetSharp.Sockets.SocketClient.AsyncSendToken"> + <member name="T:NetSharp.Sockets.RawSocketClient.AsyncSendToken"> <summary> A state token for asynchronous outgoing network IO operations. </summary> </member> - <member name="F:NetSharp.Sockets.SocketClient.AsyncSendToken.CancellationToken"> + <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.SocketClient.AsyncSendToken.CompletionSource"> + <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.SocketClient.AsyncSendToken.RentedBuffer"> + <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.SocketClient.AsyncSendToken.#ctor(System.Threading.Tasks.TaskCompletionSource{NetSharp.Utils.TransmissionResult}@,System.Byte[]@,System.Threading.CancellationToken@)"> + <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.SocketClient.AsyncSendToken" /> struct. + 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="rentedBuffer"> + <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.SocketConnection"> + <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 clients and servers. + 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.SocketConnection.BufferPool"> + <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.BufferPool"> <summary> Pools arrays to function as temporary buffers during network read/write operations. </summary> </member> - <member name="F:NetSharp.Sockets.SocketConnection.TransmissionArgsPool"> + <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.SocketConnection.Connection"> + <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.SocketConnection.#ctor(System.Net.Sockets.AddressFamily@,System.Net.Sockets.SocketType@,System.Net.Sockets.ProtocolType@,System.Int32@,System.UInt16@)"> + <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.SocketConnection" /> class. + Constructs a new instance of the <see cref="T:NetSharp.Sockets.SocketConnectionBase" /> class. </summary> - <param name="connectionAddressFamily"> - The address family for the underlying socket. - </param> - <param name="connectionSocketType"> - The socket type for the underlying socket. - </param> - <param name="connectionProtocolType"> - The protocol type for the underlying socket. + <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. @@ -547,16 +655,16 @@ The number of <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> objects to initially preallocate. </param> </member> - <member name="P:NetSharp.Sockets.SocketConnection.LocalEndPoint"> + <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.SocketConnection.CanTransmissionArgsBeReused(System.Net.Sockets.SocketAsyncEventArgs@)"> + <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.SocketConnection.TransmissionArgsPool" />. If this method returns <c>true</c>, <see cref="M:NetSharp.Sockets.SocketConnection.ResetTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs)" /> is called on the given - <paramref name="args" />. Otherwise, <see cref="M:NetSharp.Sockets.SocketConnection.DestroyTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs)" /> is called. + <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. @@ -565,35 +673,35 @@ Whether the given <paramref name="args" /> should be reset and reused, or should be destroyed. </returns> </member> - <member name="M:NetSharp.Sockets.SocketConnection.CreateTransmissionArgs"> + <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.SocketConnection.TransmissionArgsPool" />. - The resulting instance should register <see cref="M:NetSharp.Sockets.SocketConnection.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)" /> as an event handler for the + Delegate method used to construct fresh <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instances for use in the <see cref="F:NetSharp.Sockets.SocketConnectionBase.ArgsPool" />. + The resulting instance should register <see cref="M:NetSharp.Sockets.SocketConnectionBase.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)" /> as an event handler for the <see cref="E:System.Net.Sockets.SocketAsyncEventArgs.Completed" /> event. </summary> <returns> The configured <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instance. </returns> </member> - <member name="M:NetSharp.Sockets.SocketConnection.DestroyTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs)"> + <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.SocketConnection.TransmissionArgsPool" />. This method should deregister <see cref="M:NetSharp.Sockets.SocketConnection.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)" /> as an event handler for 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.SocketConnection.Dispose(System.Boolean)"> + <member name="M:NetSharp.Sockets.SocketConnectionBase.Dispose(System.Boolean)"> <summary> - Disposes of managed and unmanaged resources used by the <see cref="T:NetSharp.Sockets.SocketConnection" /> class. + 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.SocketConnection.Dispose" />. + Whether this call was made by a call to <see cref="M:NetSharp.Sockets.SocketConnectionBase.Dispose" />. </param> </member> - <member name="M:NetSharp.Sockets.SocketConnection.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)"> + <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> @@ -604,15 +712,15 @@ The <see cref="T:System.Net.Sockets.SocketAsyncEventArgs" /> instance associated with the asynchronous network IO. </param> </member> - <member name="M:NetSharp.Sockets.SocketConnection.ResetTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs)"> + <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.SocketConnection.TransmissionArgsPool" />. + 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.SocketConnection.Bind(System.Net.EndPoint@)"> + <member name="M:NetSharp.Sockets.SocketConnectionBase.Bind(System.Net.EndPoint@)"> <summary> Binds the underlying socket. </summary> @@ -620,10 +728,10 @@ The end point to which the socket should be bound. </param> </member> - <member name="M:NetSharp.Sockets.SocketConnection.Dispose"> + <member name="M:NetSharp.Sockets.SocketConnectionBase.Dispose"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.SocketConnection.Shutdown(System.Net.Sockets.SocketShutdown)"> + <member name="M:NetSharp.Sockets.SocketConnectionBase.Shutdown(System.Net.Sockets.SocketShutdown)"> <summary> Shuts down the underlying socket. </summary> @@ -631,78 +739,6 @@ Which socket transmission functions should be shut down on the socket. </param> </member> - <member name="T:NetSharp.Sockets.SocketServerPacketHandler"> - <summary> - Represents a method for serving request packets. This method should not throw any errors. - </summary> - <param name="requestPacket"> - The request packet received by the server. - </param> - <param name="clientEndPoint"> - The client from which the packet was received. - </param> - <returns> - The response packet which should be sent out to the client. If no packet should be sent out, this method must return <see cref="F:NetSharp.Packets.NetworkPacket.NullPacket" />. - </returns> - </member> - <member name="T:NetSharp.Sockets.SocketServer"> - <summary> - Abstract base class for servers. - </summary> - </member> - <member name="F:NetSharp.Sockets.SocketServer.PacketHandler"> - <summary> - The packet handler delegate to use to respond to incoming requests. - </summary> - </member> - <member name="M:NetSharp.Sockets.SocketServer.#ctor(System.Net.Sockets.AddressFamily@,System.Net.Sockets.SocketType@,System.Net.Sockets.ProtocolType@,NetSharp.Sockets.SocketServerPacketHandler@,System.Int32@,System.UInt16@)"> - <summary> - Constructs a new instance of the <see cref="T:NetSharp.Sockets.SocketServer" /> class. - </summary> - <param name="connectionAddressFamily"> - The address family that the underlying connection should use. - </param> - <param name="connectionSocketType"> - The socket type that the underlying connection should use. - </param> - <param name="connectionProtocolType"> - The protocol type that the underlying connection should use. - </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.SocketServer.DefaultPacketHandler(NetSharp.Packets.NetworkPacket@,System.Net.EndPoint@)"> - <summary> - The default request packet handler for servers. Simply echoes back any received packets. - </summary> - <param name="request"> - The request packet that was received. - </param> - <param name="remoteEndPoint"> - The client from which the packet was received. - </param> - <returns> - The received packet. - </returns> - </member> - <member name="M:NetSharp.Sockets.SocketServer.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.Stream.StreamSocketClientOptions"> <summary> Provides additional configuration options for a <see cref="T:NetSharp.Sockets.Stream.StreamSocketClient" /> instance. @@ -716,7 +752,7 @@ <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,System.Threading.CancellationToken)" /> and <see cref="M:NetSharp.Sockets.Stream.StreamSocketClient.ReceiveAsync(System.Net.EndPoint@,System.Memory{System.Byte},System.Net.Sockets.SocketFlags,System.Threading.CancellationToken)" /> methods. + <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)"> @@ -739,19 +775,19 @@ <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.ResetTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs)"> + <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.ResetTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs@)"> <inheritdoc /> </member> <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.Receive(System.Net.EndPoint@,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,System.Threading.CancellationToken)"> + <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.ReceiveAsync(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)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.SendAsync(System.Net.EndPoint@,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags,System.Threading.CancellationToken)"> + <member name="M:NetSharp.Sockets.Stream.StreamSocketClient.SendAsync(System.Net.EndPoint@,System.ReadOnlyMemory{System.Byte},System.Net.Sockets.SocketFlags)"> <inheritdoc /> </member> <member name="T:NetSharp.Sockets.Stream.StreamSocketServerOptions"> @@ -787,7 +823,7 @@ 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.AddressFamily@,System.Net.Sockets.ProtocolType@,NetSharp.Sockets.SocketServerPacketHandler@,System.Nullable{NetSharp.Sockets.Stream.StreamSocketServerOptions}@)"> + <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. </summary> @@ -808,7 +844,7 @@ <member name="M:NetSharp.Sockets.Stream.StreamSocketServer.HandleIoCompleted(System.Object,System.Net.Sockets.SocketAsyncEventArgs)"> <inheritdoc /> </member> - <member name="M:NetSharp.Sockets.Stream.StreamSocketServer.ResetTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs)"> + <member name="M:NetSharp.Sockets.Stream.StreamSocketServer.ResetTransmissionArgs(System.Net.Sockets.SocketAsyncEventArgs@)"> <inheritdoc /> </member> <member name="M:NetSharp.Sockets.Stream.StreamSocketServer.RunAsync(System.Threading.CancellationToken)"> diff --git a/NetSharp/NetSharp/NetworkConnectionBase.cs b/NetSharp/NetSharp/NetworkConnectionBase.cs @@ -0,0 +1,75 @@ +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 + { + private readonly ArrayPool<byte> bufferPool; + protected readonly SlimObjectPool<TState> StateObjectPool; + protected readonly int BufferSize; + protected readonly Socket Connection; + + protected NetworkConnectionBase(ref Socket rawConnection, int maxPooledBufferSize, int preallocatedStateObjects = 0) + { + Connection = rawConnection; + + BufferSize = maxPooledBufferSize; + bufferPool = ArrayPool<byte>.Create(maxPooledBufferSize, 1_000); + + StateObjectPool = + new SlimObjectPool<TState>(CreateStateObject, ResetStateObject, DestroyStateObject, CanReuseStateObject); + } + + protected abstract bool CanReuseStateObject(in 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 RentedBufferHandle RentBuffer(int desiredBufferSize) + { + byte[] rentedBuffer = bufferPool.Rent(desiredBufferSize); + + return new RentedBufferHandle(ref rentedBuffer); + } + + protected abstract void ResetStateObject(ref TState instance); + + protected void ReturnBuffer(RentedBufferHandle handle) + { + bufferPool.Return(handle.RentedBuffer, true); + } + + /// <inheritdoc /> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected readonly struct RentedBufferHandle + { + public readonly byte[] RentedBuffer; + + internal RentedBufferHandle(ref byte[] rentedBuffer) + { + RentedBuffer = rentedBuffer; + } + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharp/NetworkReaderBase.cs b/NetSharp/NetSharp/NetworkReaderBase.cs @@ -0,0 +1,58 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +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 CancellationToken ShutdownToken; + + protected readonly NetworkRequestHandler RequestHandler; + + protected readonly EndPoint DefaultEndPoint; + + /// <inheritdoc /> + protected NetworkReaderBase(ref Socket rawConnection, NetworkRequestHandler? requestHandler, EndPoint defaultEndPoint, int maxPooledBufferSize, int preallocatedStateObjects = 0) + : base(ref rawConnection, maxPooledBufferSize, preallocatedStateObjects) + { + shutdownTokenSource = new CancellationTokenSource(); + ShutdownToken = shutdownTokenSource.Token; + + DefaultEndPoint = defaultEndPoint; + + RequestHandler = requestHandler ?? DefaultRequestHandler; + } + + public static bool DefaultRequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, + Memory<byte> responseBuffer) + { + return requestBuffer.TryCopyTo(responseBuffer); + } + + /// <inheritdoc /> + protected override void Dispose(bool disposing) + { + if (!disposing) return; + + shutdownTokenSource.Cancel(); + shutdownTokenSource.Dispose(); + + base.Dispose(disposing); + } + + 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 @@ -0,0 +1,22 @@ +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, int maxPooledBufferSize, int preallocatedStateObjects = 0) + : base(ref rawConnection, maxPooledBufferSize, preallocatedStateObjects) + { + } + + 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/Sockets/Datagram/DatagramSocketClient.cs b/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketClient.cs @@ -40,14 +40,20 @@ namespace NetSharp.Sockets.Datagram //TODO address the need to handle series of network packets, not just single packets //TODO document class - public sealed class DatagramSocketClient : SocketClient + public sealed class DatagramSocketClient : RawSocketClient { private readonly DatagramSocketClientOptions clientOptions; - public DatagramSocketClient(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType, - in DatagramSocketClientOptions? clientOptions = null) : base(in connectionAddressFamily, SocketType.Dgram, in connectionProtocolType, - NetworkPacket.TotalSize, clientOptions?.PreallocatedTransmissionArgs ?? DatagramSocketClientOptions.Defaults.PreallocatedTransmissionArgs) + 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; } @@ -78,7 +84,7 @@ namespace NetSharp.Sockets.Datagram break; } - TransmissionArgsPool.Return(args); + ArgsPool.Return(args); } private void CompleteReceiveFrom(SocketAsyncEventArgs args) @@ -105,7 +111,7 @@ namespace NetSharp.Sockets.Datagram break; } - TransmissionArgsPool.Return(args); + ArgsPool.Return(args); } private void CompleteSendTo(SocketAsyncEventArgs args) @@ -133,7 +139,7 @@ namespace NetSharp.Sockets.Datagram } BufferPool.Return(sendToken.RentedBuffer, true); - TransmissionArgsPool.Return(args); + ArgsPool.Return(args); } /// <inheritdoc /> @@ -186,7 +192,7 @@ namespace NetSharp.Sockets.Datagram } /// <inheritdoc /> - protected override void ResetTransmissionArgs(SocketAsyncEventArgs args) + protected override void ResetTransmissionArgs(ref SocketAsyncEventArgs args) { } @@ -200,45 +206,23 @@ namespace NetSharp.Sockets.Datagram } /// <inheritdoc /> - public override ValueTask<TransmissionResult> ReceiveAsync(in EndPoint remoteEndPoint, Memory<byte> receiveBuffer, SocketFlags flags = SocketFlags.None, - CancellationToken cancellationToken = default) + public override ValueTask<TransmissionResult> ReceiveAsync(in EndPoint remoteEndPoint, Memory<byte> receiveBuffer, SocketFlags flags = SocketFlags.None) { TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); - SocketAsyncEventArgs args = TransmissionArgsPool.Rent(); + SocketAsyncEventArgs args = ArgsPool.Rent(); args.SetBuffer(receiveBuffer); args.RemoteEndPoint = remoteEndPoint; args.SocketFlags = flags; - args.UserToken = new AsyncReceiveToken(in tcs, in cancellationToken); + args.UserToken = new AsyncReceiveToken(in tcs, CancellationToken.None); - if (cancellationToken == default) - { - if (Connection.ReceiveFromAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); - } - else - { - // TODO find out why the fricc we leak memory - CancellationTokenRegistration cancellationRegistration = - cancellationToken.Register(CancelAsyncReceiveCallback, args); - - if (Connection.ReceiveFromAsync(args)) - return new ValueTask<TransmissionResult>( - tcs.Task.ContinueWith((task, state) => - { - ((CancellationTokenRegistration)state).Dispose(); - - return task.Result; - }, cancellationRegistration, CancellationToken.None) - ); - - cancellationRegistration.Dispose(); - } + if (Connection.ReceiveFromAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); TransmissionResult result = new TransmissionResult(in args); - TransmissionArgsPool.Return(args); + ArgsPool.Return(args); return new ValueTask<TransmissionResult>(result); } @@ -252,12 +236,11 @@ namespace NetSharp.Sockets.Datagram } /// <inheritdoc /> - public override ValueTask<TransmissionResult> SendAsync(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> sendBuffer, SocketFlags flags = SocketFlags.None, - CancellationToken cancellationToken = default) + public override ValueTask<TransmissionResult> SendAsync(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> sendBuffer, SocketFlags flags = SocketFlags.None) { TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); - SocketAsyncEventArgs args = TransmissionArgsPool.Rent(); + SocketAsyncEventArgs args = ArgsPool.Rent(); byte[] transmissionBuffer = BufferPool.Rent(sendBuffer.Length); sendBuffer.CopyTo(transmissionBuffer); @@ -266,35 +249,14 @@ namespace NetSharp.Sockets.Datagram args.RemoteEndPoint = remoteEndPoint; args.SocketFlags = flags; - args.UserToken = new AsyncSendToken(in tcs, in transmissionBuffer, in cancellationToken); - - if (cancellationToken == default) - { - if (Connection.SendToAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); - } - else - { - // TODO find out why the fricc we leak memory - CancellationTokenRegistration cancellationRegistration = - cancellationToken.Register(CancelAsyncSendCallback, args); - - if (Connection.SendToAsync(args)) - return new ValueTask<TransmissionResult>( - tcs.Task.ContinueWith((task, state) => - { - ((CancellationTokenRegistration)state).Dispose(); + args.UserToken = new AsyncSendToken(in tcs, ref transmissionBuffer, CancellationToken.None); - return task.Result; - }, cancellationRegistration, CancellationToken.None) - ); - - cancellationRegistration.Dispose(); - } + if (Connection.SendToAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); TransmissionResult result = new TransmissionResult(in args); BufferPool.Return(transmissionBuffer, true); - TransmissionArgsPool.Return(args); + ArgsPool.Return(args); return new ValueTask<TransmissionResult>(result); } diff --git a/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs b/NetSharp/NetSharp/Sockets/Datagram/DatagramSocketServer.cs @@ -50,7 +50,7 @@ namespace NetSharp.Sockets.Datagram //TODO address the need to handle series of network packets, not just single packets //TODO document class - public sealed class DatagramSocketServer : SocketServer + public sealed class DatagramSocketServer : RawSocketServer { private static readonly EndPoint AnyRemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); @@ -65,11 +65,17 @@ namespace NetSharp.Sockets.Datagram /// Additional options to configure the server. /// </param> /// <inheritdoc /> - public DatagramSocketServer(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType, - in SocketServerPacketHandler packetHandler, in DatagramSocketServerOptions? serverOptions = null) - : base(in connectionAddressFamily, SocketType.Dgram, in connectionProtocolType, in packetHandler, - NetworkPacket.TotalSize, serverOptions?.PreallocatedTransmissionArgs ?? DatagramSocketServerOptions.Defaults.PreallocatedTransmissionArgs) + 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; } @@ -84,30 +90,35 @@ namespace NetSharp.Sockets.Datagram if (receiveArgs.SocketError == SocketError.Success) { - NetworkPacket.Deserialise(receiveArgs.MemoryBuffer, out NetworkPacket request); + byte[] responseBuffer = BufferPool.Rent(MaxBufferSize); - NetworkPacket response = PacketHandler(in request, receiveArgs.RemoteEndPoint); + bool responseExists = PacketHandler(receiveArgs.RemoteEndPoint, receiveToken.RentedBuffer, responseBuffer); + BufferPool.Return(receiveToken.RentedBuffer, true); - if (!response.Equals(NetworkPacket.NullPacket)) - { - byte[] sendBuffer = BufferPool.Rent(NetworkPacket.TotalSize); - Memory<byte> sendBufferMemory = new Memory<byte>(sendBuffer); + //NetworkPacket.Deserialise(receiveArgs.MemoryBuffer, out NetworkPacket request); + //NetworkPacket response = PacketHandler(in request, receiveArgs.RemoteEndPoint); - NetworkPacket.Serialise(response, sendBufferMemory); + if (responseExists) + { + //byte[] sendBuffer = BufferPool.Rent(NetworkPacket.TotalSize); + //Memory<byte> sendBufferMemory = new Memory<byte>(sendBuffer); + //NetworkPacket.Serialise(response, sendBufferMemory); - receiveArgs.SetBuffer(sendBufferMemory); - receiveArgs.UserToken = new SocketOperationToken(in sendBuffer); + receiveArgs.SetBuffer(responseBuffer); + receiveArgs.UserToken = new SocketOperationToken(ref responseBuffer); SendTo(receiveArgs); } - - BufferPool.Return(receiveToken.RentedBuffer, true); + else + { + BufferPool.Return(responseBuffer, true); + } } else { BufferPool.Return(receiveToken.RentedBuffer, true); - TransmissionArgsPool.Return(receiveArgs); + ArgsPool.Return(receiveArgs); } } @@ -117,14 +128,14 @@ namespace NetSharp.Sockets.Datagram BufferPool.Return(sendToken.RentedBuffer, true); - TransmissionArgsPool.Return(sendArgs); + ArgsPool.Return(sendArgs); } private void ReceiveFrom(SocketAsyncEventArgs receiveArgs) { if (serverShutdownToken.IsCancellationRequested) { - TransmissionArgsPool.Return(receiveArgs); + ArgsPool.Return(receiveArgs); return; } @@ -133,13 +144,13 @@ namespace NetSharp.Sockets.Datagram Memory<byte> receiveBufferMemory = new Memory<byte>(receiveBuffer); receiveArgs.SetBuffer(receiveBufferMemory); - receiveArgs.UserToken = new SocketOperationToken(in receiveBuffer); + receiveArgs.UserToken = new SocketOperationToken(ref receiveBuffer); bool operationPending = Connection.ReceiveFromAsync(receiveArgs); if (operationPending) return; - SocketAsyncEventArgs newReceiveArgs = TransmissionArgsPool.Rent(); + SocketAsyncEventArgs newReceiveArgs = ArgsPool.Rent(); newReceiveArgs.RemoteEndPoint = AnyRemoteEndPoint; ReceiveFrom(newReceiveArgs); // start a new receive from operation immediately, to not drop any packets @@ -155,7 +166,7 @@ namespace NetSharp.Sockets.Datagram BufferPool.Return(sendToken.RentedBuffer, true); - TransmissionArgsPool.Return(sendArgs); + ArgsPool.Return(sendArgs); return; } @@ -198,7 +209,7 @@ namespace NetSharp.Sockets.Datagram switch (args.LastOperation) { case SocketAsyncOperation.ReceiveFrom: - SocketAsyncEventArgs newReceiveArgs = TransmissionArgsPool.Rent(); + SocketAsyncEventArgs newReceiveArgs = ArgsPool.Rent(); newReceiveArgs.RemoteEndPoint = AnyRemoteEndPoint; ReceiveFrom(newReceiveArgs); // start a new receive from operation immediately, to not drop any packets @@ -218,7 +229,7 @@ namespace NetSharp.Sockets.Datagram } /// <inheritdoc /> - protected override void ResetTransmissionArgs(SocketAsyncEventArgs args) + protected override void ResetTransmissionArgs(ref SocketAsyncEventArgs args) { } @@ -229,7 +240,7 @@ namespace NetSharp.Sockets.Datagram for (int i = 0; i < serverOptions.ConcurrentReceiveFromCalls; i++) { - SocketAsyncEventArgs newReceiveArgs = TransmissionArgsPool.Rent(); + SocketAsyncEventArgs newReceiveArgs = ArgsPool.Rent(); newReceiveArgs.RemoteEndPoint = AnyRemoteEndPoint; ReceiveFrom(newReceiveArgs); @@ -244,7 +255,7 @@ namespace NetSharp.Sockets.Datagram { public readonly byte[] RentedBuffer; - public SocketOperationToken(in byte[] rentedBuffer) + public SocketOperationToken(ref byte[] rentedBuffer) { RentedBuffer = rentedBuffer; } diff --git a/NetSharp/NetSharp/Sockets/RawSocketClient.cs b/NetSharp/NetSharp/Sockets/RawSocketClient.cs @@ -0,0 +1,307 @@ +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 @@ -0,0 +1,84 @@ +using System; +using NetSharp.Packets; + +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/SocketClient.cs b/NetSharp/NetSharp/Sockets/SocketClient.cs @@ -1,344 +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 SocketClient : SocketConnection - { - /// <summary> - /// Constructs a new instance of the <see cref="SocketClient" /> class. - /// </summary> - /// <param name="connectionAddressFamily"> - /// The address family that the underlying connection should use. - /// </param> - /// <param name="connectionSocketType"> - /// The socket type that the underlying connection should use. - /// </param> - /// <param name="connectionProtocolType"> - /// The protocol type that the underlying connection should use. - /// </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 SocketClient(in AddressFamily connectionAddressFamily, in SocketType connectionSocketType, in ProtocolType connectionProtocolType, - in int pooledBufferMaxSize, in ushort preallocatedTransmissionArgs) : base(in connectionAddressFamily, in connectionSocketType, - in connectionProtocolType, 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> - /// <param name="cancellationToken"> - /// The cancellation token to observe during the asynchronous operation. - /// </param> - /// <returns> - /// A <see cref="ValueTask" /> representing the connection attempt. - /// </returns> - public ValueTask ConnectAsync(in EndPoint remoteEndPoint, CancellationToken cancellationToken = default) - { - TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); - - SocketAsyncEventArgs args = TransmissionArgsPool.Rent(); - - args.RemoteEndPoint = remoteEndPoint; - args.UserToken = new AsyncOperationToken(in tcs, in cancellationToken); - - if (cancellationToken == default) - { - if (Connection.ConnectAsync(args)) return new ValueTask(tcs.Task); - } - else - { - // TODO find out why the fricc we leak memory - CancellationTokenRegistration cancellationRegistration = - cancellationToken.Register(CancelAsyncOperationCallback, args); - - if (Connection.ConnectAsync(args)) - return new ValueTask( - tcs.Task.ContinueWith((task, state) => - { - ((CancellationTokenRegistration)state).Dispose(); - - return task.Result; - }, cancellationRegistration, CancellationToken.None) - ); - - cancellationRegistration.Dispose(); - } - - TransmissionArgsPool.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> - /// <param name="cancellationToken"> - /// The cancellation token to observe during the asynchronous 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, CancellationToken cancellationToken = default); - - /// <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> - /// <param name="cancellationToken"> - /// The cancellation token to observe during the asynchronous 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, CancellationToken cancellationToken = default); - - /// <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="rentedBuffer"> - /// 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, in byte[] rentedBuffer, in CancellationToken cancellationToken) - { - CompletionSource = completionSource; - - RentedBuffer = rentedBuffer; - - CancellationToken = cancellationToken; - } - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/SocketConnection.cs b/NetSharp/NetSharp/Sockets/SocketConnection.cs @@ -1,177 +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 clients and servers. - /// </summary> - public abstract class SocketConnection : IDisposable - { - /// <summary> - /// Pools arrays to function as temporary buffers during network read/write operations. - /// </summary> - protected readonly ArrayPool<byte> BufferPool; - - /// <summary> - /// Pools <see cref="SocketAsyncEventArgs" /> objects for use during network read/write operations and calls to - /// <see cref="Socket" />.XXXAsync( <see cref="SocketAsyncEventArgs" />) methods. - /// </summary> - protected readonly SlimObjectPool<SocketAsyncEventArgs> TransmissionArgsPool; - - /// <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="SocketConnection" /> class. - /// </summary> - /// <param name="connectionAddressFamily"> - /// The address family for the underlying socket. - /// </param> - /// <param name="connectionSocketType"> - /// The socket type for the underlying socket. - /// </param> - /// <param name="connectionProtocolType"> - /// The protocol type for the underlying socket. - /// </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 SocketConnection(in AddressFamily connectionAddressFamily, in SocketType connectionSocketType, - in ProtocolType connectionProtocolType, in int pooledBufferMaxSize, in ushort preallocatedTransmissionArgs) - { - Connection = new Socket(connectionAddressFamily, connectionSocketType, connectionProtocolType); - - BufferPool = ArrayPool<byte>.Create(pooledBufferMaxSize, 1000); - - TransmissionArgsPool = 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(); - - TransmissionArgsPool.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="TransmissionArgsPool" />. 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(in SocketAsyncEventArgs args); - - /// <summary> - /// Delegate method used to construct fresh <see cref="SocketAsyncEventArgs" /> instances for use in the <see cref="TransmissionArgsPool" />. - /// 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="TransmissionArgsPool" />. 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="SocketConnection" /> 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; - - Connection.Close(); - Connection.Dispose(); - - TransmissionArgsPool.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="TransmissionArgsPool" />. - /// </summary> - /// <param name="args"> - /// The <see cref="SocketAsyncEventArgs" /> instance that should be reset. - /// </param> - protected abstract void ResetTransmissionArgs(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) { } - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharp/Sockets/SocketConnectionBase.cs b/NetSharp/NetSharp/Sockets/SocketConnectionBase.cs @@ -0,0 +1,175 @@ +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> + /// The maximum size of buffer that can be rented from the pool. + /// </summary> + protected readonly int MaxBufferSize; + + /// <summary> + /// Pools arrays to function as temporary buffers during network read/write operations. + /// </summary> + protected readonly ArrayPool<byte> BufferPool; + + /// <summary> + /// Pools <see cref="SocketAsyncEventArgs" /> objects for use during network read/write operations and calls to + /// <see cref="Socket" />.XXXAsync( <see cref="SocketAsyncEventArgs" />) methods. + /// </summary> + protected readonly SlimObjectPool<SocketAsyncEventArgs> ArgsPool; + + /// <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(in 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/SocketServer.cs b/NetSharp/NetSharp/Sockets/SocketServer.cs @@ -1,90 +0,0 @@ -using NetSharp.Packets; - -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="requestPacket"> - /// The request packet received by the server. - /// </param> - /// <param name="clientEndPoint"> - /// The client from which the packet was received. - /// </param> - /// <returns> - /// The response packet which should be sent out to the client. If no packet should be sent out, this method must return <see cref="NetworkPacket.NullPacket" />. - /// </returns> - public delegate NetworkPacket SocketServerPacketHandler(in NetworkPacket requestPacket, in EndPoint clientEndPoint); - - /// <summary> - /// Abstract base class for servers. - /// </summary> - public abstract class SocketServer : SocketConnection - { - /// <summary> - /// The packet handler delegate to use to respond to incoming requests. - /// </summary> - protected readonly SocketServerPacketHandler PacketHandler; - - /// <summary> - /// Constructs a new instance of the <see cref="SocketServer" /> class. - /// </summary> - /// <param name="connectionAddressFamily"> - /// The address family that the underlying connection should use. - /// </param> - /// <param name="connectionSocketType"> - /// The socket type that the underlying connection should use. - /// </param> - /// <param name="connectionProtocolType"> - /// The protocol type that the underlying connection should use. - /// </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 SocketServer(in AddressFamily connectionAddressFamily, in SocketType connectionSocketType, in ProtocolType connectionProtocolType, - in SocketServerPacketHandler packetHandler, in int pooledBufferMaxSize, in ushort preallocatedTransmissionArgs) - : base(in connectionAddressFamily, in connectionSocketType, in connectionProtocolType, pooledBufferMaxSize, preallocatedTransmissionArgs) - { - PacketHandler = packetHandler; - } - - /// <summary> - /// The default request packet handler for servers. Simply echoes back any received packets. - /// </summary> - /// <param name="request"> - /// The request packet that was received. - /// </param> - /// <param name="remoteEndPoint"> - /// The client from which the packet was received. - /// </param> - /// <returns> - /// The received packet. - /// </returns> - public static NetworkPacket DefaultPacketHandler(in NetworkPacket request, in EndPoint remoteEndPoint) - { - return request; - } - - /// <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/Stream/StreamSocketClient.cs b/NetSharp/NetSharp/Sockets/Stream/StreamSocketClient.cs @@ -40,14 +40,20 @@ namespace NetSharp.Sockets.Stream //TODO address the need to handle series of network packets, not just single packets //TODO document class - public sealed class StreamSocketClient : SocketClient + public sealed class StreamSocketClient : RawSocketClient { private readonly StreamSocketClientOptions clientOptions; - public StreamSocketClient(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType, - in StreamSocketClientOptions? clientOptions = null) : base(in connectionAddressFamily, SocketType.Stream, in connectionProtocolType, - NetworkPacket.TotalSize, clientOptions?.PreallocatedTransmissionArgs ?? StreamSocketClientOptions.Defaults.PreallocatedTransmissionArgs) + 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; } @@ -78,7 +84,7 @@ namespace NetSharp.Sockets.Stream break; } - TransmissionArgsPool.Return(args); + ArgsPool.Return(args); } private void CompleteDisconnect(SocketAsyncEventArgs args) @@ -103,7 +109,7 @@ namespace NetSharp.Sockets.Stream break; } - TransmissionArgsPool.Return(args); + ArgsPool.Return(args); } private void CompleteReceive(SocketAsyncEventArgs args) @@ -154,7 +160,7 @@ namespace NetSharp.Sockets.Stream break; } - TransmissionArgsPool.Return(args); + ArgsPool.Return(args); } private void CompleteSend(SocketAsyncEventArgs args) @@ -206,7 +212,7 @@ namespace NetSharp.Sockets.Stream } BufferPool.Return(sendToken.RentedBuffer, true); - TransmissionArgsPool.Return(args); + ArgsPool.Return(args); } /// <inheritdoc /> @@ -264,7 +270,7 @@ namespace NetSharp.Sockets.Stream } /// <inheritdoc /> - protected override void ResetTransmissionArgs(SocketAsyncEventArgs args) + protected override void ResetTransmissionArgs(ref SocketAsyncEventArgs args) { } @@ -277,7 +283,7 @@ namespace NetSharp.Sockets.Stream { TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); - SocketAsyncEventArgs args = TransmissionArgsPool.Rent(); + SocketAsyncEventArgs args = ArgsPool.Rent(); if (cancellationToken == default) { @@ -305,7 +311,7 @@ namespace NetSharp.Sockets.Stream cancellationRegistration.Dispose(); } - TransmissionArgsPool.Return(args); + ArgsPool.Return(args); return new ValueTask(); } @@ -325,44 +331,22 @@ namespace NetSharp.Sockets.Stream } /// <inheritdoc /> - public override ValueTask<TransmissionResult> ReceiveAsync(in EndPoint remoteEndPoint, Memory<byte> receiveBuffer, SocketFlags flags = SocketFlags.None, - CancellationToken cancellationToken = default) + public override ValueTask<TransmissionResult> ReceiveAsync(in EndPoint remoteEndPoint, Memory<byte> receiveBuffer, SocketFlags flags = SocketFlags.None) { TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); - SocketAsyncEventArgs args = TransmissionArgsPool.Rent(); + SocketAsyncEventArgs args = ArgsPool.Rent(); args.SetBuffer(receiveBuffer); args.SocketFlags = flags; - args.UserToken = new AsyncReceiveToken(in tcs, in cancellationToken); - - if (cancellationToken == default) - { - if (Connection.ReceiveAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); - } - else - { - // TODO find out why the fricc we leak memory - CancellationTokenRegistration cancellationRegistration = - cancellationToken.Register(CancelAsyncReceiveCallback, args); - - if (Connection.ReceiveAsync(args)) - return new ValueTask<TransmissionResult>( - tcs.Task.ContinueWith((task, state) => - { - ((CancellationTokenRegistration)state).Dispose(); + args.UserToken = new AsyncReceiveToken(in tcs, CancellationToken.None); - return task.Result; - }, cancellationRegistration, CancellationToken.None) - ); - - cancellationRegistration.Dispose(); - } + if (Connection.ReceiveAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); TransmissionResult result = new TransmissionResult(in args); - TransmissionArgsPool.Return(args); + ArgsPool.Return(args); return new ValueTask<TransmissionResult>(result); } @@ -382,12 +366,11 @@ namespace NetSharp.Sockets.Stream } /// <inheritdoc /> - public override ValueTask<TransmissionResult> SendAsync(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> sendBuffer, SocketFlags flags = SocketFlags.None, - CancellationToken cancellationToken = default) + public override ValueTask<TransmissionResult> SendAsync(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> sendBuffer, SocketFlags flags = SocketFlags.None) { TaskCompletionSource<TransmissionResult> tcs = new TaskCompletionSource<TransmissionResult>(); - SocketAsyncEventArgs args = TransmissionArgsPool.Rent(); + SocketAsyncEventArgs args = ArgsPool.Rent(); byte[] transmissionBuffer = BufferPool.Rent(sendBuffer.Length); sendBuffer.CopyTo(transmissionBuffer); @@ -395,34 +378,13 @@ namespace NetSharp.Sockets.Stream args.SetBuffer(transmissionBuffer); args.SocketFlags = flags; - args.UserToken = new AsyncSendToken(in tcs, in transmissionBuffer, in cancellationToken); - - if (cancellationToken == default) - { - if (Connection.SendAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); - } - else - { - // TODO find out why the fricc we leak memory - CancellationTokenRegistration cancellationRegistration = - cancellationToken.Register(CancelAsyncSendCallback, args); + args.UserToken = new AsyncSendToken(in tcs, ref transmissionBuffer, CancellationToken.None); - if (Connection.SendAsync(args)) - return new ValueTask<TransmissionResult>( - tcs.Task.ContinueWith((task, state) => - { - ((CancellationTokenRegistration)state).Dispose(); - - return task.Result; - }, cancellationRegistration, CancellationToken.None) - ); - - cancellationRegistration.Dispose(); - } + if (Connection.SendAsync(args)) return new ValueTask<TransmissionResult>(tcs.Task); TransmissionResult result = new TransmissionResult(in args); - TransmissionArgsPool.Return(args); + ArgsPool.Return(args); return new ValueTask<TransmissionResult>(result); } diff --git a/NetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs b/NetSharp/NetSharp/Sockets/Stream/StreamSocketServer.cs @@ -49,7 +49,7 @@ namespace NetSharp.Sockets.Stream //TODO address the need to handle series of network packets, not just single packets //TODO document class - public sealed class StreamSocketServer : SocketServer + public sealed class StreamSocketServer : RawSocketServer { private readonly StreamSocketServerOptions serverOptions; @@ -60,11 +60,17 @@ namespace NetSharp.Sockets.Stream /// Additional options to configure the server. /// </param> /// <inheritdoc /> - public StreamSocketServer(in AddressFamily connectionAddressFamily, in ProtocolType connectionProtocolType, - in SocketServerPacketHandler packetHandler, in StreamSocketServerOptions? serverOptions = null) - : base(in connectionAddressFamily, SocketType.Stream, in connectionProtocolType, in packetHandler, - NetworkPacket.TotalSize, serverOptions?.PreallocatedTransmissionArgs ?? StreamSocketServerOptions.Defaults.PreallocatedTransmissionArgs) + 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; } @@ -79,7 +85,7 @@ namespace NetSharp.Sockets.Stream if (operationPending) return; - SocketAsyncEventArgs newAcceptArgs = TransmissionArgsPool.Rent(); + SocketAsyncEventArgs newAcceptArgs = ArgsPool.Rent(); Accept(newAcceptArgs); // start a new accept operation to not miss any clients @@ -91,7 +97,7 @@ namespace NetSharp.Sockets.Stream RemoteStreamClientToken clientToken = (RemoteStreamClientToken)clientArgs.UserToken; clientToken.Dispose(); - TransmissionArgsPool.Return(clientArgs); + ArgsPool.Return(clientArgs); } private void CompleteAccept(SocketAsyncEventArgs connectedClientArgs) @@ -114,19 +120,17 @@ namespace NetSharp.Sockets.Stream if (clientArgs.BytesTransferred == NetworkPacket.TotalSize) { // buffer was fully received + byte[] responseBuffer = BufferPool.Rent(MaxBufferSize); - NetworkPacket.Deserialise(receiveToken.RentedBuffer, out NetworkPacket request); + bool responseExists = PacketHandler(clientArgs.RemoteEndPoint, receiveToken.RentedBuffer, responseBuffer); + BufferPool.Return(receiveToken.RentedBuffer, true); // at this point the request buffer can be returned - NetworkPacket response = PacketHandler(in request, clientArgs.RemoteEndPoint); + //NetworkPacket.Deserialise(receiveToken.RentedBuffer, out NetworkPacket request); + //NetworkPacket response = PacketHandler(in request, clientArgs.RemoteEndPoint); - if (!response.Equals(NetworkPacket.NullPacket)) + if (responseExists) { - byte[] responseBuffer = BufferPool.Rent(NetworkPacket.TotalSize); - Memory<byte> responseBufferMemory = new Memory<byte>(responseBuffer); - - NetworkPacket.Serialise(response, responseBufferMemory); - - BufferPool.Return(receiveToken.RentedBuffer, true); // at this point the request buffer can be returned + //NetworkPacket.Serialise(response, responseBufferMemory); receiveToken.RentedBuffer = responseBuffer; clientArgs.SetBuffer(responseBuffer, 0, NetworkPacket.TotalSize); @@ -135,10 +139,10 @@ namespace NetSharp.Sockets.Stream } else { - BufferPool.Return(receiveToken.RentedBuffer, true); // at this point the request buffer can be returned - - Receive(clientArgs); + BufferPool.Return(responseBuffer, true); } + + Receive(clientArgs); } else if (NetworkPacket.TotalSize > clientArgs.BytesTransferred && clientArgs.BytesTransferred > 0) { @@ -262,7 +266,7 @@ namespace NetSharp.Sockets.Stream switch (args.LastOperation) { case SocketAsyncOperation.Accept: - SocketAsyncEventArgs newAcceptArgs = TransmissionArgsPool.Rent(); + SocketAsyncEventArgs newAcceptArgs = ArgsPool.Rent(); Accept(newAcceptArgs); // start a new accept operation to not miss any clients @@ -286,7 +290,7 @@ namespace NetSharp.Sockets.Stream } /// <inheritdoc /> - protected override void ResetTransmissionArgs(SocketAsyncEventArgs args) + protected override void ResetTransmissionArgs(ref SocketAsyncEventArgs args) { } @@ -297,7 +301,7 @@ namespace NetSharp.Sockets.Stream for (int i = 0; i < serverOptions.ConcurrentAcceptCalls; i++) { - SocketAsyncEventArgs acceptArgs = TransmissionArgsPool.Rent(); + SocketAsyncEventArgs acceptArgs = ArgsPool.Rent(); Accept(acceptArgs); } diff --git a/NetSharp/NetSharp/Utils/SlimObjectPool.cs b/NetSharp/NetSharp/Utils/SlimObjectPool.cs @@ -109,7 +109,7 @@ namespace NetSharp.Utils /// <param name="instance"> /// The instance which should be reset. /// </param> - public delegate void ResetObjectDelegate(T instance); + public delegate void ResetObjectDelegate(ref T instance); /// <inheritdoc /> public void Dispose() @@ -141,7 +141,7 @@ namespace NetSharp.Utils { if (canObjectBeRebufferedPredicate(instance)) { - resetObjectDelegate(instance); + resetObjectDelegate(ref instance); objectBuffer.TryAdd(instance); } diff --git a/NetSharp/NetSharpExamples/BenchmarkHelper.cs b/NetSharp/NetSharpExamples/BenchmarkHelper.cs @@ -5,24 +5,24 @@ namespace NetSharpExamples { public class BenchmarkHelper { - private readonly Stopwatch bandwidthStopwatch = new Stopwatch(); - private readonly Stopwatch rttStopwatch = new Stopwatch(); + private readonly Stopwatch stopwatch = new Stopwatch(); + private long lastTicksSnapshot = 0, lastMsSnapshot = 0; private long minRttMs = int.MaxValue, maxRttMs = int.MinValue; private long minRttTicks = int.MaxValue, maxRttTicks = int.MinValue; public long RttMs { - get { return rttStopwatch.ElapsedMilliseconds; } + get { return stopwatch.ElapsedMilliseconds; } } public long RttTicks { - get { return rttStopwatch.ElapsedTicks; } + get { return stopwatch.ElapsedTicks; } } public double CalcBandwidth(long sentPacketCount, long packetSize) { - long millis = bandwidthStopwatch.ElapsedMilliseconds; + long millis = stopwatch.ElapsedMilliseconds; double megabytes = sentPacketCount * packetSize / 1_000_000.0; double bandwidth = megabytes / (millis / 1000.0); @@ -31,7 +31,7 @@ namespace NetSharpExamples public void PrintBandwidthStats(int clientId, long sentPacketCount, long packetSize) { - long millis = bandwidthStopwatch.ElapsedMilliseconds; + long millis = stopwatch.ElapsedMilliseconds; double megabytes = sentPacketCount * packetSize / 1_000_000.0; double bandwidth = megabytes / (millis / 1000.0); @@ -51,53 +51,46 @@ namespace NetSharpExamples } } - public void ResetBandwidthStopwatch() + public void ResetStopwatch() { - bandwidthStopwatch.Reset(); - } - - public void ResetRttStopwatch() - { - rttStopwatch.Reset(); - } - - public void StartBandwidthStopwatch() - { - bandwidthStopwatch.Start(); - } - - public void StartRttStopwatch() - { - rttStopwatch.Start(); - } + lastTicksSnapshot = 0; + lastMsSnapshot = 0; - public void StopBandwidthStopwatch() - { - bandwidthStopwatch.Stop(); + stopwatch.Reset(); } - public void StopRttStopwatch() + public void SnapshotRttStats() { - rttStopwatch.Stop(); - } + long elapsedTicksSnapshot = stopwatch.ElapsedTicks, elapsedMsSnapshot = stopwatch.ElapsedMilliseconds; - public void UpdateRttStats(int clientId) - { - minRttTicks = rttStopwatch.ElapsedTicks < minRttTicks - ? rttStopwatch.ElapsedTicks + minRttTicks = elapsedTicksSnapshot - lastTicksSnapshot < minRttTicks + ? elapsedTicksSnapshot - lastTicksSnapshot : minRttTicks; - minRttMs = rttStopwatch.ElapsedMilliseconds < minRttMs - ? rttStopwatch.ElapsedMilliseconds + minRttMs = elapsedMsSnapshot - lastMsSnapshot < minRttMs + ? elapsedMsSnapshot - lastMsSnapshot : minRttMs; - maxRttTicks = rttStopwatch.ElapsedTicks > maxRttTicks - ? rttStopwatch.ElapsedTicks + maxRttTicks = elapsedTicksSnapshot - lastTicksSnapshot > maxRttTicks + ? elapsedTicksSnapshot - lastTicksSnapshot : maxRttTicks; - maxRttMs = rttStopwatch.ElapsedMilliseconds > maxRttMs - ? rttStopwatch.ElapsedMilliseconds + maxRttMs = elapsedMsSnapshot - lastMsSnapshot > maxRttMs + ? elapsedMsSnapshot - lastMsSnapshot : maxRttMs; + + lastTicksSnapshot = elapsedTicksSnapshot; + lastMsSnapshot = elapsedMsSnapshot; + } + + public void StartStopwatch() + { + stopwatch.Start(); + } + + public void StopStopwatch() + { + stopwatch.Stop(); } } } \ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/DatagramNetworkReaderBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/DatagramNetworkReaderBenchmark.cs @@ -0,0 +1,109 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using NetSharp; + +namespace NetSharpExamples.Benchmarks +{ + public class DatagramNetworkReaderBenchmark : INetSharpExample + { + private const int PacketSize = 8192, PacketCount = 1_000_000, ClientCount = 12; + + public static readonly EndPoint ClientEndPoint = new IPEndPoint(IPAddress.Loopback, 0); + + public static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12349); + + public static readonly Encoding ServerEncoding = Encoding.UTF8; + + private double[] ClientBandwidths; + + /// <inheritdoc /> + public string Name { get; } = "Datagram Network Reader Benchmark"; + + private Task BenchmarkClientTask(object idObj) + { + try + { + int id = (int)idObj; + + BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); + + Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + clientSocket.Bind(ClientEndPoint); + + byte[] sendBuffer = new byte[PacketSize]; + byte[] receiveBuffer = new byte[PacketSize]; + + EndPoint remoteEndPoint = ServerEndPoint; + + lock (typeof(Console)) + { + Console.WriteLine($"[Client {id}] Starting client; sending messages to {remoteEndPoint}"); + } + + for (int i = 0; i < PacketCount; i++) + { + byte[] packetBuffer = ServerEncoding.GetBytes($"[Client {id}] Hello World! (Packet {i})"); + packetBuffer.CopyTo(sendBuffer, 0); + + benchmarkHelper.StartStopwatch(); + int sentBytes = clientSocket.SendTo(sendBuffer, remoteEndPoint); + + int receivedBytes = clientSocket.ReceiveFrom(receiveBuffer, ref remoteEndPoint); + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); + } + + benchmarkHelper.PrintBandwidthStats(id, PacketCount, PacketSize); + benchmarkHelper.PrintRttStats(id); + + ClientBandwidths[id] = benchmarkHelper.CalcBandwidth(PacketCount, PacketSize); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public async Task RunAsync() + { + EndPoint defaultRemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + rawSocket.Bind(ServerEndPoint); + using DatagramNetworkReader reader = new DatagramNetworkReader(ref rawSocket, RequestHandler, defaultRemoteEndPoint, PacketSize); + reader.Start(ClientCount); + + ClientBandwidths = new double[ClientCount]; + Task[] clientTasks = new Task[ClientCount]; + for (int i = 0; i < clientTasks.Length; i++) + { + clientTasks[i] = Task.Factory.StartNew(BenchmarkClientTask, i, TaskCreationOptions.LongRunning); + } + + await Task.WhenAll(clientTasks); + + Console.WriteLine($"Total estimated bandwidth: {ClientBandwidths.Sum():F5}"); + + reader.Stop(); + + rawSocket.Close(); + rawSocket.Dispose(); + + Console.WriteLine($"UDP Server Benchmark finished!"); + } + + private static bool RequestHandler(in EndPoint remoteEndPoint, ReadOnlyMemory<byte> requestBuffer, Memory<byte> responseBuffer) + { + return requestBuffer.TryCopyTo(responseBuffer); + } + } +} +\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Benchmarks/TcpSocketClientAsyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/TcpSocketClientAsyncBenchmark.cs @@ -83,7 +83,8 @@ namespace NetSharpExamples.Benchmarks BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); StreamSocketClientOptions clientOptions = new StreamSocketClientOptions((ushort)2); - using StreamSocketClient client = new StreamSocketClient(AddressFamily.InterNetwork, ProtocolType.Tcp, clientOptions); + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + using StreamSocketClient client = new StreamSocketClient(ref rawSocket, clientOptions); await client.ConnectAsync(in ServerEndPoint); @@ -95,16 +96,13 @@ namespace NetSharpExamples.Benchmarks byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client 0] Hello World! (Packet {i})"); packetBuffer.CopyTo(sendBuffer, 0); - benchmarkHelper.StartBandwidthStopwatch(); - benchmarkHelper.StartRttStopwatch(); + benchmarkHelper.StartStopwatch(); TransmissionResult sendResult = await client.SendAsync(in ServerEndPoint, sendBuffer); TransmissionResult receiveResult = await client.ReceiveAsync(in ServerEndPoint, receiveBuffer); - benchmarkHelper.StopRttStopwatch(); - benchmarkHelper.StopBandwidthStopwatch(); + benchmarkHelper.StopStopwatch(); - benchmarkHelper.UpdateRttStats(0); - benchmarkHelper.ResetRttStopwatch(); + benchmarkHelper.SnapshotRttStats(); } benchmarkHelper.PrintBandwidthStats(0, PacketCount, NetworkPacket.TotalSize); diff --git a/NetSharp/NetSharpExamples/Benchmarks/TcpSocketClientSyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/TcpSocketClientSyncBenchmark.cs @@ -83,7 +83,8 @@ namespace NetSharpExamples.Benchmarks BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); StreamSocketClientOptions clientOptions = new StreamSocketClientOptions((ushort)2); - using StreamSocketClient client = new StreamSocketClient(AddressFamily.InterNetwork, ProtocolType.Tcp, clientOptions); + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + using StreamSocketClient client = new StreamSocketClient(ref rawSocket, clientOptions); client.Connect(in ServerEndPoint); @@ -95,16 +96,13 @@ namespace NetSharpExamples.Benchmarks byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client 0] Hello World! (Packet {i})"); packetBuffer.CopyTo(sendBuffer, 0); - benchmarkHelper.StartBandwidthStopwatch(); - benchmarkHelper.StartRttStopwatch(); + benchmarkHelper.StartStopwatch(); TransmissionResult sendResult = client.Send(in ServerEndPoint, sendBuffer); TransmissionResult receiveResult = client.Receive(in ServerEndPoint, receiveBuffer); - benchmarkHelper.StopRttStopwatch(); - benchmarkHelper.StopBandwidthStopwatch(); - - benchmarkHelper.UpdateRttStats(0); - benchmarkHelper.ResetRttStopwatch(); + benchmarkHelper.StopStopwatch(); + + benchmarkHelper.SnapshotRttStats(); } benchmarkHelper.PrintBandwidthStats(0, PacketCount, NetworkPacket.TotalSize); diff --git a/NetSharp/NetSharpExamples/Benchmarks/TcpSocketServerBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/TcpSocketServerBenchmark.cs @@ -52,8 +52,7 @@ namespace NetSharpExamples.Benchmarks byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client {id}] Hello World! (Packet {i})"); packetBuffer.CopyTo(sendBuffer, 0); - benchmarkHelper.StartBandwidthStopwatch(); - benchmarkHelper.StartRttStopwatch(); + benchmarkHelper.StartStopwatch(); int totalSent = 0; do @@ -79,11 +78,9 @@ namespace NetSharpExamples.Benchmarks break; } - benchmarkHelper.StopRttStopwatch(); - benchmarkHelper.StopBandwidthStopwatch(); + benchmarkHelper.StopStopwatch(); - benchmarkHelper.UpdateRttStats(id); - benchmarkHelper.ResetRttStopwatch(); + benchmarkHelper.SnapshotRttStats(); } clientSocket.Disconnect(true); @@ -112,9 +109,8 @@ namespace NetSharpExamples.Benchmarks } StreamSocketServerOptions serverOptions = new StreamSocketServerOptions(clientCount, (ushort)clientCount); - - StreamSocketServer server = new StreamSocketServer(AddressFamily.InterNetwork, ProtocolType.Tcp, - SocketServer.DefaultPacketHandler, serverOptions); + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + StreamSocketServer server = new StreamSocketServer(ref rawSocket, RawSocketServer.DefaultRawPacketHandler, serverOptions); server.Bind(ServerEndPoint); diff --git a/NetSharp/NetSharpExamples/Benchmarks/UdpSocketClientAsyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/UdpSocketClientAsyncBenchmark.cs @@ -61,7 +61,8 @@ namespace NetSharpExamples.Benchmarks BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); DatagramSocketClientOptions clientOptions = new DatagramSocketClientOptions((ushort)2); - using DatagramSocketClient client = new DatagramSocketClient(AddressFamily.InterNetwork, ProtocolType.Udp, clientOptions); + 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]; @@ -71,16 +72,13 @@ namespace NetSharpExamples.Benchmarks byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client 0] Hello World! (Packet {i})"); packetBuffer.CopyTo(sendBuffer, 0); - benchmarkHelper.StartBandwidthStopwatch(); - benchmarkHelper.StartRttStopwatch(); + benchmarkHelper.StartStopwatch(); TransmissionResult sendResult = await client.SendAsync(in ServerEndPoint, sendBuffer); TransmissionResult receiveResult = await client.ReceiveAsync(in ServerEndPoint, receiveBuffer); - benchmarkHelper.StopRttStopwatch(); - benchmarkHelper.StopBandwidthStopwatch(); + benchmarkHelper.StopStopwatch(); - benchmarkHelper.UpdateRttStats(0); - benchmarkHelper.ResetRttStopwatch(); + benchmarkHelper.SnapshotRttStats(); } benchmarkHelper.PrintBandwidthStats(0, PacketCount, NetworkPacket.TotalSize); diff --git a/NetSharp/NetSharpExamples/Benchmarks/UdpSocketClientSyncBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/UdpSocketClientSyncBenchmark.cs @@ -61,7 +61,8 @@ namespace NetSharpExamples.Benchmarks BenchmarkHelper benchmarkHelper = new BenchmarkHelper(); DatagramSocketClientOptions clientOptions = new DatagramSocketClientOptions((ushort)2); - using DatagramSocketClient client = new DatagramSocketClient(AddressFamily.InterNetwork, ProtocolType.Udp, clientOptions); + 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]; @@ -73,16 +74,13 @@ namespace NetSharpExamples.Benchmarks byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client 0] Hello World! (Packet {i})"); packetBuffer.CopyTo(sendBuffer, 0); - benchmarkHelper.StartBandwidthStopwatch(); - benchmarkHelper.StartRttStopwatch(); + benchmarkHelper.StartStopwatch(); TransmissionResult sendResult = client.Send(in remoteEndPoint, sendBuffer); TransmissionResult receiveResult = client.Receive(in remoteEndPoint, receiveBuffer); - benchmarkHelper.StopRttStopwatch(); - benchmarkHelper.StopBandwidthStopwatch(); + benchmarkHelper.StopStopwatch(); - benchmarkHelper.UpdateRttStats(0); - benchmarkHelper.ResetRttStopwatch(); + benchmarkHelper.SnapshotRttStats(); } benchmarkHelper.PrintBandwidthStats(0, PacketCount, NetworkPacket.TotalSize); diff --git a/NetSharp/NetSharpExamples/Benchmarks/UdpSocketServerBenchmark.cs b/NetSharp/NetSharpExamples/Benchmarks/UdpSocketServerBenchmark.cs @@ -51,16 +51,13 @@ namespace NetSharpExamples.Benchmarks byte[] packetBuffer = Encoding.UTF8.GetBytes($"[Client {id}] Hello World! (Packet {i})"); packetBuffer.CopyTo(sendBuffer, 0); - benchmarkHelper.StartBandwidthStopwatch(); - benchmarkHelper.StartRttStopwatch(); + benchmarkHelper.StartStopwatch(); int sentBytes = clientSocket.SendTo(sendBuffer, remoteEndPoint); int receivedBytes = clientSocket.ReceiveFrom(receiveBuffer, ref remoteEndPoint); - benchmarkHelper.StopRttStopwatch(); - benchmarkHelper.StopBandwidthStopwatch(); + benchmarkHelper.StopStopwatch(); - benchmarkHelper.UpdateRttStats(id); - benchmarkHelper.ResetRttStopwatch(); + benchmarkHelper.SnapshotRttStats(); } benchmarkHelper.PrintBandwidthStats(id, PacketCount, NetworkPacket.TotalSize); @@ -87,8 +84,8 @@ namespace NetSharpExamples.Benchmarks DatagramSocketServerOptions serverOptions = new DatagramSocketServerOptions(clientCount, (ushort)clientCount); - DatagramSocketServer server = new DatagramSocketServer(AddressFamily.InterNetwork, ProtocolType.Udp, - SocketServer.DefaultPacketHandler, serverOptions); + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + DatagramSocketServer server = new DatagramSocketServer(ref rawSocket, RawSocketServer.DefaultRawPacketHandler, serverOptions); server.Bind(ServerEndPoint); @@ -112,6 +109,9 @@ namespace NetSharpExamples.Benchmarks await serverTask; + rawSocket.Close(); + rawSocket.Dispose(); + Console.WriteLine($"UDP Server Benchmark finished!"); } } diff --git a/NetSharp/NetSharpExamples/Examples/TcpSocketClientExample.cs b/NetSharp/NetSharpExamples/Examples/TcpSocketClientExample.cs @@ -20,7 +20,8 @@ namespace NetSharpExamples.Examples { StreamSocketClientOptions clientOptions = new StreamSocketClientOptions(2); - using StreamSocketClient client = new StreamSocketClient(AddressFamily.InterNetwork, ProtocolType.Tcp, clientOptions); + 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]; @@ -69,6 +70,10 @@ namespace NetSharpExamples.Examples Console.WriteLine($"[Client] Received response with contents \'{dataEncoding.GetString(receiveBuffer).TrimEnd('\0', ' ')}\' from {remoteEndPoint}"); } } + + rawSocket.Shutdown(SocketShutdown.Both); + rawSocket.Close(); + rawSocket.Dispose(); } } } \ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Examples/TcpSocketServerExample.cs b/NetSharp/NetSharpExamples/Examples/TcpSocketServerExample.cs @@ -18,18 +18,21 @@ namespace NetSharpExamples.Examples /// <inheritdoc /> public string Name { get; } = "TCP Socket Server Example"; - public static NetworkPacket ServerPacketHandler(in NetworkPacket request, in EndPoint remoteEndPoint) + 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.Data.Span).TrimEnd('\0', ' ')}\' from {remoteEndPoint}"); + 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 NetworkPacket.NullPacket - return request; + // to return false + + request.CopyTo(response); + + return true; } /// <inheritdoc /> @@ -38,8 +41,9 @@ namespace NetSharpExamples.Examples StreamSocketServerOptions serverOptions = new StreamSocketServerOptions(Environment.ProcessorCount, 2); + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); using StreamSocketServer server = - new StreamSocketServer(AddressFamily.InterNetwork, ProtocolType.Tcp, ServerPacketHandler, serverOptions); + new StreamSocketServer(ref rawSocket, ServerPacketHandler, serverOptions); server.Bind(in ServerEndPoint); diff --git a/NetSharp/NetSharpExamples/Examples/UdpSocketClientCancellationExample.cs b/NetSharp/NetSharpExamples/Examples/UdpSocketClientCancellationExample.cs @@ -1,101 +0,0 @@ -using NetSharp.Packets; -using NetSharp.Sockets.Datagram; -using NetSharp.Utils; - -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; - -namespace NetSharpExamples.Examples -{ - public class UdpSocketClientCancellationExample : INetSharpExample - { - private static readonly EndPoint ServerEndPoint = new IPEndPoint(IPAddress.Loopback, 12377); - - /// <inheritdoc /> - public string Name { get; } = "UDP Socket Client Cancellation Example"; - - /// <summary> - /// A read only server. Never sends out data! - /// </summary> - private Task ServerTask() - { - 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); - - for (int i = 0; i < 10; i++) - { - server.ReceiveFrom(transmissionBuffer, ref remoteEndPoint); - } - - for (int i = 0; i < 9; i++) - { - server.SendTo(transmissionBuffer, remoteEndPoint); - } - - server.Close(); - server.Dispose(); - - return Task.CompletedTask; - } - - /// <inheritdoc /> - public async Task RunAsync() - { - Task serverTask = Task.Factory.StartNew(ServerTask, TaskCreationOptions.LongRunning); - - DatagramSocketClientOptions clientOptions = new DatagramSocketClientOptions((ushort)2); - DatagramSocketClient client = new DatagramSocketClient(AddressFamily.InterNetwork, ProtocolType.Udp, clientOptions); - - byte[] sendBuffer = new byte[NetworkPacket.TotalSize]; - byte[] receiveBuffer = new byte[NetworkPacket.TotalSize]; - - EndPoint remoteEndPoint = ServerEndPoint; - - TimeSpan timeout = TimeSpan.FromMilliseconds(500); - - Console.WriteLine("Starting UDP Socket Client!"); - - for (int i = 0; i < 10; i++) - { - using CancellationTokenSource sendCts = new CancellationTokenSource(); - using CancellationTokenSource receiveCts = new CancellationTokenSource(); - - sendCts.CancelAfter(timeout); - TransmissionResult sendResult = await client.SendAsync(remoteEndPoint, sendBuffer, SocketFlags.None, sendCts.Token); - - if (sendResult.TimedOut()) - { - Console.WriteLine("Send timed out!"); - } - else - { - Console.WriteLine($"Sent {sendResult.Count} bytes of data!"); - - receiveCts.CancelAfter(timeout); - TransmissionResult receiveResult = await client.ReceiveAsync(remoteEndPoint, receiveBuffer, SocketFlags.None, receiveCts.Token); - - if (receiveResult.TimedOut()) - { - Console.WriteLine("Receive timed out!"); - } - else - { - Console.WriteLine($"Received {receiveResult.Count} bytes of data!"); - } - } - } - - client.Dispose(); - - Console.WriteLine($"UDP Socket Client finished!"); - } - } -} -\ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Examples/UdpSocketClientExample.cs b/NetSharp/NetSharpExamples/Examples/UdpSocketClientExample.cs @@ -20,7 +20,8 @@ namespace NetSharpExamples.Examples { DatagramSocketClientOptions clientOptions = new DatagramSocketClientOptions(2); - using DatagramSocketClient client = new DatagramSocketClient(AddressFamily.InterNetwork, ProtocolType.Udp, clientOptions); + 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]; @@ -39,7 +40,7 @@ namespace NetSharpExamples.Examples /* 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, CancellationToken.None); + await client.SendAsync(in remoteEndPoint, sendBuffer, SocketFlags.None); */ // lock is not necessary, but means that console output is clean and not interleaved @@ -53,7 +54,7 @@ namespace NetSharpExamples.Examples /* 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, CancellationToken.None); + await client.ReceiveAsync(in remoteEndPoint, receiveBuffer, SocketFlags.None); */ // lock is not necessary, but means that console output is clean and not interleaved @@ -62,6 +63,9 @@ namespace NetSharpExamples.Examples Console.WriteLine($"[Client] Received response with contents \'{dataEncoding.GetString(receiveBuffer).TrimEnd('\0', ' ')}\' from {remoteEndPoint}"); } } + + rawSocket.Close(); + rawSocket.Dispose(); } } } \ No newline at end of file diff --git a/NetSharp/NetSharpExamples/Examples/UdpSocketServerExample.cs b/NetSharp/NetSharpExamples/Examples/UdpSocketServerExample.cs @@ -18,18 +18,21 @@ namespace NetSharpExamples.Examples /// <inheritdoc /> public string Name { get; } = "UDP Socket Server Example"; - public static NetworkPacket ServerPacketHandler(in NetworkPacket request, in EndPoint remoteEndPoint) + 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.Data.Span).TrimEnd('\0', ' ')}\' from {remoteEndPoint}"); + 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 NetworkPacket.NullPacket - return request; + // to return false + + request.CopyTo(response); + + return true; } /// <inheritdoc /> @@ -38,8 +41,9 @@ namespace NetSharpExamples.Examples DatagramSocketServerOptions serverOptions = new DatagramSocketServerOptions(Environment.ProcessorCount, 2); + Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); using DatagramSocketServer server = - new DatagramSocketServer(AddressFamily.InterNetwork, ProtocolType.Udp, ServerPacketHandler, serverOptions); + new DatagramSocketServer(ref rawSocket, ServerPacketHandler, serverOptions); server.Bind(in ServerEndPoint); diff --git a/NetSharp/NetSharpExamples/NetSharpExamples.xml b/NetSharp/NetSharpExamples/NetSharpExamples.xml @@ -4,6 +4,12 @@ <name>NetSharpExamples</name> </assembly> <members> + <member name="P:NetSharpExamples.Benchmarks.DatagramNetworkReaderBenchmark.Name"> + <inheritdoc /> + </member> + <member name="M:NetSharpExamples.Benchmarks.DatagramNetworkReaderBenchmark.RunAsync"> + <inheritdoc /> + </member> <member name="F:NetSharpExamples.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. @@ -82,17 +88,6 @@ <member name="M:NetSharpExamples.Examples.TcpSocketServerExample.RunAsync"> <inheritdoc /> </member> - <member name="P:NetSharpExamples.Examples.UdpSocketClientCancellationExample.Name"> - <inheritdoc /> - </member> - <member name="M:NetSharpExamples.Examples.UdpSocketClientCancellationExample.ServerTask"> - <summary> - A read only server. Never sends out data! - </summary> - </member> - <member name="M:NetSharpExamples.Examples.UdpSocketClientCancellationExample.RunAsync"> - <inheritdoc /> - </member> <member name="P:NetSharpExamples.Examples.UdpSocketClientExample.Name"> <inheritdoc /> </member> diff --git a/NetSharp/NetSharpExamples/Program.cs b/NetSharp/NetSharpExamples/Program.cs @@ -16,7 +16,6 @@ namespace NetSharpExamples new UdpSocketClientSyncBenchmark(), new UdpSocketClientAsyncBenchmark(), new UdpSocketClientExample(), - new UdpSocketClientCancellationExample(), // TODO make functional // TCP socket server and client examples new TcpSocketServerBenchmark(), @@ -24,6 +23,9 @@ namespace NetSharpExamples new TcpSocketClientSyncBenchmark(), new TcpSocketClientAsyncBenchmark(), new TcpSocketClientExample(), + + // Restructured UDP server and client benchmarks + new DatagramNetworkReaderBenchmark(), }; private static async Task Main()